diff --git a/.circleci/config.yml b/.circleci/config.yml index 07d512a218fff..a95958fa90712 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,11 +6,13 @@ orbs: executors: linux-node: docker: - - image: cimg/node:18.20.3 + - image: cimg/node:20.15.1 linux-python: docker: - image: cimg/python:3.10.7 - bionic: + environment: + EMCC_CORES: "4" + focal: docker: - image: emscripten/emscripten-ci:focal environment: @@ -24,7 +26,7 @@ executors: environment: EMSDK_NOTTY: "1" macos: - xcode: "14.2.0" + xcode: "16.2.0" resource_class: macos.m1.medium.gen1 commands: @@ -36,11 +38,12 @@ commands: command: | # TODO: Make these part of the base image apt-get install -q -y libu2f-udev libvulkan1 xdg-utils - # wget -O ~/chrome.deb https://dl.google.com/linux/direct/google-chrome-beta_current_amd64.deb + wget -O ~/chrome.deb https://dl.google.com/linux/direct/google-chrome-unstable_current_amd64.deb # If that download link breaks, temporarily use this URL instead: # wget -O ~/chrome.deb https://storage.googleapis.com/webassembly/chrome/google-chrome-stable_current_amd64.deb - wget -O ~/chrome.deb https://dl.google.com/linux/direct/google-chrome-unstable_current_amd64.deb dpkg -i ~/chrome.deb + echo "Chrome version:" + /usr/bin/google-chrome --version emsdk-env: description: "emsdk_env.sh" steps: @@ -55,10 +58,6 @@ commands: description: "bootstrap" steps: - run: ./bootstrap - npm-install: - description: "npm ci" - steps: - - run: npm ci pip-install: description: "pip install" parameters: @@ -70,6 +69,15 @@ commands: - run: name: pip install command: << parameters.python >> -m pip install -r requirements-dev.txt + install-rust: + steps: + - run: + name: install rust + command: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + export PATH=${HOME}/.cargo/bin:${PATH} + rustup target add wasm32-unknown-emscripten + echo "export PATH=\"\$HOME/.cargo/bin:\$PATH\"" >> $BASH_ENV install-node-version: description: "install a specific version of node" parameters: @@ -106,7 +114,7 @@ commands: description: "install canary version of node" steps: - install-node-version: - node_version: "22.0.0-v8-canary20240423a2eb2005bd" + node_version: "25.0.0-v8-canary2025053097d65d1dc1" canary: true install-v8: description: "install v8 using jsvu" @@ -134,6 +142,10 @@ commands: cd ~/emsdk ./emsdk install tot ./emsdk activate tot + # Write the version of clang into a file for use in the ccache key + ./upstream/bin/clang --version > clang_version.txt + echo "clang version:" + cat ~/emsdk/clang_version.txt # Remove the emsdk version of emscripten to save space in the # persistent workspace and to avoid any confusion with the version # we are trying to test. @@ -155,7 +167,6 @@ commands: cat ~/emsdk/.emscripten - emsdk-env - bootstrap - - npm-install build-libs: description: "Build all libraries" steps: @@ -164,7 +175,13 @@ commands: command: | ./emcc --clear-cache - pip-install - - run: apt-get install -q -y ninja-build + - run: apt-get install -q -y ninja-build ccache + - run: + name: Ccache stats and configuration + command: | + ccache -s + ccache --print-stats + ccache -p - run: name: embuilder build ALL command: | @@ -189,6 +206,17 @@ commands: command: | ./embuilder build MINIMAL --pic --lto ./test/runner test_hello_world + - run: + name: "Ccache stats and configuration" + command: | + ls ~/cache/sysroot/lib/wasm32-emscripten || echo "no build" + cat ~/cache/sysroot/lib/wasm32-emscripten/crtbegin.o.ccache-log + date + ccache -s + ccache --print-stats + ccache -p + ccache --zero-stats + persist: description: "Persist the emsdk, libraries, and engines" steps: @@ -202,6 +230,19 @@ commands: - vms/ - wasi-sdk/ - .jsvu/ + - when: + # Only the main branch will store its cache results, and only the non-main branches + # will use cached results. This prevents untrusted PRs from polluting caches used + # by other branches. + condition: + equal: [ "main", << pipeline.git.branch >> ] + steps: + - save_cache: + name: "Save Ccache cache" + paths: + - ~/.ccache + key: clang-{{ checksum "~/emsdk/clang_version.txt" }} + prepare-for-tests: description: "Setup emscripten tests" steps: @@ -214,8 +255,20 @@ commands: command: git submodule update --init - emsdk-env - bootstrap - - npm-install - pip-install + - when: + # We only set EMTEST_RETRY_FLAKY on pull requests. When we run + # normal CI jobs on branches like main we still want to be able to + # detect flakyness. + condition: ${CIRCLE_PULL_REQUEST} + steps: + - set-retry-flaky-tests + remove-linux-binaries: + description: "Remove linux binaries from workspace" + steps: + - run: + name: "Remove Linux binaries" + command: rm -rf ~/emsdk ~/vms ~/.jsvu ~/wasi-sdk upload-test-results: description: "Upload test results" steps: @@ -231,16 +284,15 @@ commands: description: "Name of given test suite" type: string default: "" + extra-cflags: + description: "Extra EMCC_CFLAGS" + type: string + default: "" steps: - - when: - # We only set EMTEST_RETRY_FLAKY on pull requests. When we run - # normal CI jobs on branches like main we still want to be able to - # detect flakyness. - condition: ${CIRCLE_PULL_REQUEST} - steps: - set-retry-flaky-tests - run: name: run tests (<< parameters.title >>) + environment: + EMCC_CFLAGS: << parameters.extra-cflags >> command: | env ./test/runner << parameters.test_targets >> @@ -297,16 +349,11 @@ commands: - run: name: run tests (<< parameters.title >>) environment: - EMTEST_LACKS_SOUND_HARDWARE: "1" EMTEST_DETECT_TEMPFILE_LEAKS: "0" - # --no-sandbox because we are running as root and chrome requires - # this flag for now: https://crbug.com/638180 - CHROME_FLAGS_BASE: "--no-first-run -start-maximized --no-sandbox --use-gl=swiftshader --user-data-dir=/tmp/chrome-emscripten-profile --enable-experimental-web-platform-features" - CHROME_FLAGS_HEADLESS: "--headless=new --remote-debugging-port=1234" - CHROME_FLAGS_WASM: "--enable-experimental-webassembly-features --js-flags=\"--experimental-wasm-memory64 --experimental-wasm-stack-switching --experimental-wasm-type-reflection\"" - CHROME_FLAGS_NOCACHE: "--disk-cache-dir=/dev/null --disk-cache-size=1 --media-cache-size=1 --disable-application-cache --incognito" + EMTEST_HEADLESS: "1" + EMTEST_BROWSER: "/usr/bin/google-chrome" + EMTEST_CORES: "2" command: | - export EMTEST_BROWSER="/usr/bin/google-chrome $CHROME_FLAGS_BASE $CHROME_FLAGS_HEADLESS $CHROME_FLAGS_WASM $CHROME_FLAGS_NOCACHE" # There are tests in the browser test suite that using libraries # that are not included by "./embuilder build ALL". For example the # PIC version of libSDL which is used by test_sdl2_misc_main_module @@ -319,10 +366,13 @@ commands: name: Install brew package dependencies environment: HOMEBREW_NO_AUTO_UPDATE: "1" - command: | - brew update - brew install cmake ninja python3 pkg-config + # Use --force-bottle to force homebrew use binary packages avoiding + # costly build times. + command: brew install --force-bottle cmake ninja pkg-config + - attach_workspace: + at: ~/ - checkout + - remove-linux-binaries - run: name: submodule update command: git submodule update --init @@ -337,22 +387,20 @@ commands: type: string default: "" steps: - - prepare-for-tests - run: name: download firefox command: | wget -O ~/ff.tar.bz2 "https://download.mozilla.org/?product=firefox-nightly-latest-ssl&os=linux64&lang=en-US" tar -C ~ -xf ~/ff.tar.bz2 - run: - name: configure firefox + name: Add audio dependencies command: | - mkdir ~/tmp-firefox-profile/ - cat > ~/tmp-firefox-profile/user.js \<>) environment: @@ -360,26 +408,99 @@ commands: # TODO: Do GL testing when https://bugzil.la/1375585 (lack of WebGL # support in headless mode) resolves EMTEST_LACKS_GRAPHICS_HARDWARE: "1" - EMTEST_LACKS_WEBGPU: "1" + # TODO(https://github.com/emscripten-core/emscripten/issues/24205) EMTEST_LACKS_SOUND_HARDWARE: "1" - # OffscreenCanvas support is not yet done in Firefox. - EMTEST_LACKS_OFFSCREEN_CANVAS: "1" + EMTEST_LACKS_WEBGPU: "1" EMTEST_DETECT_TEMPFILE_LEAKS: "0" + EMTEST_HEADLESS: "1" + EMTEST_CORES: "2" DISPLAY: ":0" command: | - export EMTEST_BROWSER="$HOME/firefox/firefox -headless -profile $HOME/tmp-firefox-profile/" + export EMTEST_BROWSER="$HOME/firefox/firefox" # There are tests in the browser test suite that using libraries # that are not included by "./embuilder build ALL". For example the # PIC version of libSDL which is used by test_sdl2_misc_main_module export EM_FROZEN_CACHE="" echo "-----" - echo "Running browser tests" + echo "Running browser tests (EMTEST_BROWSER=$EMTEST_BROWSER)" echo "-----" test/runner << parameters.test_targets >> # posix and emrun suites are disabled because firefox errors on # "Firefox is already running, but is not responding." # TODO: find out a way to shut down and restart firefox - upload-test-results + run-tests-firefox-windows: + description: "Runs emscripten tests under firefox on Windows" + parameters: + test_targets: + description: "Test suites to run" + type: string + title: + description: "Name of given test suite" + type: string + default: "" + steps: + - run: + name: download firefox + shell: powershell.exe -ExecutionPolicy Bypass + command: | + # To download Firefox, we must first figure out what the latest Firefox version name is. + # This is because there does not exist a stable/static URL to download latest Firefox from. + $html = Invoke-WebRequest "https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/" + $zipLink = $html.Links | + Where-Object { $_.href -match "firefox-.*\.en-US\.win64\.zip$" } | + Select-Object -Last 1 -ExpandProperty href + + # Download Win64 Firefox. + $nightlyUrl = "https://archive.mozilla.org$zipLink" + $outZip = "$env:TEMP\firefox_nightly.zip" + + Write-Host "Downloading latest Firefox Nightly: $nightlyUrl" + Invoke-WebRequest -Uri $nightlyUrl -OutFile $outZip + + # Extract to user home directory + Write-Host "Extracing $outZip" + $installDir = Join-Path $env:USERPROFILE "firefox" + if (Test-Path $installDir) { Remove-Item -Recurse -Force $installDir } + # All files in the archive are in the firefox/ directory so extract + # directly to $USERPROFILE. + Expand-Archive -LiteralPath $outZip -DestinationPath $env:USERPROFILE + + # FIXME(sbc): This does not seem to work, and EMTEST_BROWSER is + # not set in the bash job below. + # Set environment variable that will be visibvle in future steps + #$ffExe = Join-Path $installDir "firefox.exe" + #Write-Host "Setting EMTEST_BROWSER to '$ffExe' in $env:BASH_ENV" + #Add-Content -Path $env:BASH_ENV -Value "export EMTEST_BROWSER='$ffExe'" + - run: + name: run tests (<< parameters.title >>) + environment: + EMTEST_LACKS_GRAPHICS_HARDWARE: "1" + # TODO(https://github.com/emscripten-core/emscripten/issues/24205) + EMTEST_LACKS_SOUND_HARDWARE: "1" + EMTEST_LACKS_WEBGPU: "1" + EMTEST_DETECT_TEMPFILE_LEAKS: "0" + EMTEST_HEADLESS: "1" + EMTEST_CORES: "2" + DISPLAY: ":0" + command: | + # For some reason its not possible for the powershell job above to + # add to BASH_ENV, so we need set this here + export EMTEST_BROWSER="$USERPROFILE\\firefox\\firefox.exe" + + # There are tests in the browser test suite that using libraries + # that are not included by "./embuilder build ALL". For example the + # PIC version of libSDL which is used by test_sdl2_misc_main_module + export EM_FROZEN_CACHE= + + echo "BASH_ENV($BASH_ENV):" + echo "-----" + cat $BASH_ENV + echo "-----" + echo "Running browser tests (EMTEST_BROWSER=$EMTEST_BROWSER)" + echo "-----" + test/runner << parameters.test_targets >> + - upload-test-results test-sockets-chrome: description: "Runs emscripten sockets tests under chrome" steps: @@ -390,20 +511,16 @@ commands: environment: EMTEST_LACKS_SOUND_HARDWARE: "1" EMTEST_DETECT_TEMPFILE_LEAKS: "0" - # --no-sandbox becasue we are running as root and chrome requires - # this flag for now: https://crbug.com/638180 - CHROME_FLAGS_BASE: "--no-first-run -start-maximized --no-sandbox --use-gl=swiftshader --user-data-dir=/tmp/chrome-emscripten-profile" - CHROME_FLAGS_HEADLESS: "--headless=new --remote-debugging-port=1234" - CHROME_FLAGS_WASM: "--enable-experimental-webassembly-features --js-flags=\"--experimental-wasm-memory64\"" - CHROME_FLAGS_NOCACHE: "--disk-cache-dir=/dev/null --disk-cache-size=1 --media-cache-size=1 --disable-application-cache --incognito" + EMTEST_HEADLESS: "1" + EMTEST_BROWSER: "/usr/bin/google-chrome" + EMTEST_CORES: "2" command: | - export EMTEST_BROWSER="/usr/bin/google-chrome $CHROME_FLAGS_BASE $CHROME_FLAGS_HEADLESS $CHROME_FLAGS_WASM $CHROME_FLAGS_NOCACHE" test/runner sockets - upload-test-results jobs: build-docs: - executor: bionic + executor: focal steps: - checkout - pip-install @@ -411,14 +528,23 @@ jobs: - run: make -C site text - run: tools/maint/check_emcc_help_text.py - run: make -C site html - flake8: - executor: bionic + ruff: + # Keep in sync with ruff commands in tools/maint/pre-push + executor: focal + steps: + - checkout + - pip-install + - run: ruff check + # TODO (cclauss): When ruff supports rules these errors without --preview, remove following line + - run: ruff check --preview --select=E20,E30,E221,E225,E226 + vulture: + executor: focal steps: - checkout - pip-install - - run: python3 -m flake8 --show-source --statistics + - run: vulture . --min-confidence 100 mypy: - executor: bionic + executor: focal steps: - checkout - pip-install @@ -427,18 +553,18 @@ jobs: executor: linux-node steps: - checkout - - npm-install + - run: npm ci - run: | npm run lint npm run check test-sanity: - executor: bionic + executor: focal steps: - run-tests-linux: frozen_cache: false test_targets: "sanity" build-linux: - executor: bionic + executor: focal # xlarge has 4x the cores of the default medium, costs 4x as much, and runs # in about 1/2 the time, so it is not cost-effective (overall it is 2x the # cost for the same work), but given this blocks almost all the other jobs @@ -447,6 +573,9 @@ jobs: environment: EMCC_CORES: 16 EMCC_USE_NINJA: 1 + EM_COMPILER_WRAPPER: "ccache" + CCACHE_DEBUG: 1 + CCACHE_DEBUGLEVEL: 1 steps: - checkout - run: @@ -474,23 +603,34 @@ jobs: tar xvf wasi-sysroot-11.0.tar.gz -C ~/wasi-sdk/ - install-v8 - install-emsdk + - when: + condition: + not: + equal: [ "main", << pipeline.git.branch >> ] + steps: + - restore_cache: + name: "Restore Ccache cache" + key: clang-{{ checksum "~/emsdk/clang_version.txt" }} - build-libs + - run: + name: Clean build directory + command: rm -rf ~/cache/build - persist # Perhaps we don't need to run this suite with every commit. Consider moving this to FYI bot. test-posixtest: - executor: bionic + executor: focal steps: - run-tests-linux: test_targets: "posixtest" test-core0: - executor: bionic + executor: focal environment: EMTEST_SKIP_NODE_CANARY: "1" steps: - run-tests-linux: test_targets: "core0" test-core2: - executor: bionic + executor: focal environment: EMTEST_BROWSER: "node" EMTEST_SKIP_NODE_CANARY: "1" @@ -502,6 +642,7 @@ jobs: title: "asan+lsan" test_targets: " asan.test_stat + asan.test_stack asan.test_float_builtins asan.test_embind* asan.test_abort_on_exceptions @@ -512,14 +653,20 @@ jobs: asan.test_dlfcn_basic asan.test_async_hello_jspi asan.test_cubescript - asan.test_wasm_worker_hello asan.test_externref_emjs_dynlink asan.test_asyncify_longjmp asan.test_pthread_run_on_main_thread + asan.test_minimal_runtime_global_initializer + asan.test_fs_js_api_wasmfs + asan.test_modularize_instance_pthreads + asan.test_minimal_runtime_hello_world + lsan.test_dylink_dso_needed lsan.test_stdio_locking lsan.test_dlfcn_basic lsan.test_pthread_create - lsan.test_pthread_exit_main_stub" + lsan.test_pthread_exit_main_stub + lsan.test_dylink_iostream + ubsan.test_externref_emjs_dynlink" - freeze-cache - run-tests: # also run a single test of EMTEST_BROWSER=node. @@ -531,7 +678,7 @@ jobs: corez.test_dylink_syslibs_all" - upload-test-results test-core3: - executor: bionic + executor: focal environment: EMTEST_SKIP_NODE_CANARY: "1" steps: @@ -543,7 +690,7 @@ jobs: test_targets: " lto2.test_dylink_syslibs_all lto2.test_float_builtins - lto2.test_emscripten_lazy_load_code_unconditional + lto2.test_avx_nontrapping lto0.test_exceptions_allowed_uncaught lto0.test_longjmp_standalone_standalone lto0.test_embind_i64_val @@ -554,7 +701,9 @@ jobs: core2ss.test_pthread_thread_local_storage core2s.test_dylink_syslibs_missing_assertions core2s.test_module_wasm_memory + cores.test_minimal_runtime_safe_heap wasm2js3.test_memorygrowth_2 + wasm2js2.test_pthread_proxying wasmfs.test_hello_world wasmfs.test_unistd_links* wasmfs.test_atexit_standalone @@ -564,6 +713,7 @@ jobs: wasmfs.test_readdir_rawfs wasmfs.test_utime wasmfs.test_unistd_unlink + wasmfs.test_unistd_dup wasmfs.test_unistd_access wasmfs.test_unistd_close wasmfs.test_unistd_truncate @@ -577,8 +727,6 @@ jobs: wasmfs.test_fs_write wasmfs.test_fs_writev wasmfs.test_fs_writev_rawfs - wasmfs.test_fs_writeFile - wasmfs.test_fs_writeFile_rawfs wasmfs.test_fs_readv wasmfs.test_fs_write wasmfs.test_fs_readv_rawfs @@ -604,19 +752,33 @@ jobs: wasmfs.test_futimens wasmfs.test_unistd_links wasmfs.test_fcntl_open - wasmfs.test_fs_js_api wasmfs.test_fs_llseek wasmfs.test_fs_llseek_rawfs - wasmfs.test_freetype" - test-wasm2js1: + wasmfs.test_freetype + minimal0.test_utf + minimal0.test_ubsan_full_stack_trace_gsource_map + minimal0.test_static_variable + minimal0.test_stack_overflow + omitexports0.test_asyncify_longjmp + omitexports0.test_emscripten_api + strict.test_no_declare_asm_module_exports + " + test-modularize-instance: + executor: focal environment: EMTEST_SKIP_NODE_CANARY: "1" - executor: bionic steps: - run-tests-linux: - test_targets: "wasm2js1" - test-wasm64: - # We don't use `bionic` here since its tool old to run recent node versions: + test_targets: "instance" + test-stress: + executor: focal + environment: + EMTEST_SKIP_NODE_CANARY: "1" + steps: + - run-tests-linux: + test_targets: "stress" + test-esm-integration: + # We don't use `bionic` here since its too old to run recent node versions: # `/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found` executor: linux-python steps: @@ -627,21 +789,19 @@ jobs: - install-v8 - install-node-canary - run-tests: - title: "wasm64" - test_targets: " - wasm64 - core_2gb.test_*em_asm* - core_2gb.test_*embind* - wasm64l.test_hello_world - wasm64l.test_bigswitch - other.test_memory64_proxies - other.test_failing_growth_wasm64" + title: "esm_integration" + test_targets: "esm_integration" - upload-test-results + test-wasm2js1: + environment: + EMTEST_SKIP_NODE_CANARY: "1" + executor: focal + steps: + - run-tests-linux: + test_targets: "wasm2js1" test-wasm64-4gb: environment: LANG: "C.UTF-8" - # Only run 2 tests at a time to avoid OOM (since each tests used >4gb) - EMCC_CORES: "2" # We don't use `bionic` here since its too old to run recent node versions: # `/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found` executor: linux-python @@ -652,9 +812,25 @@ jobs: - run: rm -rf $HOME/.jsvu - install-v8 - install-node-canary + # When running wasm64 tests we need to make sure we use the testing + # version of node (node canary) when running the compiler output (e.g. + # in configure tests. + - run: + name: configure node canary + command: echo "NODE_JS = NODE_JS_TEST" >> ~/emsdk/.emscripten - run-tests: title: "wasm64_4gb" - test_targets: "wasm64_4gb" + test_targets: "wasm64_4gb + core_2gb.test_*em_asm* + core_2gb.test_*embind* + core_2gb.test_fs_js_api_wasmfs + wasm64.test_safe_stack + wasm64l.test_hello_world + wasm64l.test_bigswitch + wasm64l.test_module_wasm_memory + wasm64l.test_longjmp2_emscripten + wasm64l.test_embind_val_basics_legacy + other.*_wasm64" - upload-test-results test-jsc: executor: linux-python @@ -720,6 +896,18 @@ jobs: command: git submodule update --init - pip-install - install-emsdk + # `install-node-version` only changes the NODE_JS_TEST (the version of + # node used to run test), not NODE_JS (the version of node used to run the + # compiler itself). + # In order to test that the compiler itself can run under the oldest + # supported version of node, we run all the tests in the runner under that + # version. + # Keep this in sync with MINIMUM_NODE_VERSION in tools/shared.py. + - install-node-version: + node_version: "18.3.0" + - run: + name: configure compiler to use 18.3.0 + command: echo "NODE_JS = '$(which node)'" >> ~/emsdk/.emscripten - install-node-canary - run-tests: title: "node (canary)" @@ -728,25 +916,83 @@ jobs: other.test_gen_struct_info other.test_native_call_before_init other.test_node_unhandled_rejection + other.test_*growable_arraybuffers + other.test_add_js_function_bigint_memory64 core2.test_hello_world + core2.test_esm_integration* + core0.test_esm_integration* core0.test_pthread_join_and_asyncify - core0.test_async_ccall_promise_jspi - core0.test_async_ccall_promise_exit_runtime_jspi - core0.test_cubescript_jspi" + core0.test_async_ccall_promise_jspi* + core0.test_cubescript_jspi + " # Run some basic tests with the minimum version of node that we currently - # support. + # support in the generated code. + # Keep this in sync with `OLDEST_SUPPORTED_NODE` in `feature_matrix.py` - install-node-version: - node_version: "10.19.0" + node_version: "12.22.9" - run-tests: - title: "node (oldest / 10.19.0)" + title: "node (oldest / 12.22.9)" + extra-cflags: "-sMIN_NODE_VERSION=122209" + # We include most but not all of the nodefs and node rawfs tests here. + # test_fs_nodefs_rw, test_fs_nodefs_statvfs, and test_unistd_io_nodefs_bigint fail. test_targets: " other.test_gen_struct_info other.test_native_call_before_init other.test_js_optimizer_verbose other.test_node_unhandled_rejection + other.test_file_packager_separate_metadata other.test_full_js_library* core2.test_hello_world - core2.test_fs_write_rawfs" + core2.test_fcntl_open_nodefs + core2.test_fcntl_open_rawfs + core2.test_fgetc_ungetc_nodefs + core2.test_fgetc_ungetc_rawfs + core2.test_fs_append_rawfs + core2.test_fs_emptyPath_rawfs + core2.test_fs_enotdir_nodefs + core2.test_fs_enotdir_rawfs + core2.test_fs_errorstack_rawfs + core2.test_fs_llseek_rawfs + core2.test_fs_mkdir_dotdot_nodefs + core2.test_fs_mkdir_dotdot_rawfs + core2.test_fs_mmap_nodefs + core2.test_fs_mmap_rawfs + core2.test_fs_nodefs_cloexec + core2.test_fs_nodefs_cloexec_rawfs + core2.test_fs_nodefs_dup + core2.test_fs_nodefs_dup_rawfs + core2.test_fs_nodefs_home + core2.test_fs_nodefs_nofollow + core2.test_fs_nodefs_readdir + core2.test_fs_noderawfs_nofollow + core2.test_fs_readdir_ino_matches_stat_ino_nodefs + core2.test_fs_readdir_ino_matches_stat_ino_rawfs + core2.test_fs_readv_rawfs + core2.test_fs_rename_on_existing_nodefs + core2.test_fs_rename_on_existing_rawfs + core2.test_fs_symlink_resolution_nodefs + core2.test_fs_symlink_resolution_rawfs + core2.test_fs_writeFile* + core2.test_fs_write_rawfs + core2.test_fs_writev_rawfs + core2.test_futimens_rawfs + core2.test_readdir_rawfs + core2.test_stat_chmod_rawfs + core2.test_unistd_access_nodefs + core2.test_unistd_access_rawfs + core2.test_unistd_close_rawfs + core2.test_unistd_dup_rawfs + core2.test_unistd_io_nodefs + core2.test_unistd_links_nodefs + core2.test_unistd_misc_nodefs + core2.test_unistd_pipe_rawfs + core2.test_unistd_symlink_on_nodefs + core2.test_unistd_truncate_nodefs + core2.test_unistd_truncate_rawfs + core2.test_unistd_unlink_nodefs + core2.test_unistd_unlink_rawfs + core2.test_unistd_write_broken_link_rawfs + " # Run a few test with the most recent version of node # In particular we have some tests that require node flags on older # versions of node but not with the most recent version. @@ -766,40 +1012,79 @@ jobs: core2.test_i64_invoke_bigint core2.test_sse2 core2.test_source_map - core2.test_exceptions_wasm + core2.test_exceptions_wasm_legacy core2.test_pthread_unhandledrejection" - upload-test-results test-other: - executor: bionic + executor: focal environment: EMTEST_SKIP_NODE_CANARY: "1" + EMTEST_SKIP_RUST: "1" + EMTEST_SKIP_WASM64: "1" + EMTEST_SKIP_NEW_CMAKE: "1" steps: - - run: apt-get install -q -y ninja-build scons + - install-rust + - run: apt-get install -q -y ninja-build scons ccache - run-tests-linux: # some native-dependent tests fail because of the lack of native # headers on emsdk-bundled clang test_targets: "other skip:other.test_native_link_error_message" test-browser-chrome: - executor: bionic + executor: focal environment: EMTEST_LACKS_WEBGPU: "1" steps: + - checkout + - run: + name: submodule update + command: git submodule update --init + - pip-install + - install-emsdk - run-tests-chrome: title: "browser" - # Skip test_4gb_fail as it OOMs on the current bot + # Just tests that don't run in browser 2G mode test_targets: " - browser skip:browser.test_4gb_fail + browser.test_gl_stride + browser.test_memory_growth_during_startup + browser.test_pthread_large_pthread_allocation + browser.test_pthread_sbrk* + browser.test_pthread_asan* + browser.test_webgl_multi_draw* + browser.test_pthread_growth* + browser.test_4gb + browser.test_emmalloc_3gb* + browser.test_dlmalloc_3gb + browser.test_2gb_fail + browser.test_audio_worklet* " test-browser-chrome-wasm64: - executor: bionic + executor: focal environment: EMTEST_LACKS_WEBGPU: "1" steps: - run-tests-chrome: title: "browser64" - test_targets: "browser64" + # Just tests that don't run in browser64 4G mode + # TODO: restore this coverage (and the corresponding wasm64) and/or + # make this exclusion list a bit more automatic. + test_targets: " + browser64.test_emscripten_animate_canvas_element_size* + browser64.test_*malloc_*gb* + browser64.test_emmalloc_memgrowth + browser64.test_subdata_es2* + browser64.test_webgl2_garbage_free_entrypoints* + browser64.test_gl_stride + browser64.test_webgl2_get_buffer_sub_data + browser64.test_webgl2_pbo + browser64.test_webgl2_sokol* + browser64.test_memory_growth_during_startup + browser64.test_pthread_large_pthread_allocation + browser64.test_pthread_sbrk* + browser64.test_pthread_asan* + browser64.test_webgl_multi_draw* + browser64.test_pthread_growth*" test-browser-chrome-2gb: - executor: bionic + executor: focal environment: EMTEST_LACKS_WEBGPU: "1" steps: @@ -807,17 +1092,19 @@ jobs: title: "browser_2gb" test_targets: "browser_2gb" test-browser-chrome-wasm64-4gb: - executor: bionic + executor: focal environment: EMTEST_LACKS_WEBGPU: "1" - EMTEST_SKIP_NODE_CANARY: "1" steps: - run-tests-chrome: title: "browser64_4gb" test_targets: "browser64_4gb" test-browser-firefox: - executor: bionic + executor: focal + environment: + EMTEST_LACKS_GROWABLE_ARRAYBUFFERS: "1" steps: + - prepare-for-tests - run-tests-firefox: title: "browser" # browser.test_sdl2_mouse and/or SDL2 should be fixed. The case happens @@ -825,26 +1112,84 @@ jobs: # initial position of the mouse pointer relative to the canvas. # browser.test_html5_webgl_create_context is skipped because # anti-aliasing is not well supported. - # browser.test_webgl_offscreen_canvas_in_pthread and - # browser.test_webgl_offscreen_canvas_in_mainthread_after_pthread - # are crashing Firefox (bugzil.la/1281796). The former case is - # further blocked by issue #6897. - test_targets: "browser skip:browser.test_sdl2_mouse skip:browser.test_html5_webgl_create_context skip:browser.test_webgl_offscreen_canvas_in_pthread skip:browser.test_webgl_offscreen_canvas_in_mainthread_after_pthread skip:browser.test_glut_glutget" + test_targets: " + browser + skip:browser.test_sdl2_mouse + skip:browser.test_html5_webgl_create_context + skip:browser.test_glut_glutget + " + test-browser-firefox-wasm64: + executor: focal + steps: + - checkout + - run: + name: submodule update + command: git submodule update --init + - pip-install + - install-emsdk + - run-tests-firefox: + title: "browser64" + test_targets: " + browser64.test_sdl_image + browser64.test_dylink_many + " + test-windows-browser-firefox: + # Use spaces and other special chars in the path to emsdk to flush out + # issues with windows. In particular, the .bat file launchers can have + # trouble with these. + working_directory: "~/path with spaces ()/" + executor: + name: win/server-2019 + shell: bash.exe -eo pipefail + environment: + PYTHONUNBUFFERED: "1" + EMSDK_NOTTY: "1" + steps: + - checkout + - run: + name: Install packages + command: | + choco install -y cmake.portable ninja pkgconfiglite + - run: + name: Add python to bash path + command: echo "export PATH=\"$PATH:/c/Python27amd64/\"" >> $BASH_ENV + - install-emsdk + - pip-install: + python: "$EMSDK_PYTHON" + - run-tests-firefox-windows: + title: "browser on firefox on windows" + # skip browser.test_glbook, as it requires mingw32-make, which is not + # installed on CircleCI. + # skip browser.test_sdl2_mixer_wav_dash_l, fails to build on Windows + # on CircleCI (works locally) + # + # For now, just run a small subset of tests that are know to work. + # TODO: Include more tests here. + test_targets: " + browser.test_sdl_image + browser.test_dylink_many + skip:browser.test_glbook + skip:browser.test_sdl2_mixer_wav_dash_l + " # TODO(sbc): Re-enable once we figure out why the emrun tests are # locking up. #test-browser-chrome-emrun: - # executor: bionic + # executor: focal # steps: # - run-tests-chrome: # test_targets: "emrun" test-sockets-chrome: - executor: bionic + executor: focal steps: - test-sockets-chrome + # windows and mac do not have separate build and test jobs, as they only run # a limited set of tests; it is simpler and faster to do it all in one job. test-windows: - working_directory: "~/path with spaces" + # Use spaces and other special chars in the path to emsdk to flush out + # issues with windows. In particular, the .bat file launchers can have + # trouble with these. + working_directory: "~/path with spaces ()/" executor: name: win/server-2019 shell: bash.exe -eo pipefail @@ -855,10 +1200,11 @@ jobs: # https://github.com/emscripten-core/emscripten/pull/11382#pullrequestreview-428902638 EMTEST_LACKS_NATIVE_CLANG: "1" EMTEST_SKIP_V8: "1" - EMTEST_SKIP_EH: "1" + EMTEST_SKIP_WASM_LEGACY_EH: "1" + EMTEST_SKIP_WASM_EH: "1" EMTEST_SKIP_WASM64: "1" - EMTEST_SKIP_SIMD: "1" EMTEST_SKIP_SCONS: "1" + EMTEST_SKIP_RUST: "1" EMTEST_SKIP_NODE_CANARY: "1" EMTEST_BROWSER: "0" steps: @@ -891,15 +1237,18 @@ jobs: # We don't install d8 or modern node on the mac runner so we skip any # tests that depend on those. EMTEST_SKIP_V8: "1" - EMTEST_SKIP_EH: "1" + EMTEST_SKIP_WASM_LEGACY_EH: "1" + EMTEST_SKIP_WASM_EH: "1" EMTEST_SKIP_WASM64: "1" EMTEST_SKIP_SCONS: "1" + EMTEST_SKIP_RUST: "1" # Some native clang tests assume x86 clang (e.g. -sse2) EMTEST_LACKS_NATIVE_CLANG: "1" EMCC_SKIP_SANITY_CHECK: "1" steps: - setup-macos - install-emsdk + - freeze-cache # TODO: We can't currently do pip install here since numpy and other packages # are currently missing arm64 macos binaries. - run-tests: @@ -910,8 +1259,9 @@ jobs: workflows: build-test: jobs: - - flake8 + - ruff - mypy + - vulture - eslint - build-docs - build-linux @@ -930,9 +1280,6 @@ workflows: - test-core3: requires: - build-linux - - test-wasm64: - requires: - - build-linux - test-wasm64-4gb: requires: - build-linux @@ -942,9 +1289,16 @@ workflows: - test-other: requires: - build-linux - - test-browser-chrome: + - test-modularize-instance: + requires: + - build-linux + - test-esm-integration: + requires: + - build-linux + - test-stress: requires: - build-linux + - test-browser-chrome - test-browser-chrome-2gb: requires: - build-linux @@ -957,6 +1311,7 @@ workflows: - test-browser-firefox: requires: - build-linux + - test-browser-firefox-wasm64 - test-sockets-chrome: requires: - build-linux @@ -964,4 +1319,7 @@ workflows: - test-spidermonkey - test-node-compat - test-windows - - test-mac-arm64 + - test-windows-browser-firefox + - test-mac-arm64: + requires: + - build-linux diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index e8ca40a3d059c..0000000000000 --- a/.coveragerc +++ /dev/null @@ -1,7 +0,0 @@ -[run] -source = . -omit = ./test/* - ./third_party/* - ./tools/emcoverage.py - test.py - diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index 59361f6b88d27..0000000000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,63 +0,0 @@ -env: - browser: true - es2022: true - node: true -extends: - - prettier -parserOptions: - ecmaVersion: 13 - sourceType: "module" -ignorePatterns: - - "out/" - - "site/" - - "cache/" - - "third_party/" - - "test/" - - "src/polyfill/" - - "src/library*.js" - - "src/runtime_*.js" - - "src/shell*.js" - - "src/preamble*.js" - - "src/postamble*.js" - - "src/closure-externs/" - - "src/embind/" - - "src/emrun_postjs.js" - - "src/wasm_worker.js" - - "src/audio_worklet.js" - - "src/wasm2js.js" - - "src/webGLClient.js" - - "src/webGLWorker.js" - - "src/*_shell_read.js" - - "src/wasm_offset_converter.js" - - "src/threadprofiler.js" - - "src/cpuprofiler.js" - - "src/memoryprofiler.js" - - "src/gl-matrix.js" - - "src/headless.js" - - "src/headlessCanvas.js" - - "src/emscripten-source-map.min.js" - - "src/source_map_support.js" - - "src/Fetch.js" - - "src/settings.js" - - "src/settings_internal.js" - - "src/arrayUtils.js" - - "src/deterministic.js" - - "src/base64Utils.js" - - "src/base64Decode.js" - - "src/proxyWorker.js" - - "src/proxyClient.js" - - "src/IDBStore.js" - - "src/URIUtils.js" - - "tools/experimental" -rules: - #max-len: ["error", 100] - max-len: "off" - no-multi-spaces: "off" - require-jsdoc: "off" - arrow-body-style: ["error", "as-needed"] - space-infix-ops: "error" - quotes: ["error", "single", {"avoidEscape": true}] -overrides: - - files: "**/*.mjs" - rules: - no-unused-vars: ["error", {"vars": "all", "args": "none", "ignoreRestSiblings": false, "destructuredArrayIgnorePattern": "^_" }] diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 74d7233230b35..0000000000000 --- a/.flake8 +++ /dev/null @@ -1,18 +0,0 @@ -[flake8] -ignore = E111,E114,E501,E261,E266,E121,E402,E241,W504,E741,B011,B023,U101 -exclude = - ./node_modules/, # third-party code - ./third_party/, # third-party code - ./tools/filelock.py, # third-party code - ./tools/scons/, # third-party code - ./test/third_party/, # third-party code - ./site/source/conf.py, - ./system/lib/, # system libraries - ./cache/, # download/built content - .git -# The ports plugins have a lot of unused imports because -# they need to implement the specific plugin APIs -per-file-ignores = - ./tools/ports/*.py: U100 - ./test/*.py: U100 - ./tools/toolchain_profiler.py: U100 diff --git a/.gitattributes b/.gitattributes index bddcc6d2240d2..95c5a1e388bc9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,8 +3,7 @@ tools/crunch-worker.js -diff third_party/lzma.js/lzma-decoder.js -diff third_party/lzma.js/lzma-full.js -diff -test/other/test_emsize.js -diff -src/emscripten-source-map.min.js -diff +test/other/test_emsize.js -diff eol=lf test/* linguist-vendored third_party/* linguist-vendored system/ linguist-vendored diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 90e05c40d0459..e3ae1a9ac556c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,21 @@ version: 2 updates: - - package-ecosystem: "github-actions" # See documentation for possible values + - package-ecosystem: "npm" + directory: "/" # Look for `package.json` and `lock` files in the `root` directory + schedule: + interval: "monthly" + commit-message: + prefix: "[deps]" + groups: + production-dependencies: + dependency-type: "production" + development-dependencies: + dependency-type: "development" + + - package-ecosystem: "github-actions" directory: "/" # Location of package manifests schedule: - interval: "weekly" + interval: "monthly" + commit-message: + prefix: "[deps]" diff --git a/.github/workflows/archive.yml b/.github/workflows/archive.yml deleted file mode 100644 index 480bf446da5c9..0000000000000 --- a/.github/workflows/archive.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: CI - -on: - create: - tags: - push: - branches: - - main - pull_request: - -permissions: - contents: read - -jobs: - archive: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - - name: make dist - run: | - make dist - version=`cat emscripten-version.txt | sed s/\"//g` - echo "VERSION=$version" >> $GITHUB_ENV - - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 - with: - name: emscripten-${{ env.VERSION }} - path: emscripten-${{ env.VERSION }}.tar.bz2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000000..97a74aef3c60e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,95 @@ +name: CI + +on: + create: + tags: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +jobs: + archive: + name: Archive + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - name: make dist + run: | + make dist + version=`cat emscripten-version.txt | sed s/\"//g` + echo "VERSION=$version" >> $GITHUB_ENV + - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.5 + with: + name: emscripten-${{ env.VERSION }} + path: emscripten-${{ env.VERSION }}.tar.bz2 + + codesize-checks: + name: Codesize Checks + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 # We want access to other branches, specifically `main` + - name: pip install + run: | + which python3 + python3 --version + python3 -m pip install -r requirements-dev.txt + - name: Install emsdk + run: | + EM_CONFIG=$HOME/emsdk/.emscripten + echo "emscripten config file: $EM_CONFIG" + echo "EM_CONFIG=$EM_CONFIG" >> $GITHUB_ENV + curl -# -L -o ~/emsdk-main.tar.gz https://github.com/emscripten-core/emsdk/archive/main.tar.gz + tar -C ~ -xf ~/emsdk-main.tar.gz + mv ~/emsdk-main ~/emsdk + cd ~/emsdk + ./emsdk install tot + ./emsdk activate tot + echo "emscripten config file ($EM_CONFIG) contents:" + cat $EM_CONFIG + - name: Check test expectations on target branch + # Skip this step for rebaseline PRs, since the target branch is expected + # to be out of date in this case. + if: "!contains(github.event.pull_request.title, 'Automatic rebaseline of codesize expectations')" + run: | + echo "Checking out ${{ github.base_ref }}" + git checkout ${{ github.base_ref }} + git rev-parse HEAD + # Uncomment this like to pull the rebaseline_tests.py from the + # current branch: + # git checkout - ./tools/maint/rebaseline_tests.py + ./bootstrap + if ! ./tools/maint/rebaseline_tests.py --check-only; then + echo "" + echo "Test expectations are out-of-date on the target branch." + echo "Please run the 'Rebaseline Tests' github action on the target" + echo "branch (normally 'main') before proceeding." + echo "" + echo "You can also run './tools/maint/rebaseline_tests.py --new-branch'" + echo "and use it to create a seperate PR." + exit 1 + fi + - name: Check test expectations on PR branch + run: | + echo "Checking out ${{ github.ref }} (${{ github.sha }})" + # For some reason we cannot pass ${{ github.ref }} direclty to git + # since it doesn't recognise it. + git checkout ${{ github.sha }} + git rev-parse HEAD + ./bootstrap + if ! ./tools/maint/rebaseline_tests.py --check-only --clear-cache; then + echo "Test expectations are out-of-date on the PR branch." + echo "You can run './tools/maint/rebaseline_tests.py' to" + echo "create a commit updating the expectations." + echo "Be sure to have 'emsdk install tot' first." + echo "-- This failure is only a warning and can be ignored" + exit 1 + fi diff --git a/.github/workflows/rebaseline-tests.yml b/.github/workflows/rebaseline-tests.yml new file mode 100644 index 0000000000000..e636b31c95cc6 --- /dev/null +++ b/.github/workflows/rebaseline-tests.yml @@ -0,0 +1,62 @@ +name: Rebaseline Tests + +on: + workflow_dispatch: + # Trigger every day at 3.30 AM PST (= 10:30 AM UTC) + schedule: + - cron: "30 10 * * *" + +permissions: + contents: write + pull-requests: write + +jobs: + rebaseline-tests: + name: Rebaseline Tests + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ github.token }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + - name: pip install + run: | + which python3 + python3 --version + python3 -m pip install -r requirements-dev.txt + - name: Install emsdk + run: | + EM_CONFIG=$HOME/emsdk/.emscripten + echo $EM_CONFIG + curl -# -L -o ~/emsdk-main.tar.gz https://github.com/emscripten-core/emsdk/archive/main.tar.gz + tar -C ~ -xf ~/emsdk-main.tar.gz + mv ~/emsdk-main ~/emsdk + cd ~/emsdk + ./emsdk install tot + ./emsdk activate tot + echo "JS_ENGINES = [NODE_JS]" >> $EM_CONFIG + echo "final config:" + cat $EM_CONFIG + echo "EM_CONFIG=$EM_CONFIG" >> $GITHUB_ENV + - name: Rebaseline tests + run: | + git config user.name emscripten-bot + git config user.email emscripten-bot@users.noreply.github.com + ./bootstrap + if ./tools/maint/rebaseline_tests.py --new-branch; then + echo "rebaseline_tests returned zero, expectations up-to-date" + # Exit early and don't create a PR + exit 0 + else + code=$? + if [[ $code != 2 ]] ; then + echo "rebaseline_tests.py failed with unexpected error $code (expected 2)" + exit 1 + fi + fi + git push origin rebaseline_tests + gh pr create --fill --base ${{ github.ref_name }} + gh pr merge --squash --auto diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 1ec3af9dd58c9..6f8a036e68ac2 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -29,7 +29,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 with: results_file: results.sarif results_format: sarif @@ -44,7 +44,7 @@ jobs: # Upload the results as artifacts (optional). - name: "Upload artifact" - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: SARIF file path: results.sarif @@ -52,6 +52,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 + uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: sarif_file: results.sarif diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml new file mode 100644 index 0000000000000..66c9d08d51b6b --- /dev/null +++ b/.github/workflows/tag-release.yml @@ -0,0 +1,53 @@ +# Tag release and update changelog +name: Tag release and update Changelog + +on: + workflow_dispatch: + inputs: + release-sha: + required: true + type: string + +jobs: + create-release: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + # Updates changelog and writes the release version into the environment + - name: Update Changelog + run: python3 tools/maint/create_release.py --action + - name: Create Changelog PR + id: cpr + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.EMSCRIPTEN_BOT_TOKEN }} + title: Mark ${{ env.RELEASE_VERSION }} as released + body: Update changelog and emscripten-version.txt [ci skip] + team-reviewers: release-reviewers + branch: release_${{ env.RELEASE_VERSION }} + delete-branch: true + - name: Enable auto-merge + run: gh pr merge --squash --auto "${{ steps.cpr.outputs.pull-request-number }}" + env: + GH_TOKEN: ${{ secrets.EMSCRIPTEN_BOT_TOKEN }} + - name: Tag release sha + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.EMSCRIPTEN_BOT_TOKEN }} + script: | + const tag_sha = '${{ inputs.release-sha }}'; + const release_version = '${{ env.RELEASE_VERSION }}'; + console.log(`Version ${release_version} at SHA ${tag_sha}`); + const regex = /^[0-9]+.[0-9]+.[0-9]+$/; + const match = release_version.match(regex); + if (!match) { + throw new Error('Malformed release version'); + } + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/tags/${release_version}`, + sha: tag_sha + }); + diff --git a/.gitignore b/.gitignore index c2d9041dcbfeb..558e31c0b3b46 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ *.swp # Compiled python files -*.pyc +__pycache__ # Default emscripten cache /cache @@ -30,8 +30,52 @@ coverage.xml # Test output /out/ -# All the ps1 files are generated by bootstrap... +# Windows ps1 launchers (created by ./tools/maint/create_entry_points.py) *.ps1 # ...except the templates. !/tools/run_python.ps1 !/tools/run_python_compiler.ps1 + +# Shell scripts (created by ./tools/maint/create_entry_points.py) +em-config +emar +embuilder +emcmake +emconfigure +emdump +emdwp +emmake +emnm +empath-split +emprofile +emranlib +emrun +emscan-deps +emscons +emsize +emstrip +emsymbolizer +tools/file_packager +tools/webidl_binder + +# Windows .bat files (created by ./tools/maint/create_entry_points.py) +em-config.bat +emar.bat +embuilder.bat +emcmake.bat +emconfigure.bat +emdump.bat +emdwp.bat +emmake.bat +emnm.bat +empath-split.bat +emprofile.bat +emranlib.bat +emrun.bat +emscan-deps.bat +emscons.bat +emsize.bat +emstrip.bat +emsymbolizer.bat +tools/file_packager.bat +tools/webidl_binder.bat diff --git a/.mypy.ini b/.mypy.ini deleted file mode 100644 index 6974ee7bb8f0c..0000000000000 --- a/.mypy.ini +++ /dev/null @@ -1,15 +0,0 @@ -[mypy] -mypy_path = third_party/,third_party/ply,third_party/websockify -files = . -exclude = (?x)( - cache | - third_party | - conf\.py | - emrun\.py | - tools/scons/site_scons/site_tools/emscripten/__init__\.py | - site/source/get_wiki\.py | - test/parse_benchmark_output\.py - ) - -[mypy-tools.create_dom_pk_codes,tools.webidl_binder,tools.toolchain_profiler,tools.filelock,tools.find_bigvars,leb128,ply.*] -ignore_errors = True diff --git a/AUTHORS b/AUTHORS index 5aed48f68b996..3baa0068ef86b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -601,3 +601,4 @@ a license to everyone to use it as detailed in LICENSE.) * Artur Gatin (copyright owned by Teladoc Health, Inc.) * Christian Lloyd (copyright owned by Teladoc Health, Inc.) * Sean Morris +* Mitchell Wills (copyright owned by Google, Inc.) diff --git a/ChangeLog.md b/ChangeLog.md index b012f668967ad..b968afb3e0c2c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -18,8 +18,483 @@ to browse the changes between the tags. See docs/process.md for more on how version tagging works. -3.1.65 (in development) +4.0.17 (in development) ----------------------- +- Minimum Firefox version was bumped up to Firefox 68 ESR, since older Firefox + versions are not able to run the parallel browser harness: (#25493) + - Firefox: v65 -> v68 +- For windows users, colored console output for error messages and logging now + requires Windows 10 or above. (#25502) +- Fixed an issue from previous release 4.0.16 where "-sENVIRONMENT=worker" was + erroneously made to imply "-sENVIRONMENT=web,worker" (#25514) +- Passing '-sENVIRONMENT=worker' is now disallowed due to being ambiguous in + its meaning. Instead, use '-sENVIRONMENT=web,worker' or + '-sENVIRONMENT=node,worker' to refer to either Web or Node.js multithreading. + +4.0.16 - 10/07/25 +----------------- +- A warning was added about usage of embind without C++17 or above. (#25424) +- The minimum supported versions of Node, Chrome and Firefox were bumped + enabling the removal of the `globalThis` polyfill and universally enabling + mutable globals: (#25375, #25385) + - Node: v10.19.0 -> v12.22.9 + - Chrome: v70 -> v74 + - Firefox: v55 -> v65 +- The Embind `val` functions `call`, `operator()`, and `new_` now support + passing `pointer`s by using the `allow_raw_pointers()` argument. This feature + is only enabled with C++17 and newer. Older versions will allow pointers by + default. + +4.0.15 - 09/17/25 +----------------- +- The `RELOCATABLE` and `LINKABLE` settings were deprecated in favor the higher + level and better supported `MAIN_MODULE` / `SIDE_MODULE` settings. (#25265) +- The `-gsource-map` flag has been updated to be independent of other types of + debugging effects (in particular it no longer causes the wasm binary to have + a name section, and it no longer suppresses minification of the JS output). + To get the previous behavior, add `-g2` along with `-gsource-map`. + See also the newly updated + [documentation](https://emscripten.org/docs/porting/Debugging.html) which + covers debugging flags and use cases (#25238). +- Ogg port updated to 1.3.5. (#25274) +- Vorbis port updated to 1.3.7. (#25274) +- SDL3 port updated to 3.2.22. (#25273) + +4.0.14 - 09/02/25 +----------------- +- The `-sASYNCIFY_LAZY_LOAD_CODE` setting and the corresponding C function + `emscripten_lazy_load_code` were removed. (#25236) +- The `addRunDependency` and `removeRunDependency` API are now optional and need + to be included and/or exported using, for example, + `DEFAULT_LIBRARY_FUNCS_TO_INCLUDE` or `EXPORTED_RUNTIME_METHODS`. (#24974) +- The `LOAD_SOURCE_MAP` setting was made an internal setting. This was always + an internal detail of the sanitizers, which is enabled automatically when + needed, so setting it explicitly should never be needed. (#24967) +- The wasm offset converter was removed along with the `USE_OFFSET_CONVERTER` + setting. This feature only existed to work around an old v8 bug that was fixed + back in 2019. (#24963) + +4.0.13 - 08/14/25 +----------------- +- The `handle` callback on the `preloadPlugins` used by `--use-preload-plugins` + (and `FS_createPreloadedFile` API`) was converted from callbacks to async. + Any externally managed plugins would need to be updated accordingly. An + assertion will detect any such non-async plugins in the wild. (#24914) +- SDL2 updated from 2.32.0 to 2.32.8. (#24912/) +- `sdl-config` and `sdl2-config` scripts were simplified to avoid using python + and the `.bat` file versions were removed, matching upstream SDL. (#24907) +- The `addRunDependency`/`removeRunDependency` now assert in debug builds if + they are not passed an `id` parameter. We have been issuing warnings in + this case since 2012 (f67ad60), so it seems highly unlikely anyone is not + passing IDs here. (#24890). +- The `-sMODULARIZE` setting generates a factory function that must be called + before the program is instantiated that run. However, emscripten previously + had very special case where this instantiation would happen automatically + (with no parameterization) under certain specific circumstances: When + `-sMINIMAL_RUNTIME`, `-sSINGLE_FILE` and `-sMODULARIZE` were used in + combination with default html output. This special case was removed. If you + want to instantiate the module on startup you can still do so by adding a call + the factory function in `--extern-post-js`. (#24874) +- emcc will now error if `MINIMAL_RUNTIME_STREAMING_WASM_COMPILATION` is used + when not generating html output. This was always incompatible but previously + ignored. (#24849) +- emcc will now error if `MINIMAL_RUNTIME_STREAMING_WASM_COMPILATION` or + `MINIMAL_RUNTIME_STREAMING_WASM_INSTANTIATION` are used with `SINGLE_FILE`. + These are fundamentally incompatible but were previously ignored. (#24849) +- `--export-es6` flag was added to `file_packager.py` available when run + standalone, to enable ES6 imports of generated JavaScript code (#24737) + +4.0.12 - 08/01/25 +----------------- +- The `#!` line that emscripten, under some circumstances, will add to the + generated JS code no longer injects the `--experimental-wasm-bigint` node + flag. This flag is not needed on recent versions of node, and in fact + errors there, so it's not possible to know if it's safe to include. (#24808) +- In `-sMODULARIZE` mode the factory function will now always return a promise, + even when `WASM_ASYNC_COMPILATION` is disabled. This is because emscripten + has other features that might also return async module creation (e.g. loading + files over the network, or other users of the `addRunDependency` API). For + consistency and simplicity we now *always* return a promise here. (#24727) +- libcxx, libcxxabi, libunwind, and compiler-rt were updated to LLVM 20.1.8. + (#24757) +- The `fsblkcnt_t` and `fsfilcnt_t` types used by `statfs`/`statvfs` were + changed from 32-bit to 64-bit. (#24769) +- Support for `-sTEXT_DECODER=0` was removed, due to widespread support for + `TextDecoder`. The remaining valid values for this setting are `=1` + (conditional use of `TextDecoder` with fallback) and `=2` (unconditional use + of `TextDecoder`). (#24700) + +4.0.11 - 07/14/25 +----------------- +- `emdump` tool/script was removed. This tool was mostly useful for analyzing + asm.js code, which emscripten has not generated in a long time now. +- Add support for [Source-based Code Coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html) + To build with coverage enabled use `-fprofile-instr-generate -fcoverage-mapping`. (#24160) +- The `ENVIRONMENT` setting will now be automatically updated to include + `worker` if multi-threading is enabled. (#24525) +- Removed the `HEADLESS` option. It tried to simulate a minimal browser-like + environment before browser engines had real headless modes. For headless + testing, users are now encouraged to use + [Playwright](https://playwright.dev/), [Puppeteer](https://pptr.dev/) or + Node.js with [JSDOM](https://github.com/jsdom/jsdom) instead. You can also + use browser headless mode with `emrun` as follows: + emrun --browser=chrome --browser-args=--headless [..] + for chrome, or + emrun --browser=firefox --browser-args=-headless [..] + for firefox. (#24537) +- When JSPI is enabled `async` library functions are no longer automatically + wrapped with `WebAssembly.Suspending` functions. To automatically wrap library + functions for use with JSPI they must now explicitly set + `myLibraryFunction__async: true`. +- Removed special casing for `size_t` in Embind, since it was also inadvertently + affecting `unsigned long` on wasm64. Both will now match the behaviour of + other 64-bit integers on wasm64 and will be passed as `bigint` instead of + `number` to the JavaScript code. (#24678) + +4.0.10 - 06/07/25 +----------------- +- Emscripten ports now install pkg-config `.pc` files so they will show up, for + example, when you run `pkg-config --list-all` or `pkg-config --cflags + `. Bare in mind that the correct PKG_CONFIG_PATH needs to be set for + this to work. One way to do this is to run `emmake pkg-config`. (#24426) +- libcxx, libcxxabi, and compiler-rt were updated to LLVM 20.1.4. (#24346 and + #24357) +- Emscripten will not longer generate trampoline functions for Wasm exports + prior to the module being instantiated. Storing a reference to a Wasm export + (e.g. `Module['_malloc']`) prior to instantiation will no longer work. In + debug builds we generate stub functions that can detect this case. (#24384) +- The `-sASYNCIFY_LAZY_LOAD_CODE` setting was deprecated. This setting was + added as an experiment a long time ago and as far we know has no active users. + In addition, it cannot work with JSPI (the future of ASYNCIFY). (#24383) +- `-sUSE_WEBGPU` was deprecated in favor of the external port Emdawnwebgpu, a + fork of Emscripten's original bindings, implementing a newer, more stable + version of the standardized `webgpu.h` interface. Please try migrating using + `--use-port=emdawnwebgpu`. If you find issues, verify in the [latest + nightly release](https://github.com/google/dawn/releases) and file feedback + with Dawn. (Emdawnwebgpu is maintained as part of Dawn, the open-source + WebGPU implementation used by Chromium, but it is still cross-browser.) +- The `-sMAYBE_WASM2JS` setting was removed. This was originally added for + debugging purposes, and we now have `-sWASM=2` for folks that want to be able + to fall back to js if wasm fails. (#24176) +- The field `responseUrl` is added to `emscripten_fetch_t`. This is notably + usable for obtaining resolved URL, in line with JS `XMLHttpRequest.responseURL` + field. (#24414) +- `emscripten_fetch_get_response_headers_length` now excludes the trailing + null character from the length calculation to match the documented behaviour. + (#24486) +- `--closure=1` can now be used while preserving readable function names with + `-g2` or `-g`. +- Functions `UTF8ToString`, `UTF16ToString` and `UTF32ToString` take a new + optional `ignoreNul` parameter that allows to ignore the NUL characters and + read the entire string up to the specific byte length. (#24487) + +4.0.9 - 05/19/25 +---------------- +- cmake will not longer detect SDL2 or SDL3 as being present until they are + installed in the sysroot. This means that they now need to be installed, + either indirectly (e.g. by running any emcc command with `-sUSE_SDL=2`) or + directly (e.g. by running `./embuilder build sdl2`). (#24306) +- libunwind was updated to LLVM 20.1.4. (#24251) +- When using cmake the EMSCRIPTEN_FORCE_COMPILERS setting was reverted to + being on by default due to issues that were found with disabling it. (#24223) +- Several symbols from embind (`InternalError`, `BindingError`, + `count_emval_handles`) and from `libbrowser.py` (`requestFullscreen`, + `requestFullScreen`, `createContext`, `getUserMedia`, `setCanvasSize`) are no + longer exported by default. They can be exported using + `-sEXPORTED_RUNTIME_METHODS=requestFullscreen`, for example. (#24223, #24269) +- Embind: fixed support for unsigned 64-bit integers, which were previously + returned to JavaScript as their signed counterparts. (#24285) +- Added handing for 64-bit integer access to AddressSanitizer, `-sSAFE_HEAP` and + `-sSUPPORT_BIG_ENDIAN` features. (#24283) + +4.0.8 - 04/30/25 +---------------- +- Programs built with `-sWASM_WORKERS` and `-sAUDIO_WORKLET` no longer generate + separate `.ww.js` and `.aw.js` files. This is similar to the change that was + already made for pthreads in #21701. This saves on complexity, code size and + network requests. (#24163, #24190) +- Closure arguments can now be used from ports using `settings.CLOSURE_ARGS` + (#24192) +- Embind's `val` now requires a pointer policy when using pointers. e.g. + `(val v(pointer, allow_raw_pointers())`. + +4.0.7 - 04/15/25 +---------------- +- Added experimental support for Wasm ESM integration with + `-sWASM_ESM_INTEGRATION`. This is currently only supported in node behind a + flag and not in any browsers. (#23985) +- Runtime callbacks registered in `Module['preRun']` or `Module['postRun']`, or + using `addOnPreRun()`, `addOnInit()`, `addOnPostCtor()`, `addOnPreMain()`, + `addOnExit()`, or `addOnPostRun()`, are now enqueued and executed following + the order of registration (i.e. `Module['preRun'] = [a, b]`, or equivalently + `addOnPreRun(a); addOnPreRun(b);` will run `a` then `b`; the previous behavior + was to run `b` then `a`). While this might be a breaking change for some users, + the intention is to be more consistent by making those callbacks match the + behavior of `Module['preInit']` and compile time callbacks (rather than the + contrary, as we generally expect an array of functions to be executed left to + right). (#24012) +- The standard memory views (HEAP8, HEAP32, etc) are no longer exported by + default. This matches the existing behaviour of `-sSTRICT` and + `-sMINIMAL_RUNTIME`. If you need to access those from outside the module code + you can export them by adding them to `-sEXPORTED_RUNTIME_METHODS`. For + example, `-sEXPORTED_RUNTIME_METHODS=HEAP8,HEAPU32` (if you need `HEAP8` and + `HEAPU32`). (#24079) +- libjpeg port updated from 9c to 9f. (#24085) +- Missing exports in EXPORTED_RUNTIME_METHODS will now error instead of warn. + +4.0.6 - 03/26/25 +---------------- +- Added support for applying path prefix substitution to the sources of the + source map : use `-sSOURCE_MAP_PREFIXES=["="]` with `-gsource-map`. + Alternatively, you can now embed the sources content into the source map file + using `-gsource-map=inline`. (#23741) +- The python `__file__` builtin now works in the emscripten config file. + (#23973) +- Three deprecated settings were removed. These settings were marked as + deprecated for more than year: + - SUPPORT_ERRNO: Instead, export `__errno_location` if needed. + - EXTRA_EXPORTED_RUNTIME_METHODS: Instead use EXPORTED_RUNTIME_METHODS. + - DEMANGLE_SUPPORT: Instead use the `$demangle` JS libary function. + (#23975) + +4.0.5 - 03/12/25 +---------------- +- Added initial support for wasm source phase imports via + `-sSOURCE_PHASE_IMPORTS`. This is currently experimental and not yet + implemented in browsers. (#23175) +- The `FS.allocate` API was removed. This was originally intended to + implement the fallocate/posix_fallocate system calls, but without the ability + to punch holes (`FALLOC_FL_PUNCH_HOLE`) the `FS.truncate` API is sufficient + for resizing files. + +4.0.4 - 02/25/25 +---------------- +- An initial port of SDL3 was added. Use it with `-sUSE_SDL=3`. This port + is still experimental. (#23630) +- The `--output_eol` command line flag was renamed `--output-eol` for + consistency with other flags. The old name continues to work as an alias. + (#20735) +- Added Lua contrib port (`--use-port=contrib.lua`) to easily embed the Lua + scripting language in any C/C++ Emscripten project (#23682) +- The `USE_ES6_IMPORT_META` settings was removed. This setting was always + on by default, but now it cannot be disabled. This setting was originally + added in 2019 as a temporary measure while engines and bundlers learned to + deal with `import.meta`. (#23171) + +4.0.3 - 02/07/25 +---------------- +- emscan-deps tools was added. This tool wraps clang-scan-deps and injects the + needed `--target` and `--sysroot` argument that would normally be injected by + emcc itself. This enables support for C++20 in cmake projects. (#21987) +- The version of python required to run emscripten was bumped from 3.6 to 3.8. + (#23417) +- The `EM_LOG_C_STACK` flag to `emscripten_log` was deprecated and the helper + file on which it was based (`emscripten-source-map.min.js`) deleted. This + feature (userspace source map parsing in logs) was never ported to wasm + source maps, so it has not worked in many years, and there have been no + requests for it. This has no impact on the source map support in browser + devtools. (#23553) +- The WASMFS fetch backend now fetches files in chunks using HTTP range + requests (if supported by the server). `wasmfs_create_fetch_backend` now + takes a second parameter (`uint32_t chunk_size`) to configure the size of + each chunk. If a file is read a few times with random accesses, a small + chunk size will minimize bandwidth; if a file is read in larger contiguous + ranges, a larger chunk size will reduce the number of requests. (#23021) + +4.0.2 - 01/30/25 +---------------- +- The standard Wasm EH, enabled by `-sWASM_LEGACY_EXCEPTIONS=0`, now uses the + LLVM backend implementation rather than the previously used Binaryen + translator + (https://github.com/WebAssembly/binaryen/blob/main/src/passes/TranslateEH.cpp). + (#23469) No specific action from the user is required. +- Added support for compiling AVX2 intrinsics, 256-bit wide intrinsic is emulated + on top of 128-bit Wasm SIMD instruction set. (#23035). Pass `-msimd128 -mavx2` + to enable targeting AVX2. +- The system JS libraries in `src/` were renamed from `library_foo.js` to + `lib/libfoo.js`. They are still included via the same `-lfoo.js` flag so + this should not be a user-visible change. (#23348) +- When using cmake the emscripten toolchain will no longer skip the toolchain + detection stages. This means the initial cmake run will be slower, but will + result in more accruate information. If cmake is running too slow for you, + you can revert to the previous behaviour with `-DEMSCRIPTEN_FORCE_COMPILERS=ON`. + +4.0.1 - 01/17/25 +---------------- +- The minimum version of node required to run emscripten was bumped from v16.20 + to v18.3. Version 4.0 was mistakenly shipped with a change that required v20, + but that was reverted. (#23410) +- `emscripten_webgl_create_context` now displays a warning message when there is + a conflict between the `majorVersion` requested and the WebGL support defined + via linker flags (`MIN_WEBGL_VERSION` and `MAX_WEBGL_VERSION`). This warning + will be turned into a hard failure in a future release. (#23372, #23416) +- zlib port updated from 1.2.13 to 1.3.1. (#23462) + +4.0.0 - 01/14/25 +---------------- +- Emscripten version was bumped to 4.0.0. Happy new year, happy new major + version! While version has a few interesting changes, there is nothing huge + that makes it different from any other release. (#19053) +- `-sWASM_LEGACY_EXCEPTIONS` option is added. (#23365) If true, it will emit + instructions for the legacy Wasm exception handling proposal + (https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/legacy/Exceptions.md), + and if false, the new standardized exception handling proposal + (https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md). + This option defaults to true, given that major web browsers do not support the + new proposal by default yet. This option replaces the existing + `-sWASM_EXNREF`, whose meaning was the opposite. +- compiler-rt, libcxx, libcxxabi, and libunwind were updated to LLVM 19.1.6. + (#22937, #22994, and #23294) +- The default Safari version targeted by Emscripten has been raised from 14.1 + to 15.0 (the `MIN_SAFARI_VERSION` setting) (#23312). This has several effects: + - The Wasm nontrapping-fptoint feature is enabled by default. Clang will + generate nontrapping (saturating) float-to-int conversion instructions for + C typecasts. This should have no effect on programs that do not have + undefined behavior but if the casted floating-point value is outside the range + of the target integer type, the result will be a number of the max or min value + instead of a trap. This also results in a small code size improvement because + of details of the LLVM IR semantics. This feature can be disabled in clang with + the `-mno-nontrapping-fptoint` flag. (#23007) + - The `WASM_BIGINT` feature is enabled by default. This has the effect that + Wasm i64 values are passed and returned between Wasm and JS as BigInt values + rather than being split by Binaryen into pairs of Numbers. (#22993) + - The `BULK_MEMORY` feature is enabled by default. `memory.copy` and + `memory.fill` instructions are used in the implementation of C `memcpy` and + `memset`, and Clang may generate them elsewhere (#22873). It can be + disabled with the `-mno-bulk-memory -mno-bulk-memory-opt` flags. +- When using `-sMODULARIZE` we now assert if the factory function is called with + the JS `new` keyword. e.g. `a = new Module()` rather than `b = Module()`. + This paves the way for marking the function as `async` which does not allow + `new` to be used. This usage of `new` here was never documented and is + considered an antipattern. (#23210) +- `PATH.basename()` no longer calls `PATH.normalize()`, so that + `PATH.basename("a/.")` returns `"."` instead of `"a"` and + `PATH.basename("a/b/..")` returns `".."` instead of `"a"`. This is in line with + the behaviour of both node and coreutils, and is already the case when using + NODERAWFS". (#23180) +- The factory function exposed in `-sMODULARIZE` mode is now marked as `async` + when `WASM_ASYNC_COMPILATION` is enabled (the default). This allows us to use + `await` during module creation. One side effect of this is that code in + `--post-js` files will now be delayed until after module creation and after + `main` runs. This matches the existing behaviour when using sync instantation + (`-sWASM_ASYNC_COMPILATION=0`) but is an observable difference. (#23157) +- The `POLYFILL_OLD_MATH_FUNCTIONS` setting was removed. The browser versions + that require these polyfills are no longer supported by emscripten so the + polyfills should never be needed. (#23262) +- JavaScript libraries can now be specified via `-lfoo.js`. This works like the + existing `--js-library` flag but will search the library path (all paths + specified with `-L`) for `libfoo.js`. (#23338) +- The `mallinfo` struct members are now defined as `size_t` which makes them + compatible with larger memories, and is also how linux defines them. (#23368) +- Emscripten now uses the debug version of malloc (i.e. assertions enabled) + when linking in debug mode (`-O0` and/or `-sASSERTIONS`). This means that + things like double-free will be detected in these builds. Previously this was + only true with `-sASSERTIONS=2`. (#23330) +- The code geneated in `--proxy-to-worker` no longer contains support for + reading the `?noProxy` URL parameter (this was not documented or tested). + (#23297) + +3.1.74 - 12/14/24 +----------------- +- The file system was updated to independently track atime, mtime and ctime + instead of using the same time for all three. (#22998) +- Emscripten-generated code will now use async/await internally when loading + the Wasm module. This will be lowered away by babel when targeting older + browsers. (#23068) +- Due to the discontinued support for invalid specializations of + `std::basic_string` (https://github.com/llvm/llvm-project/pull/72694), the + support for `std::basic_string` was removed from embind. + (#23070) +- The minimum supported versions of browser engines that we support were updated + to versions that support Promise, Fetch and Object.asign APIs, allowing the + polyfills for these to be removed. Chrome 32 -> 45, Firefox 34 -> 40, Safari + 9.0 -> 10.1. These browser engines version are all over 8 years old now. + (#23077, #23118) + +3.1.73 - 11/28/24 +----------------- +- libunwind was updated to LLVM 19.1.4. (#22934) +- mimalloc was updated to 2.1.7. (#21548) + +3.1.72 - 11/19/24 +----------------- +- The `MEMORY64` setting is no longer experimental. At time of writing all + browsers still require a flag to run the resulting binaries but that should + change in the coming months since the proposal is now at stage 4. (#22864) +- GLFW: Fixed regression introduced in 3.1.51. CSS scaling is now available + again. Note that CSS scaling is disabled in HiDPI mode. (#22847, #22900) + +3.1.71 - 11/04/24 +----------------- +- SDL2 port updated to 2.30.9. (#22830) +- LLVM's `-Wnontrivial-memaccess` warning has been updated to also warn about + passing non-trivially-copyable destination parameter to `memcpy`, + `memset` and similar functions for which it is a documented undefined + behavior (#22798). See https://github.com/llvm/llvm-project/pull/111434 +- The automatic fallback to `$HOME/.emscripten_cache` when the emscripten + directory is read-only was removed. This automatic behaviour could cause + confusion. Anyone who really wants to use `$HOME/.emscripten_cache` can + still do so either via an environment variable (`EMCC_CACHE`) or via a config + file setting `CACHE`. +- The standalone `file_packager.py` tool now outputs modern JS (specifically it + includes nullish assignment). If you use this output directly and you want + to support older browsers you may need to transpile it. If you use + `file_packager` via emcc the output will be transpiled as part of the emcc + output. (#22805) + +3.1.70 - 10/25/24 +----------------- +- Improvements to Audio Worklet support (#22731, #22681) +- Small improvements to embind (#22734) + +3.1.69 - 10/12/24 +----------------- +- The usage of `EM_BOOL` in the emscripten API has been replaced with C/C++ + bool. This change should not be observable since `EM_BOOL` has been + equivalent to `bool` since #22157. (#22155) +- Fix regression introduced in 3.1.67 (#22557) which broke webgpu / int64 + integration. (#22689) +- SDL2 port updated from 2.28.4 to 2.30.8. (#22697) +- embind no longer exports any library functions by default. Previously we + would export getInheritedInstanceCount, getLiveInheritedInstances, + flushPendingDeletes and setDelayFunction. If you need these library function + exprted they can be added to `EXPORTED_RUNTIME_METHODS`. (#22705) + +3.1.68 - 09/30/24 +----------------- +- Added support for compiling 256-bit wide AVX intrinsics, emulated on top + of 128-bit Wasm SIMD instruction set. (#22430). Pass `-msimd128 -mavx` to + enable targeting AVX. +- Pthread-based programs no longer generates `.worker.js` file. This file was + made redundant back in 3.1.58 and now is completely removed. (#22598) +- The freetype port was updated from v2.6 to v2.13.3. (#22585) +- The number of arguments passed to Embind function calls is now only verified + with ASSERTIONS enabled. (#22591) +- Optional arguments can now be omitted from Embind function calls. (#22591) +- Recent changes to Binaryen included in this version significantly improve + the speed at which the post-link optimizations run for some large programs. + +3.1.67 - 09/17/24 +----------------- +- Add option `nonnull()` to Embind to omit `| null` from TS definitions + for functions that return pointers. + +3.1.66 - 09/10/24 +----------------- +- The behaviour of the `pthread_kill` function was fixed to match the spec + and will now run the designated handler on the target thread. (#22467) +- Added support for WebGL extensions EXT_clip_control, EXT_depth_clamp, + EXT_polygon_offset_clamp and WEBGL_polygon_mode (#20841) +- New `emscripten_console_trace` and `emscripten_dbg_backtrace` APIs we were + added to `console.h`. The former simply maps directly to `console.trace`. + The latter uses `dbg()` so it writes directly to stderr under node (better for + multi-threaded apps). + +3.1.65 - 08/22/24 +----------------- +- A new `--emit-minification-map` command line flag was added, which can be used + to emit a minifiction map in the case that import/export minification is + performed (this happens at higher optimization levels). (#22428) - Remove `Module['quit']` handling. This could be used to override the internal method for shutting down the program, but it was neither documented nor tested. Programs that want to intercept the shutting down of a program can @@ -589,7 +1064,7 @@ See docs/process.md for more on how version tagging works. `ASSERTIONS` is enabled. This option is mainly for the users who want only exceptions' stack traces without turning `ASSERTIONS` on. (#18642 and #18535) - `SUPPORT_LONGJMP`'s default value now depends on the exception mode. If Wasm - EH (`-fwasm-exception`) is used, it defaults to `wasm`, and if Emscripten EH + EH (`-fwasm-exceptions`) is used, it defaults to `wasm`, and if Emscripten EH (`-sDISABLE_EXCEPTION_CATCHING=0`) is used or no exception support is used, it defaults to `emscripten`. Previously it always defaulted to `emscripten`, so when a user specified `-fwasm-exceptions`, it resulted in Wasm EH + Emscripten diff --git a/bootstrap b/bootstrap index eef0f00c6730f..16710c6c5cf88 100755 --- a/bootstrap +++ b/bootstrap @@ -11,8 +11,8 @@ # To make modifications to this file, edit `tools/run_python.sh` and then run # `tools/maint/create_entry_points.py` -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. +# $PYTHON -E does not ignore _PYTHON_SYSCONFIGDATA_NAME, an internal of cpython +# used in cross compilation via setup.py, so we unset it explicitly here. unset _PYTHON_SYSCONFIGDATA_NAME if [ -z "$PYTHON" ]; then diff --git a/bootstrap.bat b/bootstrap.bat index 83edd646f7918..e8840745a6d33 100644 --- a/bootstrap.bat +++ b/bootstrap.bat @@ -23,7 +23,7 @@ :: %~dp0 expansions will not work. :: So first try if %~dp0 might work, and if not, manually look up this script from PATH. @if exist "%~f0" ( - set MYDIR=%~dp0 + set "MYDIR=%~dp0" goto FOUND_MYDIR ) @for %%I in (%~n0.bat) do ( diff --git a/bootstrap.py b/bootstrap.py index 695ae528b212b..059415053c5ed 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -10,6 +10,7 @@ import argparse import os import shutil +import subprocess import sys __rootdir__ = os.path.dirname(os.path.abspath(__file__)) @@ -17,18 +18,27 @@ STAMP_DIR = os.path.join(__rootdir__, 'out') -from tools import shared, utils +# N.b. This script bootstrap.py cannot use 'from tools import shared', +# because shared.py requires that a valid .emscripten config is already +# created. Bootstrap.py needs to be run before an .emscripten config exists. +from tools import utils actions = [ - ('npm packages', ['package.json'], [shutil.which('npm'), 'ci']), + ('npm packages', [ + 'package.json', + 'package-lock.json', + ], ['npm', 'ci']), ('create entry points', [ 'tools/maint/create_entry_points.py', 'tools/maint/run_python.bat', 'tools/maint/run_python.sh', 'tools/maint/run_python.ps1', - ], - [sys.executable, 'tools/maint/create_entry_points.py']), - ('git submodules', ['test/third_party/posixtestsuite/'], [shutil.which('git'), 'submodule', 'update', '--init']), + ], [sys.executable, 'tools/maint/create_entry_points.py']), + ('git submodules', [ + 'test/third_party/posixtestsuite/', + 'test/third_party/googletest', + 'test/third_party/wasi-test-suite', + ], ['git', 'submodule', 'update', '--init']), ] @@ -57,8 +67,23 @@ def main(args): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-v', '--verbose', action='store_true', help='verbose', default=False) parser.add_argument('-n', '--dry-run', action='store_true', help='dry run', default=False) + parser.add_argument('-i', '--install-git-hooks', action='store_true', help='install emscripten git hooks', default=False) args = parser.parse_args() + if args.install_git_hooks: + if not os.path.exists(utils.path_from_root('.git')): + print('--install-git-hooks requires git checkout') + return 1 + + dst = utils.path_from_root('.git/hooks') + if not os.path.exists(dst): + os.mkdir(dst) + + src = utils.path_from_root('tools/maint/post-checkout') + for src in ('tools/maint/post-checkout', 'tools/maint/pre-push'): + shutil.copy(utils.path_from_root(src), dst) + return 0 + for name, deps, cmd in actions: if check_deps(name, deps): print('Up-to-date: %s' % name) @@ -67,11 +92,17 @@ def main(args): stamp_file = get_stamp_file(name) if args.dry_run: print(' (skipping: dry run) -> %s' % ' '.join(cmd)) - return + continue + orig_exe = cmd[0] + if not os.path.isabs(orig_exe): + cmd[0] = shutil.which(orig_exe) + if not cmd[0]: + utils.exit_with_error(f'command not found: {orig_exe}') print(' -> %s' % ' '.join(cmd)) - shared.run_process(cmd, cwd=utils.path_from_root()) + subprocess.run(cmd, check=True, text=True, encoding='utf-8', cwd=utils.path_from_root()) utils.safe_ensure_dirs(STAMP_DIR) utils.write_file(stamp_file, 'Timestamp file created by bootstrap.py') + return 0 if __name__ == '__main__': diff --git a/cmake/Modules/FindOpenGL.cmake b/cmake/Modules/FindOpenGL.cmake index 3220a11fddbce..b1d4135a336dc 100644 --- a/cmake/Modules/FindOpenGL.cmake +++ b/cmake/Modules/FindOpenGL.cmake @@ -30,3 +30,19 @@ mark_as_advanced( OPENGL_glu_LIBRARY OPENGL_gl_LIBRARY ) + +if (NOT TARGET OpenGL::GL) + add_library(OpenGL::GL INTERFACE IMPORTED) + set_target_properties(OpenGL::GL PROPERTIES + IMPORTED_LIBNAME "${OPENGL_gl_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${OPENGL_INCLUDE_DIR}" + ) +endif() + +if (NOT TARGET OpenGL::GLU) + add_library(OpenGL::GLU INTERFACE IMPORTED) + set_target_properties(OpenGL::GLU PROPERTIES + IMPORTED_LIBNAME "${OPENGL_glu_LIBRARY}" + INTERFACE_LINK_LIBRARIES OpenGL::GL + ) +endif() diff --git a/cmake/Modules/Platform/Emscripten.cmake b/cmake/Modules/Platform/Emscripten.cmake index 0922fb856bcc3..ca4c770ad75eb 100644 --- a/cmake/Modules/Platform/Emscripten.cmake +++ b/cmake/Modules/Platform/Emscripten.cmake @@ -60,7 +60,7 @@ endif() # Locate where the Emscripten compiler resides in relative to this toolchain file. if (NOT DEFINED EMSCRIPTEN_ROOT_PATH) get_filename_component(GUESS_EMSCRIPTEN_ROOT_PATH "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) - if (EXISTS "${GUESS_EMSCRIPTEN_ROOT_PATH}/emranlib") + if (EXISTS "${GUESS_EMSCRIPTEN_ROOT_PATH}/emranlib.py") set(EMSCRIPTEN_ROOT_PATH "${GUESS_EMSCRIPTEN_ROOT_PATH}") else() # If not found by above search, locate using the EMSCRIPTEN environment variable. @@ -93,6 +93,7 @@ set(CMAKE_C_COMPILER_AR "${CMAKE_AR}") set(CMAKE_CXX_COMPILER_AR "${CMAKE_AR}") set(CMAKE_C_COMPILER_RANLIB "${CMAKE_RANLIB}") set(CMAKE_CXX_COMPILER_RANLIB "${CMAKE_RANLIB}") +set(CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS "${EMSCRIPTEN_ROOT_PATH}/emscan-deps") # Capture the Emscripten version to EMSCRIPTEN_VERSION variable. if (NOT EMSCRIPTEN_VERSION) @@ -110,10 +111,20 @@ if (NOT EMSCRIPTEN_VERSION) set(EMSCRIPTEN_VERSION "${CMAKE_MATCH_1}") endif() -# Don't allow CMake to autodetect the compiler, since this is quite slow with -# Emscripten. -# Pass -DEMSCRIPTEN_FORCE_COMPILERS=OFF to disable (sensible mostly only for -# testing/debugging purposes). +execute_process(COMMAND "${EMSCRIPTEN_ROOT_PATH}/em-config${EMCC_SUFFIX}" "CACHE" + RESULT_VARIABLE _emcache_result + OUTPUT_VARIABLE _emcache_output + OUTPUT_STRIP_TRAILING_WHITESPACE) +if (NOT _emcache_result EQUAL 0) + message(FATAL_ERROR "Failed to find emscripten cache directory with command \"'${EMSCRIPTEN_ROOT_PATH}/em-config${EMCC_SUFFIX}' CACHE\"! Process returned with error code ${_emcache_result}.") +endif() +file(TO_CMAKE_PATH "${_emcache_output}" _emcache_output) +set(EMSCRIPTEN_SYSROOT "${_emcache_output}/sysroot") + +# Allow skipping of CMake compiler autodetection. On by default since this is +# quite slow with Emscripten and also leads to issues with +# CMAKE_C_IMPLICIT_LINK_LIBRARIES. +# See https://github.com/emscripten-core/emscripten/issues/23944 option(EMSCRIPTEN_FORCE_COMPILERS "Force C/C++ compiler" ON) if (EMSCRIPTEN_FORCE_COMPILERS) @@ -159,58 +170,58 @@ if (EMSCRIPTEN_FORCE_COMPILERS) set(CMAKE_C_PLATFORM_ID "emscripten") set(CMAKE_CXX_PLATFORM_ID "emscripten") + if (NOT DEFINED CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES) + set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES "${EMSCRIPTEN_SYSROOT}/include") + endif() + if (NOT DEFINED CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES) + set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "${EMSCRIPTEN_SYSROOT}/include;${EMSCRIPTEN_SYSROOT}/include/c++/v1") + endif() + + set(CXX_COMPILE_FEATURES_BASE "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates;cxx_std_17") + set(CXX11_COMPILE_FEATURES_BASE "cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") + set(CXX14_COMPILE_FEATURES_BASE "cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") + if ("${CMAKE_VERSION}" VERSION_LESS "3.8") set(CMAKE_C_COMPILE_FEATURES "c_function_prototypes;c_restrict;c_variadic_macros;c_static_assert") set(CMAKE_C90_COMPILE_FEATURES "c_function_prototypes") set(CMAKE_C99_COMPILE_FEATURES "c_restrict;c_variadic_macros") set(CMAKE_C11_COMPILE_FEATURES "c_static_assert") - set(CMAKE_CXX_COMPILE_FEATURES "cxx_template_template_parameters;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") + set(CMAKE_CXX_COMPILE_FEATURES "cxx_template_template_parameters;${CXX11_COMPILE_FEATURES_BASE};${CXX14_COMPILE_FEATURES_BASE}") set(CMAKE_CXX98_COMPILE_FEATURES "cxx_template_template_parameters") - set(CMAKE_CXX11_COMPILE_FEATURES "cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") - set(CMAKE_CXX14_COMPILE_FEATURES "cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") + set(CMAKE_CXX11_COMPILE_FEATURES "${CXX11_COMPILE_FEATURES_BASE}") + set(CMAKE_CXX14_COMPILE_FEATURES "${CXX14_COMPILE_FEATURES_BASE}") else() # 3.8+ + set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_function_prototypes;c_std_99;c_restrict;c_variadic_macros;c_std_11;c_static_assert") + set(CMAKE_CXX_COMPILE_FEATURES "${CXX_COMPILE_FEATURES_BASE}") set(CMAKE_C90_COMPILE_FEATURES "c_std_90;c_function_prototypes") set(CMAKE_C99_COMPILE_FEATURES "c_std_99;c_restrict;c_variadic_macros") set(CMAKE_C11_COMPILE_FEATURES "c_std_11;c_static_assert") set(CMAKE_CXX98_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters") - set(CMAKE_CXX11_COMPILE_FEATURES "cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates") - set(CMAKE_CXX14_COMPILE_FEATURES "cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates") + set(CMAKE_CXX11_COMPILE_FEATURES "cxx_std_11;${CXX11_COMPILE_FEATURES_BASE}") + set(CMAKE_CXX14_COMPILE_FEATURES "cxx_std_14;${CXX14_COMPILE_FEATURES_BASE}") set(CMAKE_CXX17_COMPILE_FEATURES "cxx_std_17") - if ("${CMAKE_VERSION}" VERSION_LESS "3.12") # [3.8, 3.12) - set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_function_prototypes;c_std_99;c_restrict;c_variadic_macros;c_std_11;c_static_assert") - set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates;cxx_std_17") - else() # 3.12+ + if ("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.12") # 3.12+ set(CMAKE_CXX20_COMPILE_FEATURES "cxx_std_20") - if ("${CMAKE_VERSION}" VERSION_LESS "3.20") # [3.12, 3.20) - set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_function_prototypes;c_std_99;c_restrict;c_variadic_macros;c_std_11;c_static_assert") - set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates;cxx_std_17;cxx_std_20") - else() # 3.20+ + set(CMAKE_CXX_COMPILE_FEATURES "${CMAKE_CXX_COMPILE_FEATURES};cxx_std_20") + if ("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.20") # 3.20+ set(CMAKE_CXX23_COMPILE_FEATURES "cxx_std_23") - set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates;cxx_std_17;cxx_std_20;cxx_std_23") - if ("${CMAKE_VERSION}" VERSION_LESS "3.21") # 3.20 - set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_function_prototypes;c_std_99;c_restrict;c_variadic_macros;c_std_11;c_static_assert") - else() # 3.21+ + set(CMAKE_CXX_COMPILE_FEATURES "${CMAKE_CXX_COMPILE_FEATURES};cxx_std_23") + if ("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.25") # 3.25+ + set(CMAKE_CXX26_COMPILE_FEATURES "cxx_std_26") + set(CMAKE_CXX_COMPILE_FEATURES "${CMAKE_CXX_COMPILE_FEATURES};cxx_std_26") + endif() + if ("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.21") set(CMAKE_C17_COMPILE_FEATURES "c_std_17") set(CMAKE_C23_COMPILE_FEATURES "c_std_23") - set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_function_prototypes;c_std_99;c_restrict;c_variadic_macros;c_std_11;c_static_assert;c_std_17;c_std_23") + set(CMAKE_C_COMPILE_FEATURES "${CMAKE_C_COMPILE_FEATURES};c_std_17;c_std_23") endif() endif() endif() endif() endif() -execute_process(COMMAND "${EMSCRIPTEN_ROOT_PATH}/em-config${EMCC_SUFFIX}" "CACHE" - RESULT_VARIABLE _emcache_result - OUTPUT_VARIABLE _emcache_output - OUTPUT_STRIP_TRAILING_WHITESPACE) -if (NOT _emcache_result EQUAL 0) - message(FATAL_ERROR "Failed to find emscripten cache directory with command \"'${EMSCRIPTEN_ROOT_PATH}/em-config${EMCC_SUFFIX}' CACHE\"! Process returned with error code ${_emcache_result}.") -endif() -file(TO_CMAKE_PATH "${_emcache_output}" _emcache_output) -set(EMSCRIPTEN_SYSROOT "${_emcache_output}/sysroot") - list(APPEND CMAKE_FIND_ROOT_PATH "${EMSCRIPTEN_SYSROOT}") list(APPEND CMAKE_SYSTEM_PREFIX_PATH /) @@ -279,6 +290,10 @@ set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1) set(CMAKE_C_RESPONSE_FILE_LINK_FLAG "@") set(CMAKE_CXX_RESPONSE_FILE_LINK_FLAG "@") +# Enable $ for CMake 3.24+ +set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "-Wl,--whole-archive" "" "-Wl,--no-whole-archive") +set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED True) + # Set a global EMSCRIPTEN variable that can be used in client CMakeLists.txt to # detect when building using Emscripten. set(EMSCRIPTEN 1 CACHE INTERNAL "If true, we are targeting Emscripten output.") @@ -362,11 +377,3 @@ endif() # complain about unused CMake variable. if (CMAKE_CROSSCOMPILING_EMULATOR) endif() - -# TODO: CMake appends /usr/include to implicit includes; switching to use usr/include will make this redundant. -if (NOT DEFINED CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES) - set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES "${EMSCRIPTEN_SYSROOT}/include") -endif() -if (NOT DEFINED CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES) - set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "${EMSCRIPTEN_SYSROOT}/include") -endif() diff --git a/docs/emcc.txt b/docs/emcc.txt index 710395165026c..87be29ca9b79d 100644 --- a/docs/emcc.txt +++ b/docs/emcc.txt @@ -49,8 +49,8 @@ Options that are modified or new in *emcc* are listed below: "-O1" [compile+link] Simple optimizations. During the compile step these - include LLVM "-O1" optimizations. During the link step this does - not include various runtime assertions in JS that *-O0* would do. + include LLVM "-O1" optimizations. During the link step this omits + various runtime assertions in JS that *-O0* would include. "-O2" [compile+link] Like "-O1", but enables more optimizations. During @@ -68,15 +68,16 @@ Options that are modified or new in *emcc* are listed below: "-O3" [compile+link] Like "-O2", but with additional optimizations that - may take longer to run. + may take longer to run and may increase code size. Note: This is a good setting for a release build. "-Og" - [compile+link] Like "-O1". In future versions, this option might - disable different optimizations in order to improve debuggability. + [compile+link] Like "-O1", with an additional flag to extend the + liveness of variables for improved debugging. In future versions, + additional optimizations might also be disabled. "-Os" [compile+link] Like "-O3", but focuses more on code size (and may @@ -181,43 +182,54 @@ Options that are modified or new in *emcc* are listed below: alongside the wasm object files. This option must be used together with "-c". -"-gsource-map" - [link] Generate a source map using LLVM debug information (which - must be present in object files, i.e., they should have been - compiled with "-g"). When this option is provided, the **.wasm** - file is updated to have a "sourceMappingURL" section. The resulting - URL will have format: "" + "" + ".map". - "" defaults to being empty (which means the source map is - served from the same directory as the Wasm file). It can be changed - using --source-map-base. +"-gsource-map[=inline]" + [compile+link] [same as -g3 if passed at compile time, otherwise + applies at link] Generate a source map using LLVM debug information + (which must be present in object files, i.e., they should have been + compiled with "-g" or "-gsource-map"). + + When this option is provided, the **.wasm** file is updated to have + a "sourceMappingURL" section. The resulting URL will have format: + "" + "" + ".map". "" defaults + to being empty (which means the source map is served from the same + directory as the Wasm file). It can be changed using --source-map- + base. + + Path substitution can be applied to the referenced sources using + the "-sSOURCE_MAP_PREFIXES" (link). If "inline" is specified, the + sources content is embedded in the source map (in this case you + don't need path substitution, but it comes with the cost of having + a large source map file). "-g" - [compile+link] Controls the level of debuggability. Each level - builds on the previous one: + [compile+link] If used at compile time, adds progressively more + DWARF information to the object file, according to the underlying + behavior of clang. If used at link time, controls the level of + debuggability overall. Each level builds on the previous one: * "-g0": Make no effort to keep code debuggable. - * "-g1": When linking, preserve whitespace in JavaScript. + * "-g1": Preserve whitespace in JavaScript. - * "-g2": When linking, preserve function names in compiled code. + * "-g2": Also preserve function names in compiled code (via the + wasm name section). - * "-g3": When compiling to object files, keep debug info, - including JS whitespace, function names, and LLVM debug info - (DWARF) if any (this is the same as -g). + * "-g3": Also keep LLVM debug info (DWARF) if there is any in + the object files (this is the same as -g). "--profiling" - [same as -g2 if passed at compile time, otherwise applies at link] - Use reasonable defaults when emitting JavaScript to make the build - readable but still useful for profiling. This sets "-g2" (preserve - whitespace and function names) and may also enable optimizations - that affect performance and otherwise might not be performed in - "-g2". + [link] Make the output suitable for profiling. This means including + function names in the wasm and JS output, and preserving whitespace + in the JS output. It does not affect optimizations (to ensure that + performance profiles reflect production builds). Currenly this is + the same as "-g2". "--profiling-funcs" - [link] Preserve function names in profiling, but otherwise minify - whitespace and names as we normally do in optimized builds. This is - useful if you want to look at profiler results based on function - names, but do *not* intend to read the emitted code. + [link] Preserve wasm function names as in "--profiling", but + otherwise minify whitespace and names as we normally do in + optimized builds. This is useful if you want to look at profiler + results based on function names, but do *not* intend to read the + emitted code. "--tracing" [link] Enable the Emscripten Tracing API. @@ -232,7 +244,9 @@ Options that are modified or new in *emcc* are listed below: [link] Save a map file between function indexes in the Wasm and function names. By storing the names on a file on the side, you can avoid shipping the names, and can still reconstruct meaningful - stack traces by translating the indexes back to the names. + stack traces by translating the indexes back to the names. This is + a simpler format than source maps, but less detailed because it + only describes function names and not source locations. Note: @@ -240,13 +254,19 @@ Options that are modified or new in *emcc* are listed below: "[name].js.symbols" (with WASM symbols) and "[name].wasm.js.symbols" (with ASM.js symbols) +"--emit-minification-map " + [link] In cases where emscripten performs import/export minificiton + this option can be used to output a file that maps minified names + back to their original names. The format of this file is single + line per import/export of the form ":". + "-flto" [compile+link] Enables link-time optimizations (LTO). "--closure 0|1|2" [link] Runs the *Closure Compiler*. Possible values are: - * "0": No closure compiler (default in "-O2" and below). + * "0": No closure compiler (default). * "1": Run closure compiler. This greatly reduces the size of the support JavaScript code (everything but the WebAssembly or @@ -271,9 +291,6 @@ Options that are modified or new in *emcc* are listed below: before the closure-compiled code runs, because then it will reuse that variable. - * Closure is only run if JavaScript opts are being done ("-O2" or - above). - "--closure-args=" [link] Pass arguments to the *Closure compiler*. This is an alternative to "EMCC_CLOSURE_ARGS". @@ -571,12 +588,12 @@ Options that are modified or new in *emcc* are listed below: [compile] Tells *emcc* to emit an object file which can then be linked with other object files to produce an executable. -"--output_eol windows|linux" +"--output-eol windows|linux" [link] Specifies the line ending to generate for the text files - that are outputted. If "--output_eol windows" is passed, the final - output files will have Windows rn line endings in them. With "-- - output_eol linux", the final generated files will be written with - Unix n line endings. + that are outputted. If "--output-eol windows" is passed, the final + output files will have Windows "\r\n" line endings in them. With " + --output-eol linux", the final generated files will be written with + Unix "\n" line endings. "--cflags" [other] Prints out the flags "emcc" would pass to "clang" to diff --git a/docs/process.md b/docs/process.md index 0c3b23b1062d7..f1bf04a714a11 100644 --- a/docs/process.md +++ b/docs/process.md @@ -55,8 +55,8 @@ pre-processor. See [`.clang-format`][clang-format] for more details. ### Python Code We generally follow the pep8 standard with the major exception that we use 2 -spaces for indentation. `flake8` is run on all PRs to ensure that python code -conforms to this style. See [`.flake8`][flake8] for more details. +spaces for indentation. `ruff` is run on all PRs to ensure that Python code +conforms to this style. See [`pyproject.toml`][pyproject.toml] for more details. #### Static Type Checking @@ -304,7 +304,7 @@ To update our libraries to a newer musl release: [emsdk_tags]: https://github.com/emscripten-core/emsdk/tags [emscripten_tags]: https://github.com/emscripten-core/emscripten/tags [clang-format]: https://github.com/emscripten-core/emscripten/blob/main/.clang-format -[flake8]: https://github.com/emscripten-core/emscripten/blob/main/.flake8 +[pyproject.toml]: https://github.com/emscripten-core/emscripten/blob/main/pyproject.toml [mypy]: https://github.com/emscripten-core/emscripten/blob/main/.mypy [update_docs]: https://github.com/emscripten-core/emscripten/blob/main/tools/maint/update_docs.py [llvm_repo]: https://github.com/llvm/llvm-project diff --git a/em++.bat b/em++.bat index 68026fe148de5..7b690151414fc 100644 --- a/em++.bat +++ b/em++.bat @@ -23,7 +23,7 @@ :: %~dp0 expansions will not work. :: So first try if %~dp0 might work, and if not, manually look up this script from PATH. @if exist "%~f0" ( - set MYDIR=%~dp0 + set "MYDIR=%~dp0" goto FOUND_MYDIR ) @for %%I in (%~n0.bat) do ( diff --git a/em-config b/em-config deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/em-config +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/em-config.bat b/em-config.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/em-config.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/em-config.py b/em-config.py index 2708d18ca6dd2..bcbd9abe1cf8d 100755 --- a/em-config.py +++ b/em-config.py @@ -24,7 +24,7 @@ def main(): not re.match(r"^[\w\W_][\w\W_\d]*$", sys.argv[1]) or \ not hasattr(config, sys.argv[1]): print('Usage: em-config VAR_NAME', file=sys.stderr) - exit(1) + sys.exit(1) print(getattr(config, sys.argv[1])) return 0 diff --git a/emar b/emar deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/emar +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/emar.bat b/emar.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/emar.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/embuilder b/embuilder deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/embuilder +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/embuilder.bat b/embuilder.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/embuilder.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/embuilder.py b/embuilder.py index 9684670b4eb7a..77c3da6998266 100755 --- a/embuilder.py +++ b/embuilder.py @@ -15,6 +15,7 @@ import argparse import fnmatch import logging +import os import sys import time from contextlib import contextmanager @@ -28,30 +29,50 @@ from tools.system_libs import USE_NINJA -# Minimal subset of targets used by CI systems to build enough to useful +# Minimal subset of targets used by CI systems to build enough to be useful MINIMAL_TASKS = [ - 'libbulkmemory', 'libcompiler_rt', - 'libcompiler_rt-wasm-sjlj', + 'libcompiler_rt-mt', + 'libcompiler_rt-legacysjlj', + 'libcompiler_rt-wasmsjlj', + 'libcompiler_rt-ww', 'libc', 'libc-debug', + 'libc-mt-debug', + 'libc-ww-debug', 'libc_optz', 'libc_optz-debug', 'libc++abi', - 'libc++abi-except', + 'libc++abi-legacyexcept', + 'libc++abi-wasmexcept', 'libc++abi-noexcept', 'libc++abi-debug', - 'libc++abi-debug-except', + 'libc++abi-debug-legacyexcept', + 'libc++abi-debug-wasmexcept', 'libc++abi-debug-noexcept', + 'libc++abi-debug-mt-noexcept', + 'libc++abi-debug-ww-noexcept', 'libc++', - 'libc++-except', + 'libc++-legacyexcept', + 'libc++-wasmexcept', 'libc++-noexcept', + 'libc++-ww-noexcept', + 'libc++-debug', + 'libc++-debug-wasmexcept', + 'libc++-debug-legacyexcept', + 'libc++-debug-noexcept', + 'libc++-debug-mt-noexcept', + 'libc++-debug-ww-noexcept', 'libal', 'libdlmalloc', 'libdlmalloc-tracing', 'libdlmalloc-debug', + 'libdlmalloc-mt-debug', + 'libdlmalloc-ww', + 'libdlmalloc-ww-debug', 'libembind', 'libembind-rtti', + 'libembind-mt-rtti', 'libemmalloc', 'libemmalloc-debug', 'libemmalloc-memvalidate', @@ -61,49 +82,52 @@ 'libmimalloc-mt', 'libGL', 'libGL-getprocaddr', + 'libGL-mt-getprocaddr', + 'libGL-emu-getprocaddr', + 'libGL-emu-webgl2-ofb-getprocaddr', + 'libGL-webgl2-ofb-getprocaddr', + 'libGL-ww-getprocaddr', 'libhtml5', 'libsockets', + 'libsockets-mt', + 'libsockets-ww', 'libstubs', 'libstubs-debug', 'libstandalonewasm-nocatch', 'crt1', 'crt1_proxy_main', 'crtbegin', - 'libunwind-except', + 'libunwind-legacyexcept', + 'libunwind-wasmexcept', 'libnoexit', - 'sqlite3', - 'sqlite3-mt', 'libwebgpu', 'libwebgpu_cpp', + 'bullet', ] # Additional tasks on top of MINIMAL_TASKS that are necessary for PIC testing on # CI (which has slightly more tests than other modes that want to use MINIMAL) MINIMAL_PIC_TASKS = MINIMAL_TASKS + [ - 'libcompiler_rt-mt', 'libc-mt', - 'libc-mt-debug', 'libc_optz-mt', 'libc_optz-mt-debug', 'libc++abi-mt', 'libc++abi-mt-noexcept', 'libc++abi-debug-mt', - 'libc++abi-debug-mt-noexcept', 'libc++-mt', 'libc++-mt-noexcept', + 'libc++-debug-mt', 'libdlmalloc-mt', 'libGL-emu', 'libGL-emu-webgl2-getprocaddr', - 'libGL-mt-getprocaddr', 'libGL-mt-emu', 'libGL-mt-emu-webgl2-getprocaddr', 'libGL-mt-emu-webgl2-ofb-getprocaddr', 'libsockets_proxy', - 'libsockets-mt', 'crtbegin', 'libsanitizer_common_rt', 'libubsan_rt', - 'libwasm_workers_stub-debug', + 'libwasm_workers-debug-stub', 'libfetch', 'libfetch-mt', 'libwasmfs', @@ -156,8 +180,8 @@ def clear_port(port_name): def build_port(port_name): - with get_port_variant(port_name) as port_name: - ports.build_port(port_name, settings) + with get_port_variant(port_name) as port_name_base: + ports.build_port(port_name_base, settings) def get_system_tasks(): @@ -184,9 +208,9 @@ def main(): parser.add_argument('--lto=thin', dest='lto', action='store_const', const='thin', help='build bitcode object for ThinLTO') parser.add_argument('--pic', action='store_true', help='build relocatable objects for suitable for dynamic linking') - parser.add_argument('--force', action='store_true', + parser.add_argument('-f', '--force', action='store_true', help='force rebuild of target (by removing it first)') - parser.add_argument('--verbose', action='store_true', + parser.add_argument('-v', '--verbose', action='store_true', help='show build commands') parser.add_argument('--wasm64', action='store_true', help='use wasm64 architecture') @@ -266,6 +290,9 @@ def main(): if auto_tasks: print('Building targets: %s' % ' '.join(tasks)) + if USE_NINJA: + os.environ['EMBUILDER_PORT_BUILD_DEFERRED'] = '1' + for what in tasks: for old, new in legacy_prefixes.items(): if what.startswith(old): @@ -283,7 +310,7 @@ def main(): if USE_NINJA: library.generate() else: - library.build(deterministic_paths=True) + library.build() elif what == 'sysroot': if do_clear: cache.erase_file('sysroot_install.stamp') diff --git a/emcc.bat b/emcc.bat index 68026fe148de5..7b690151414fc 100644 --- a/emcc.bat +++ b/emcc.bat @@ -23,7 +23,7 @@ :: %~dp0 expansions will not work. :: So first try if %~dp0 might work, and if not, manually look up this script from PATH. @if exist "%~f0" ( - set MYDIR=%~dp0 + set "MYDIR=%~dp0" goto FOUND_MYDIR ) @for %%I in (%~n0.bat) do ( diff --git a/emcc.py b/emcc.py index 0d88acfb4df31..2f806e28f0f64 100644 --- a/emcc.py +++ b/emcc.py @@ -22,30 +22,30 @@ from tools.toolchain_profiler import ToolchainProfiler -import json import logging import os -import re import shlex import shutil import sys import time import tarfile +from dataclasses import dataclass from enum import Enum, auto, unique from subprocess import PIPE -from tools import shared, system_libs, utils, ports -from tools import colored_logger, diagnostics, building -from tools.shared import unsuffixed, unsuffixed_basename, get_file_suffix +from tools import shared, system_libs, utils, cmdline +from tools import diagnostics, building, compile +from tools.shared import unsuffixed_basename, get_file_suffix from tools.shared import run_process, exit_with_error, DEBUG -from tools.shared import in_temp, OFormat -from tools.shared import DYNAMICLIB_ENDINGS +from tools.shared import in_temp +from tools.shared import DYLIB_EXTENSIONS +from tools.cmdline import CLANG_FLAGS_WITH_ARGS from tools.response_file import substitute_response_files from tools import config from tools import cache -from tools.settings import default_setting, user_settings, settings, MEM_SIZE_SETTINGS, COMPILE_TIME_SETTINGS -from tools.utils import read_file, removeprefix +from tools.settings import default_setting, user_settings, settings, COMPILE_TIME_SETTINGS +from tools.utils import read_file logger = logging.getLogger('emcc') @@ -57,118 +57,57 @@ import bootstrap bootstrap.check() -# endings = dot + a suffix, compare against result of shared.suffix() -C_ENDINGS = ['.c', '.i'] -CXX_ENDINGS = ['.cppm', '.pcm', '.cpp', '.cxx', '.cc', '.c++', '.CPP', '.CXX', '.C', '.CC', '.C++', '.ii'] -OBJC_ENDINGS = ['.m', '.mi'] -PREPROCESSED_ENDINGS = ['.i', '.ii'] -OBJCXX_ENDINGS = ['.mm', '.mii'] -SPECIAL_ENDINGLESS_FILENAMES = [os.devnull] -C_ENDINGS += SPECIAL_ENDINGLESS_FILENAMES # consider the special endingless filenames like /dev/null to be C - -SOURCE_ENDINGS = C_ENDINGS + CXX_ENDINGS + OBJC_ENDINGS + OBJCXX_ENDINGS + ['.bc', '.ll', '.S'] -ASSEMBLY_ENDINGS = ['.s'] -HEADER_ENDINGS = ['.h', '.hxx', '.hpp', '.hh', '.H', '.HXX', '.HPP', '.HH'] - -# These symbol names are allowed in INCOMING_MODULE_JS_API but are not part of the -# default set. -EXTRA_INCOMING_JS_API = [ - 'fetchSettings' -] - -SIMD_INTEL_FEATURE_TOWER = ['-msse', '-msse2', '-msse3', '-mssse3', '-msse4.1', '-msse4.2', '-msse4', '-mavx'] -SIMD_NEON_FLAGS = ['-mfpu=neon'] +PREPROCESSED_EXTENSIONS = {'.i', '.ii'} +ASSEMBLY_EXTENSIONS = {'.s'} +HEADER_EXTENSIONS = {'.h', '.hxx', '.hpp', '.hh', '.H', '.HXX', '.HPP', '.HH'} +SOURCE_EXTENSIONS = { + '.c', '.i', # C + '.cppm', '.pcm', '.cpp', '.cxx', '.cc', '.c++', '.CPP', '.CXX', '.C', '.CC', '.C++', '.ii', # C++ + '.m', '.mi', '.mm', '.mii', # ObjC/ObjC++ + '.bc', '.ll', # LLVM IR + '.S', # asm with preprocessor + os.devnull, # consider the special endingless filenames like /dev/null to be C +} | PREPROCESSED_EXTENSIONS + LINK_ONLY_FLAGS = { '--bind', '--closure', '--cpuprofiler', '--embed-file', '--emit-symbol-map', '--emrun', '--exclude-file', '--extern-post-js', '--extern-pre-js', '--ignore-dynamic-linking', '--js-library', - '--js-transform', '--oformat', '--output_eol', + '--js-transform', '--oformat', '--output_eol', '--output-eol', '--post-js', '--pre-js', '--preload-file', '--profiling-funcs', '--proxy-to-worker', '--shell-file', '--source-map-base', - '--threadprofiler', '--use-preload-plugins' + '--threadprofiler', '--use-preload-plugins', } @unique class Mode(Enum): - PREPROCESS_ONLY = auto() - PCH = auto() + # Used any time we are not linking, including PCH, pre-processing, etc COMPILE_ONLY = auto() + # Only when --post-link is specified POST_LINK_ONLY = auto() + # This is the default mode, in the absence of any flags such as -c, -E, etc COMPILE_AND_LINK = auto() +@dataclass +class LinkFlag: + """Used to represent a linker flag. + + The flag value is stored along with a bool that distingingishes input + files from non-files. + + A list of these is return by separate_linker_flags. + """ + value: str + is_file: int + + class EmccState: def __init__(self, args): self.mode = Mode.COMPILE_AND_LINK # Using tuple here to prevent accidental mutation self.orig_args = tuple(args) - self.has_dash_c = False - self.has_dash_E = False - self.has_dash_S = False - # List of link options paired with their position on the command line [(i, option), ...]. - self.link_flags = [] - self.lib_dirs = [] - - def has_link_flag(self, f): - return f in [x for _, x in self.link_flags] - - def add_link_flag(self, i, flag): - if flag.startswith('-L'): - self.lib_dirs.append(flag[2:]) - # Link flags should be adding in strictly ascending order - assert not self.link_flags or i > self.link_flags[-1][0], self.link_flags - self.link_flags.append((i, flag)) - - def append_link_flag(self, flag): - if self.link_flags: - index = self.link_flags[-1][0] + 1 - else: - index = 1 - self.add_link_flag(index, flag) - - -class EmccOptions: - def __init__(self): - self.target = '' - self.output_file = None - self.no_minify = False - self.post_link = False - self.save_temps = False - self.executable = False - self.compiler_wrapper = None - self.oformat = None - self.requested_debug = '' - self.emit_symbol_map = False - self.use_closure_compiler = None - self.closure_args = [] - self.js_transform = None - self.pre_js = [] # before all js - self.post_js = [] # after all js - self.extern_pre_js = [] # before all js, external to optimized code - self.extern_post_js = [] # after all js, external to optimized code - self.preload_files = [] - self.embed_files = [] - self.exclude_files = [] - self.ignore_dynamic_linking = False - self.shell_path = None - self.source_map_base = '' - self.emit_tsd = '' - self.embind_emit_tsd = '' - self.emrun = False - self.cpu_profiler = False - self.memory_profiler = False - self.use_preload_cache = False - self.use_preload_plugins = False - self.valid_abspaths = [] - # Specifies the line ending format to use for all generated text files. - # Defaults to using the native EOL on each platform (\r\n on Windows, \n on - # Linux & MacOS) - self.output_eol = os.linesep - self.no_entry = False - self.shared = False - self.relocatable = False - self.reproduce = None def create_reproduce_file(name, args): @@ -193,7 +132,7 @@ def make_relative(filename): if arg.startswith('--reproduce='): continue - if arg.startswith('-o='): + if len(arg) > 2 and arg.startswith('-o'): rsp.write('-o\n') arg = arg[3:] output_arg = True @@ -217,13 +156,7 @@ def make_relative(filename): if ignore: continue - if arg in ('-MT', '-MF', '-MJ', '-MQ', '-D', '-U', '-o', '-x', - '-Xpreprocessor', '-include', '-imacros', '-idirafter', - '-iprefix', '-iwithprefix', '-iwithprefixbefore', - '-isysroot', '-imultilib', '-A', '-isystem', '-iquote', - '-install_name', '-compatibility_version', - '-current_version', '-I', '-L', '-include-pch', - '-Xlinker', '-Xclang'): + if arg in CLANG_FLAGS_WITH_ARGS: ignore_next = True if arg == '-o': @@ -232,270 +165,6 @@ def make_relative(filename): reproduce_file.add(rsp_name, os.path.join(root, 'response.txt')) -def expand_byte_size_suffixes(value): - """Given a string with KB/MB size suffixes, such as "32MB", computes how - many bytes that is and returns it as an integer. - """ - value = value.strip() - match = re.match(r'^(\d+)\s*([kmgt]?b)?$', value, re.I) - if not match: - exit_with_error("invalid byte size `%s`. Valid suffixes are: kb, mb, gb, tb" % value) - value, suffix = match.groups() - value = int(value) - if suffix: - size_suffixes = {suffix: 1024 ** i for i, suffix in enumerate(['b', 'kb', 'mb', 'gb', 'tb'])} - value *= size_suffixes[suffix.lower()] - return value - - -def apply_user_settings(): - """Take a map of users settings {NAME: VALUE} and apply them to the global - settings object. - """ - - # Stash a copy of all available incoming APIs before the user can potentially override it - settings.ALL_INCOMING_MODULE_JS_API = settings.INCOMING_MODULE_JS_API + EXTRA_INCOMING_JS_API - - for key, value in user_settings.items(): - if key in settings.internal_settings: - exit_with_error('%s is an internal setting and cannot be set from command line', key) - - # map legacy settings which have aliases to the new names - # but keep the original key so errors are correctly reported via the `setattr` below - user_key = key - if key in settings.legacy_settings and key in settings.alt_names: - key = settings.alt_names[key] - - # In those settings fields that represent amount of memory, translate suffixes to multiples of 1024. - if key in MEM_SIZE_SETTINGS: - value = str(expand_byte_size_suffixes(value)) - - filename = None - if value and value[0] == '@': - filename = removeprefix(value, '@') - if not os.path.isfile(filename): - exit_with_error('%s: file not found parsing argument: %s=%s' % (filename, key, value)) - value = read_file(filename).strip() - else: - value = value.replace('\\', '\\\\') - - expected_type = settings.types.get(key) - - if filename and expected_type == list and value.strip()[0] != '[': - # Prefer simpler one-line-per value parser - value = parse_symbol_list_file(value) - else: - try: - value = parse_value(value, expected_type) - except Exception as e: - exit_with_error(f'error parsing "-s" setting "{key}={value}": {e}') - - setattr(settings, user_key, value) - - if key == 'EXPORTED_FUNCTIONS': - # used for warnings in emscripten.py - settings.USER_EXPORTS = settings.EXPORTED_FUNCTIONS.copy() - - # TODO(sbc): Remove this legacy way. - if key == 'WASM_OBJECT_FILES': - settings.LTO = 0 if value else 'full' - - if key == 'JSPI': - settings.ASYNCIFY = 2 - if key == 'JSPI_IMPORTS': - settings.ASYNCIFY_IMPORTS = value - if key == 'JSPI_EXPORTS': - settings.ASYNCIFY_EXPORTS = value - - -def cxx_to_c_compiler(cxx): - # Convert C++ compiler name into C compiler name - dirname, basename = os.path.split(cxx) - basename = basename.replace('clang++', 'clang').replace('g++', 'gcc').replace('em++', 'emcc') - return os.path.join(dirname, basename) - - -def is_dash_s_for_emcc(args, i): - # -s OPT=VALUE or -s OPT or -sOPT are all interpreted as emscripten flags. - # -s by itself is a linker option (alias for --strip-all) - if args[i] == '-s': - if len(args) <= i + 1: - return False - arg = args[i + 1] - else: - arg = removeprefix(args[i], '-s') - arg = arg.split('=')[0] - return arg.isidentifier() and arg.isupper() - - -def parse_s_args(args): - settings_changes = [] - for i in range(len(args)): - if args[i].startswith('-s'): - if is_dash_s_for_emcc(args, i): - if args[i] == '-s': - key = args[i + 1] - args[i + 1] = '' - else: - key = removeprefix(args[i], '-s') - args[i] = '' - - # If not = is specified default to 1 - if '=' not in key: - key += '=1' - - # Special handling of browser version targets. A version -1 means that the specific version - # is not supported at all. Replace those with INT32_MAX to make it possible to compare e.g. - # #if MIN_FIREFOX_VERSION < 68 - if re.match(r'MIN_.*_VERSION(=.*)?', key): - try: - if int(key.split('=')[1]) < 0: - key = key.split('=')[0] + '=0x7FFFFFFF' - except Exception: - pass - - settings_changes.append(key) - - newargs = [a for a in args if a] - return (settings_changes, newargs) - - -def get_target_flags(): - return ['-target', shared.get_llvm_target()] - - -def get_clang_flags(user_args): - flags = get_target_flags() - - # if exception catching is disabled, we can prevent that code from being - # generated in the frontend - if settings.DISABLE_EXCEPTION_CATCHING and not settings.WASM_EXCEPTIONS: - flags.append('-fignore-exceptions') - - if settings.INLINING_LIMIT: - flags.append('-fno-inline-functions') - - if settings.RELOCATABLE and '-fPIC' not in user_args: - flags.append('-fPIC') - - if settings.RELOCATABLE or settings.LINKABLE or '-fPIC' in user_args: - if not any(a.startswith('-fvisibility') for a in user_args): - # For relocatable code we default to visibility=default in emscripten even - # though the upstream backend defaults visibility=hidden. This matches the - # expectations of C/C++ code in the wild which expects undecorated symbols - # to be exported to other DSO's by default. - flags.append('-fvisibility=default') - - if settings.LTO: - if not any(a.startswith('-flto') for a in user_args): - flags.append('-flto=' + settings.LTO) - # setjmp/longjmp handling using Wasm EH - # For non-LTO, '-mllvm -wasm-enable-eh' added in - # building.llvm_backend_args() sets this feature in clang. But in LTO, the - # argument is added to wasm-ld instead, so clang needs to know that EH is - # enabled so that it can be added to the attributes in LLVM IR. - if settings.SUPPORT_LONGJMP == 'wasm': - flags.append('-mexception-handling') - - else: - # In LTO mode these args get passed instead at link time when the backend runs. - for a in building.llvm_backend_args(): - flags += ['-mllvm', a] - - return flags - - -cflags = None - - -def get_cflags(user_args, is_cxx): - global cflags - if cflags: - return cflags - - # Flags we pass to the compiler when building C/C++ code - # We add these to the user's flags (newargs), but not when building .s or .S assembly files - cflags = get_clang_flags(user_args) - cflags.append('--sysroot=' + cache.get_sysroot(absolute=True)) - - if settings.EMSCRIPTEN_TRACING: - cflags.append('-D__EMSCRIPTEN_TRACING__=1') - - if settings.SHARED_MEMORY: - cflags.append('-D__EMSCRIPTEN_SHARED_MEMORY__=1') - - if settings.WASM_WORKERS: - cflags.append('-D__EMSCRIPTEN_WASM_WORKERS__=1') - - if not settings.STRICT: - # The preprocessor define EMSCRIPTEN is deprecated. Don't pass it to code - # in strict mode. Code should use the define __EMSCRIPTEN__ instead. - cflags.append('-DEMSCRIPTEN') - - # Changes to default clang behavior - - # Implicit functions can cause horribly confusing function pointer type errors, see #2175 - # If your codebase really needs them - very unrecommended! - you can disable the error with - # -Wno-error=implicit-function-declaration - # or disable even a warning about it with - # -Wno-implicit-function-declaration - # This is already an error in C++ so we don't need to inject extra flags. - if not is_cxx: - cflags += ['-Werror=implicit-function-declaration'] - - ports.add_cflags(cflags, settings) - - def array_contains_any_of(hay, needles): - for n in needles: - if n in hay: - return True - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER) or array_contains_any_of(user_args, SIMD_NEON_FLAGS): - if '-msimd128' not in user_args and '-mrelaxed-simd' not in user_args: - exit_with_error('passing any of ' + ', '.join(SIMD_INTEL_FEATURE_TOWER + SIMD_NEON_FLAGS) + ' flags also requires passing -msimd128 (or -mrelaxed-simd)!') - cflags += ['-D__SSE__=1'] - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[1:]): - cflags += ['-D__SSE2__=1'] - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[2:]): - cflags += ['-D__SSE3__=1'] - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[3:]): - cflags += ['-D__SSSE3__=1'] - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[4:]): - cflags += ['-D__SSE4_1__=1'] - - # Handle both -msse4.2 and its alias -msse4. - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[5:]): - cflags += ['-D__SSE4_2__=1'] - - if array_contains_any_of(user_args, SIMD_INTEL_FEATURE_TOWER[7:]): - cflags += ['-D__AVX__=1'] - - if array_contains_any_of(user_args, SIMD_NEON_FLAGS): - cflags += ['-D__ARM_NEON__=1'] - - if '-nostdinc' not in user_args: - if not settings.USE_SDL: - cflags += ['-Xclang', '-iwithsysroot' + os.path.join('/include', 'fakesdl')] - cflags += ['-Xclang', '-iwithsysroot' + os.path.join('/include', 'compat')] - - return cflags - - -def get_library_basename(filename): - """Similar to get_file_suffix this strips off all numeric suffixes and then - then final non-numeric one. For example for 'libz.so.1.2.8' returns 'libz'""" - filename = os.path.basename(filename) - while filename: - filename, suffix = os.path.splitext(filename) - # Keep stipping suffixes until we strip a non-numeric one. - if not suffix[1:].isdigit(): - return filename - - # # Main run() function # @@ -512,17 +181,16 @@ def run(args): # want that to break for users of EMCC_CFLAGS. if len(args) == 2 and args[1] == '-v': # autoconf likes to see 'GNU' in the output to enable shared object support - print(version_string(), file=sys.stderr) - return shared.check_call([clang, '-v'] + get_target_flags(), check=False).returncode + print(cmdline.version_string(), file=sys.stderr) + return shared.check_call([clang, '-v'] + compile.get_target_flags(), check=False).returncode # Additional compiler flags that we treat as if they were passed to us on the # commandline - EMCC_CFLAGS = os.environ.get('EMCC_CFLAGS') - if EMCC_CFLAGS: + if EMCC_CFLAGS := os.environ.get('EMCC_CFLAGS'): args += shlex.split(EMCC_CFLAGS) if DEBUG: - logger.warning(f'invocation: {shared.shlex_join(args)} (in {os.getcwd()})') + logger.warning(f'invocation: {shlex.join(args)} (in {os.getcwd()})') # Strip args[0] (program name) args = args[1:] @@ -532,7 +200,7 @@ def run(args): # read response files very early on try: args = substitute_response_files(args) - except IOError as e: + except OSError as e: exit_with_error(e) if '--help' in args: @@ -554,22 +222,24 @@ def run(args): ## Process argument and setup the compiler state = EmccState(args) - options, newargs = phase_parse_arguments(state) + options, newargs = cmdline.parse_arguments(state.orig_args) if not shared.SKIP_SUBPROCS: shared.check_sanity() + # Begin early-exit flag handling. + if '--version' in args: - print(version_string()) + print(cmdline.version_string()) print('''\ -Copyright (C) 2014 the Emscripten authors (see AUTHORS.txt) +Copyright (C) 2025 the Emscripten authors (see AUTHORS.txt) This is free and open source software under the MIT license. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ''') return 0 if '-dumpversion' in args: # gcc's doc states "Print the compiler version [...] and don't do anything else." - print(shared.EMSCRIPTEN_VERSION) + print(utils.EMSCRIPTEN_VERSION) return 0 if '--cflags' in args: @@ -590,48 +260,29 @@ def run(args): exit_with_error(f'unable to parse output of `{cmd}`:\n{proc.stderr}') parts = shlex.split(lines[0].replace('\\', '\\\\')) parts = [x for x in parts if x not in ['-c', '-o', '-v', '-emit-llvm'] and input_file not in x and temp_target not in x] - print(shared.shlex_join(parts[1:])) + print(shlex.join(parts[1:])) return 0 - if 'EMMAKEN_NO_SDK' in os.environ: - exit_with_error('EMMAKEN_NO_SDK is no longer supported. The standard -nostdlib and -nostdinc flags should be used instead') - - if 'EMMAKEN_COMPILER' in os.environ: - exit_with_error('`EMMAKEN_COMPILER` is no longer supported.\n' + - 'Please use the `LLVM_ROOT` and/or `COMPILER_WRAPPER` config settings instead') - - if 'EMMAKEN_CFLAGS' in os.environ: - exit_with_error('`EMMAKEN_CFLAGS` is no longer supported, please use `EMCC_CFLAGS` instead') - - if 'EMCC_REPRODUCE' in os.environ: - options.reproduce = os.environ['EMCC_REPRODUCE'] - - # For internal consistency, ensure we don't attempt or read or write any link time - # settings until we reach the linking phase. - settings.limit_settings(COMPILE_TIME_SETTINGS) - - newargs, input_files = phase_setup(options, state, newargs) - - if '-dumpmachine' in newargs or '-print-target-triple' in newargs or '--print-target-triple' in newargs: + if '-dumpmachine' in args or '-print-target-triple' in args or '--print-target-triple' in args: print(shared.get_llvm_target()) return 0 - if '-print-search-dirs' in newargs or '--print-search-dirs' in newargs: + if '-print-search-dirs' in args or '--print-search-dirs' in args: print(f'programs: ={config.LLVM_ROOT}') print(f'libraries: ={cache.get_lib_dir(absolute=True)}') return 0 - if '-print-resource-dir' in newargs: - shared.check_call([clang] + newargs) + if '-print-resource-dir' in args: + shared.check_call([clang] + args) return 0 - if '-print-libgcc-file-name' in newargs or '--print-libgcc-file-name' in newargs: + if '-print-libgcc-file-name' in args or '--print-libgcc-file-name' in args: settings.limit_settings(None) compiler_rt = system_libs.Library.get_usable_variations()['libcompiler_rt'] print(compiler_rt.get_path(absolute=True)) return 0 - print_file_name = [a for a in newargs if a.startswith('-print-file-name=') or a.startswith('--print-file-name=')] + print_file_name = [a for a in args if a.startswith(('-print-file-name=', '--print-file-name='))] if print_file_name: libname = print_file_name[-1].split('=')[1] system_libpath = cache.get_lib_dir(absolute=True) @@ -642,185 +293,123 @@ def run(args): print(libname) return 0 + # End early-exit flag handling + + if 'EMMAKEN_NO_SDK' in os.environ: + exit_with_error('EMMAKEN_NO_SDK is no longer supported. The standard -nostdlib and -nostdinc flags should be used instead') + + if 'EMMAKEN_COMPILER' in os.environ: + exit_with_error('`EMMAKEN_COMPILER` is no longer supported.\n' + + 'Please use the `LLVM_ROOT` and/or `COMPILER_WRAPPER` config settings instead') + + if 'EMMAKEN_CFLAGS' in os.environ: + exit_with_error('`EMMAKEN_CFLAGS` is no longer supported, please use `EMCC_CFLAGS` instead') + + if 'EMCC_REPRODUCE' in os.environ: + options.reproduce = os.environ['EMCC_REPRODUCE'] + + # For internal consistency, ensure we don't attempt or read or write any link time + # settings until we reach the linking phase. + settings.limit_settings(COMPILE_TIME_SETTINGS) + + phase_setup(options, state) + if options.reproduce: create_reproduce_file(options.reproduce, args) if state.mode == Mode.POST_LINK_ONLY: - if len(input_files) != 1: + if len(options.input_files) != 1: exit_with_error('--post-link requires a single input file') + linker_args = separate_linker_flags(newargs)[1] + linker_args = [f.value for f in linker_args] # Delay import of link.py to avoid processing this file when only compiling from tools import link - link.run_post_link(input_files[0][1], options, state, newargs) + link.run_post_link(options.input_files[0], options, linker_args) return 0 - ## Compile source code to object files - linker_inputs = phase_compile_inputs(options, state, newargs, input_files) + # Compile source code to object files + # When only compiling this function never returns. + linker_args = phase_compile_inputs(options, state, newargs) if state.mode == Mode.COMPILE_AND_LINK: # Delay import of link.py to avoid processing this file when only compiling from tools import link - return link.run(linker_inputs, options, state, newargs) + return link.run(options, linker_args) else: logger.debug('stopping after compile phase') return 0 -def normalize_boolean_setting(name, value): - # boolean NO_X settings are aliases for X - # (note that *non*-boolean setting values have special meanings, - # and we can't just flip them, so leave them as-is to be - # handled in a special way later) - if name.startswith('NO_') and value in ('0', '1'): - name = removeprefix(name, 'NO_') - value = str(1 - int(value)) - return name, value +def separate_linker_flags(newargs): + """Process argument list separating out compiler args and linker args. - -@ToolchainProfiler.profile_block('parse arguments') -def phase_parse_arguments(state): - """The first phase of the compiler. Parse command line argument and - populate settings. - """ - newargs = list(state.orig_args) - - # Scan and strip emscripten specific cmdline warning flags. - # This needs to run before other cmdline flags have been parsed, so that - # warnings are properly printed during arg parse. - newargs = diagnostics.capture_warnings(newargs) - - if not diagnostics.is_enabled('deprecated'): - settings.WARN_DEPRECATED = 0 - - for i in range(len(newargs)): - if newargs[i] in ('-l', '-L', '-I', '-z'): - # Scan for flags that can be written as either one or two arguments - # and normalize them to the single argument form. - newargs[i] += newargs[i + 1] - newargs[i + 1] = '' - - options, settings_changes, user_js_defines, newargs = parse_args(newargs) - - if options.post_link or options.oformat == OFormat.BARE: - diagnostics.warning('experimental', '--oformat=bare/--post-link are experimental and subject to change.') - - explicit_settings_changes, newargs = parse_s_args(newargs) - settings_changes += explicit_settings_changes - - for s in settings_changes: - key, value = s.split('=', 1) - key, value = normalize_boolean_setting(key, value) - user_settings[key] = value - - # STRICT is used when applying settings so it needs to be applied first before - # calling `apply_user_settings`. - strict_cmdline = user_settings.get('STRICT') - if strict_cmdline: - settings.STRICT = int(strict_cmdline) - - # Apply user -jsD settings - for s in user_js_defines: - settings[s[0]] = s[1] - - # Apply -s settings in newargs here (after optimization levels, so they can override them) - apply_user_settings() - - return options, newargs - - -@ToolchainProfiler.profile_block('setup') -def phase_setup(options, state, newargs): - """Second phase: configure and setup the compiler based on the specified settings and arguments. + - Linker flags include input files and are returned a list of LinkFlag objects. + - Compiler flags are those to be passed to `clang -c`. """ if settings.RUNTIME_LINKED_LIBS: newargs += settings.RUNTIME_LINKED_LIBS - # Find input files + compiler_args = [] + linker_args = [] - # These three arrays are used to store arguments of different types for - # type-specific processing. In order to shuffle the arguments back together - # after processing, all of these arrays hold tuples (original_index, value). - # Note that the index part of the tuple can have a fractional part for input - # arguments that expand into multiple processed arguments, as in -Wl,-f1,-f2. - input_files = [] + def add_link_arg(flag, is_file=False): + linker_args.append(LinkFlag(flag, is_file)) - # find input files with a simple heuristic. we should really analyze - # based on a full understanding of gcc params, right now we just assume that - # what is left contains no more |-x OPT| things skip = False - has_header_inputs = False for i in range(len(newargs)): if skip: skip = False continue arg = newargs[i] - if arg in {'-MT', '-MF', '-MJ', '-MQ', '-D', '-U', '-o', '-x', - '-Xpreprocessor', '-include', '-imacros', '-idirafter', - '-iprefix', '-iwithprefix', '-iwithprefixbefore', - '-isysroot', '-imultilib', '-A', '-isystem', '-iquote', - '-install_name', '-compatibility_version', - '-current_version', '-I', '-L', '-include-pch', - '-undefined', '-target', - '-Xlinker', '-Xclang', '-z'}: + if arg in CLANG_FLAGS_WITH_ARGS: skip = True - if not arg.startswith('-'): - # we already removed -o , so all these should be inputs - newargs[i] = '' + def get_next_arg(): + if len(newargs) <= i + 1: + exit_with_error(f"option '{arg}' requires an argument") + return newargs[i + 1] + + if not arg.startswith('-') or arg == '-': # os.devnul should always be reported as existing but there is bug in windows # python before 3.8: # https://bugs.python.org/issue1311 - if not os.path.exists(arg) and arg != os.devnull: + if not os.path.exists(arg) and arg not in (os.devnull, '-'): exit_with_error('%s: No such file or directory ("%s" was expected to be an input file, based on the commandline arguments provided)', arg, arg) - file_suffix = get_file_suffix(arg) - if file_suffix in HEADER_ENDINGS: - has_header_inputs = True - input_files.append((i, arg)) - elif arg.startswith('-L'): - state.add_link_flag(i, arg) - elif arg.startswith('-l'): - state.add_link_flag(i, arg) + add_link_arg(arg, True) elif arg == '-z': - state.add_link_flag(i, newargs[i]) - state.add_link_flag(i + 1, newargs[i + 1]) - elif arg.startswith('-z'): - state.add_link_flag(i, newargs[i]) + add_link_arg(arg) + add_link_arg(get_next_arg()) elif arg.startswith('-Wl,'): - # Multiple comma separated link flags can be specified. Create fake - # fractional indices for these: -Wl,a,b,c,d at index 4 becomes: - # (4, a), (4.25, b), (4.5, c), (4.75, d) - link_flags_to_add = arg.split(',')[1:] - for flag_index, flag in enumerate(link_flags_to_add): - state.add_link_flag(i + float(flag_index) / len(link_flags_to_add), flag) + for flag in arg.split(',')[1:]: + add_link_arg(flag) elif arg == '-Xlinker': - state.add_link_flag(i + 1, newargs[i + 1]) - elif arg == '-s': - state.add_link_flag(i, newargs[i]) - elif arg == '-': - input_files.append((i, arg)) - newargs[i] = '' + add_link_arg(get_next_arg()) + elif arg == '-s' or arg.startswith(('-l', '-L', '--js-library=', '-z', '-u')): + add_link_arg(arg) + elif not arg.startswith('-o') and arg not in ('-nostdlib', '-nostartfiles', '-nolibc', '-nodefaultlibs', '-s'): + # All other flags are for the compiler + compiler_args.append(arg) + if skip: + compiler_args.append(get_next_arg()) + + return compiler_args, linker_args - newargs = [a for a in newargs if a] - # SSEx is implemented on top of SIMD128 instruction set, but do not pass SSE flags to LLVM - # so it won't think about generating native x86 SSE code. - newargs = [x for x in newargs if x not in SIMD_INTEL_FEATURE_TOWER and x not in SIMD_NEON_FLAGS] +@ToolchainProfiler.profile_block('setup') +def phase_setup(options, state): + """Second phase: configure and setup the compiler based on the specified settings and arguments. + """ - state.has_dash_c = '-c' in newargs or '--precompile' in newargs - state.has_dash_S = '-S' in newargs - state.has_dash_E = '-E' in newargs + has_header_inputs = any(get_file_suffix(f) in HEADER_EXTENSIONS for f in options.input_files) if options.post_link: state.mode = Mode.POST_LINK_ONLY - elif state.has_dash_E or '-M' in newargs or '-MM' in newargs or '-fsyntax-only' in newargs: - state.mode = Mode.PREPROCESS_ONLY - elif has_header_inputs: - state.mode = Mode.PCH - elif state.has_dash_c or state.has_dash_S: + elif has_header_inputs or options.dash_c or options.dash_S or options.syntax_only or options.dash_E or options.dash_M: state.mode = Mode.COMPILE_ONLY - if state.mode in (Mode.COMPILE_ONLY, Mode.PREPROCESS_ONLY): + if state.mode == Mode.COMPILE_ONLY: for key in user_settings: if key not in COMPILE_TIME_SETTINGS: diagnostics.warning( @@ -842,17 +431,6 @@ def phase_setup(options, state, newargs): if settings.PTHREADS or settings.WASM_WORKERS: settings.SHARED_MEMORY = 1 - if settings.PTHREADS and '-pthread' not in newargs: - newargs += ['-pthread'] - elif settings.SHARED_MEMORY: - if '-matomics' not in newargs: - newargs += ['-matomics'] - if '-mbulk-memory' not in newargs: - newargs += ['-mbulk-memory'] - - if settings.SHARED_MEMORY: - settings.BULK_MEMORY = 1 - if 'DISABLE_EXCEPTION_CATCHING' in user_settings and 'EXCEPTION_CATCHING_ALLOWED' in user_settings: # If we get here then the user specified both DISABLE_EXCEPTION_CATCHING and EXCEPTION_CATCHING_ALLOWED # on the command line. This is no longer valid so report either an error or a warning (for @@ -889,10 +467,8 @@ def phase_setup(options, state, newargs): if options.target.startswith('wasm64'): default_setting('MEMORY64', 1) - if settings.MEMORY64: - if options.target.startswith('wasm32'): - exit_with_error('wasm32 target is not compatible with -sMEMORY64') - diagnostics.warning('experimental', '-sMEMORY64 is still experimental. Many features may not work.') + if settings.MEMORY64 and options.target.startswith('wasm32'): + exit_with_error('wasm32 target is not compatible with -sMEMORY64') # Wasm SjLj cannot be used with Emscripten EH if settings.SUPPORT_LONGJMP == 'wasm': @@ -921,24 +497,9 @@ def phase_setup(options, state, newargs): if settings.USE_SDL == 2 or settings.USE_SDL_MIXER == 2 or settings.USE_SDL_GFX == 2: default_setting('GL_ENABLE_GET_PROC_ADDRESS', 1) - return (newargs, input_files) - - -def get_clang_output_extension(state): - if '-emit-llvm' in state.orig_args: - if state.has_dash_S: - return '.ll' - else: - return '.bc' - - if state.has_dash_S: - return '.s' - else: - return '.o' - @ToolchainProfiler.profile_block('compile inputs') -def phase_compile_inputs(options, state, newargs, input_files): +def phase_compile_inputs(options, state, newargs): if shared.run_via_emxx: compiler = [shared.CLANG_CXX] else: @@ -948,110 +509,68 @@ def phase_compile_inputs(options, state, newargs, input_files): logger.debug('using compiler wrapper: %s', config.COMPILER_WRAPPER) compiler.insert(0, config.COMPILER_WRAPPER) - compile_args = newargs system_libs.ensure_sysroot() - def get_language_mode(args): - return_next = False - for item in args: - if return_next: - return item - if item == '-x': - return_next = True - continue - if item.startswith('-x'): - return removeprefix(item, '-x') - return '' - - language_mode = get_language_mode(newargs) - use_cxx = 'c++' in language_mode or shared.run_via_emxx - def get_clang_command(): - return compiler + get_cflags(state.orig_args, use_cxx) + compile_args + return compiler + compile.get_cflags(state.orig_args) def get_clang_command_preprocessed(): - return compiler + get_clang_flags(state.orig_args) + compile_args + return compiler + compile.get_clang_flags(state.orig_args) def get_clang_command_asm(): - return compiler + get_target_flags() + compile_args - - # preprocessor-only (-E) support - if state.mode == Mode.PREPROCESS_ONLY: - inputs = [i[1] for i in input_files] - cmd = get_clang_command() + inputs - if options.output_file: - cmd += ['-o', options.output_file] - # Do not compile, but just output the result from preprocessing stage or - # output the dependency rule. Warning: clang and gcc behave differently - # with -MF! (clang seems to not recognize it) - logger.debug(('just preprocessor ' if state.has_dash_E else 'just dependencies: ') + ' '.join(cmd)) - shared.exec_process(cmd) - assert False, 'exec_process does not return' - - # Precompiled headers support - if state.mode == Mode.PCH: - inputs = [i[1] for i in input_files] - for header in inputs: - if not shared.suffix(header) in HEADER_ENDINGS: - exit_with_error(f'cannot mix precompiled headers with non-header inputs: {inputs} : {header}') - cmd = get_clang_command() + inputs - if options.output_file: - cmd += ['-o', options.output_file] - logger.debug(f"running (for precompiled headers): {cmd[0]} {' '.join(cmd[1:])}") - shared.exec_process(cmd) - assert False, 'exec_process does not return' + return compiler + compile.get_target_flags() if state.mode == Mode.COMPILE_ONLY: - inputs = [i[1] for i in input_files] - if all(get_file_suffix(i) in ASSEMBLY_ENDINGS for i in inputs): - cmd = get_clang_command_asm() + inputs + if options.output_file and get_file_suffix(options.output_file) == '.bc' and not settings.LTO and '-emit-llvm' not in state.orig_args: + diagnostics.warning('emcc', '.bc output file suffix used without -flto or -emit-llvm. Consider using .o extension since emcc will output an object file, not a bitcode file') + if all(get_file_suffix(i) in ASSEMBLY_EXTENSIONS for i in options.input_files): + cmd = get_clang_command_asm() + newargs else: - cmd = get_clang_command() + inputs - if options.output_file: - cmd += ['-o', options.output_file] - if get_file_suffix(options.output_file) == '.bc' and not settings.LTO and '-emit-llvm' not in state.orig_args: - diagnostics.warning('emcc', '.bc output file suffix used without -flto or -emit-llvm. Consider using .o extension since emcc will output an object file, not a bitcode file') + cmd = get_clang_command() + newargs shared.exec_process(cmd) assert False, 'exec_process does not return' # In COMPILE_AND_LINK we need to compile source files too, but we also need to # filter out the link flags + assert state.mode == Mode.COMPILE_AND_LINK + assert not options.dash_c + compile_args, linker_args = separate_linker_flags(newargs) - def is_link_flag(flag): - if flag in ('-nostdlib', '-nostartfiles', '-nolibc', '-nodefaultlibs', '-s'): - return True - return flag.startswith(('-l', '-L', '-Wl,', '-z')) - - compile_args = [a for a in compile_args if a and not is_link_flag(a)] - linker_inputs = [] + # Map of file basenames to how many times we've seen them. We use this to generate + # unique `_NN` suffix for object files in cases when we are compiling multiple soures that + # have the same basename. e.g. `foo/utils.c` and `bar/utils.c` on the same command line. seen_names = {} def uniquename(name): if name not in seen_names: - seen_names[name] = str(len(seen_names)) - return unsuffixed(name) + '_' + seen_names[name] + shared.suffix(name) + # No suffix needed the firt time we see given name. + seen_names[name] = 1 + return name + + unique_suffix = '_%d' % seen_names[name] + seen_names[name] += 1 + base, ext = os.path.splitext(name) + return base + unique_suffix + ext def get_object_filename(input_file): - return in_temp(shared.replace_suffix(uniquename(input_file), '.o')) + objfile = unsuffixed_basename(input_file) + '.o' + return in_temp(uniquename(objfile)) - def compile_source_file(i, input_file): + def compile_source_file(input_file): logger.debug(f'compiling source file: {input_file}') output_file = get_object_filename(input_file) - linker_inputs.append((i, output_file)) - if get_file_suffix(input_file) in ASSEMBLY_ENDINGS: + ext = get_file_suffix(input_file) + if ext in ASSEMBLY_EXTENSIONS: cmd = get_clang_command_asm() - elif get_file_suffix(input_file) in PREPROCESSED_ENDINGS: + elif ext in PREPROCESSED_EXTENSIONS: cmd = get_clang_command_preprocessed() else: cmd = get_clang_command() - if get_file_suffix(input_file) in ['.pcm']: + if ext == '.pcm': cmd = [c for c in cmd if not c.startswith('-fprebuilt-module-path=')] - cmd += [input_file] - if not state.has_dash_c: - cmd += ['-c'] - cmd += ['-o', output_file] - if state.mode == Mode.COMPILE_AND_LINK and '-gsplit-dwarf' in newargs: - # When running in COMPILE_AND_LINK mode we compile to temporary location + cmd += compile_args + ['-c', input_file, '-o', output_file] + if options.requested_debug == '-gsplit-dwarf': + # When running in COMPILE_AND_LINK mode we compile objects to a temporary location # but we want the `.dwo` file to be generated in the current working directory, # like it is under clang. We could avoid this hack if we use the clang driver # to generate the temporary files, but that would also involve using the clang @@ -1063,539 +582,29 @@ def compile_source_file(i, input_file): assert os.path.exists(output_file) if options.save_temps: shutil.copyfile(output_file, shared.unsuffixed_basename(input_file) + '.o') + return output_file - # First, generate LLVM bitcode. For each input file, we get base.o with bitcode - for i, input_file in input_files: + # Compile input files individually to temporary locations. + for arg in linker_args: + if not arg.is_file: + continue + input_file = arg.value file_suffix = get_file_suffix(input_file) - if file_suffix in SOURCE_ENDINGS + ASSEMBLY_ENDINGS or (state.has_dash_c and file_suffix == '.bc'): - compile_source_file(i, input_file) - elif file_suffix in DYNAMICLIB_ENDINGS: + if file_suffix in SOURCE_EXTENSIONS | ASSEMBLY_EXTENSIONS or (options.dash_c and file_suffix == '.bc'): + arg.value = compile_source_file(input_file) + elif file_suffix in DYLIB_EXTENSIONS: logger.debug(f'using shared library: {input_file}') - linker_inputs.append((i, input_file)) elif building.is_ar(input_file): logger.debug(f'using static library: {input_file}') - linker_inputs.append((i, input_file)) - elif language_mode: - compile_source_file(i, input_file) + elif options.input_language: + arg.value = compile_source_file(input_file) elif input_file == '-': exit_with_error('-E or -x required when input is from standard input') else: # Default to assuming the inputs are object files and pass them to the linker - logger.debug(f'using object file: {input_file}') - linker_inputs.append((i, input_file)) - - return linker_inputs - - -def version_string(): - # if the emscripten folder is not a git repo, don't run git show - that can - # look up and find the revision in a parent directory that is a git repo - revision_suffix = '' - if os.path.exists(utils.path_from_root('.git')): - git_rev = run_process( - ['git', 'rev-parse', 'HEAD'], - stdout=PIPE, stderr=PIPE, cwd=utils.path_from_root()).stdout.strip() - revision_suffix = ' (%s)' % git_rev - elif os.path.exists(utils.path_from_root('emscripten-revision.txt')): - rev = read_file(utils.path_from_root('emscripten-revision.txt')).strip() - revision_suffix = ' (%s)' % rev - return f'emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) {shared.EMSCRIPTEN_VERSION}{revision_suffix}' - - -def parse_args(newargs): - options = EmccOptions() - settings_changes = [] - user_js_defines = [] - should_exit = False - skip = False - - for i in range(len(newargs)): - if skip: - skip = False - continue - - # Support legacy '--bind' flag, by mapping to `-lembind` which now - # has the same effect - if newargs[i] == '--bind': - newargs[i] = '-lembind' - - arg = newargs[i] - arg_value = None - - def check_flag(value): - # Check for and consume a flag - if arg == value: - newargs[i] = '' - return True - return False - - def check_arg(name): - nonlocal arg_value - if arg.startswith(name) and '=' in arg: - arg_value = arg.split('=', 1)[1] - newargs[i] = '' - return True - if arg == name: - if len(newargs) <= i + 1: - exit_with_error("option '%s' requires an argument" % arg) - arg_value = newargs[i + 1] - newargs[i] = '' - newargs[i + 1] = '' - return True - return False - - def consume_arg(): - nonlocal arg_value - assert arg_value is not None - rtn = arg_value - arg_value = None - return rtn - - def consume_arg_file(): - name = consume_arg() - if not os.path.isfile(name): - exit_with_error("'%s': file not found: '%s'" % (arg, name)) - return name - - if arg.startswith('-O'): - # Let -O default to -O2, which is what gcc does. - requested_level = removeprefix(arg, '-O') or '2' - if requested_level == 's': - requested_level = 2 - settings.SHRINK_LEVEL = 1 - elif requested_level == 'z': - requested_level = 2 - settings.SHRINK_LEVEL = 2 - elif requested_level == 'g': - requested_level = 1 - settings.SHRINK_LEVEL = 0 - settings.DEBUG_LEVEL = max(settings.DEBUG_LEVEL, 1) - elif requested_level == 'fast': - # TODO(https://github.com/emscripten-core/emscripten/issues/21497): - # If we ever map `-ffast-math` to `wasm-opt --fast-math` then - # then we should enable that too here. - requested_level = 3 - settings.SHRINK_LEVEL = 0 - else: - settings.SHRINK_LEVEL = 0 - settings.OPT_LEVEL = validate_arg_level(requested_level, 3, 'invalid optimization level: ' + arg, clamp=True) - elif check_arg('--js-opts'): - logger.warning('--js-opts ignored when using llvm backend') - consume_arg() - elif check_arg('--llvm-opts'): - diagnostics.warning('deprecated', '--llvm-opts is deprecated. All non-emcc args are passed through to clang.') - elif arg.startswith('-flto'): - if '=' in arg: - settings.LTO = arg.split('=')[1] - else: - settings.LTO = 'full' - elif arg == "-fno-lto": - settings.LTO = 0 - elif arg == "--save-temps": - options.save_temps = True - elif check_arg('--llvm-lto'): - logger.warning('--llvm-lto ignored when using llvm backend') - consume_arg() - elif check_arg('--closure-args'): - args = consume_arg() - options.closure_args += shlex.split(args) - elif check_arg('--closure'): - options.use_closure_compiler = int(consume_arg()) - elif check_arg('--js-transform'): - options.js_transform = consume_arg() - elif check_arg('--reproduce'): - options.reproduce = consume_arg() - elif check_arg('--pre-js'): - options.pre_js.append(consume_arg_file()) - elif check_arg('--post-js'): - options.post_js.append(consume_arg_file()) - elif check_arg('--extern-pre-js'): - options.extern_pre_js.append(consume_arg_file()) - elif check_arg('--extern-post-js'): - options.extern_post_js.append(consume_arg_file()) - elif check_arg('--compiler-wrapper'): - config.COMPILER_WRAPPER = consume_arg() - elif check_flag('--post-link'): - options.post_link = True - elif check_arg('--oformat'): - formats = [f.lower() for f in OFormat.__members__] - fmt = consume_arg() - if fmt not in formats: - exit_with_error('invalid output format: `%s` (must be one of %s)' % (fmt, formats)) - options.oformat = getattr(OFormat, fmt.upper()) - elif check_arg('--minify'): - arg = consume_arg() - if arg != '0': - exit_with_error('0 is the only supported option for --minify; 1 has been deprecated') - options.no_minify = True - elif arg.startswith('-g'): - options.requested_debug = arg - requested_level = removeprefix(arg, '-g') or '3' - if is_int(requested_level): - # the -gX value is the debug level (-g1, -g2, etc.) - settings.DEBUG_LEVEL = validate_arg_level(requested_level, 4, 'invalid debug level: ' + arg) - # if we don't need to preserve LLVM debug info, do not keep this flag - # for clang - if settings.DEBUG_LEVEL < 3: - newargs[i] = '-g0' - else: - # for 3+, report -g3 to clang as -g4 etc. are not accepted - newargs[i] = '-g3' - if settings.DEBUG_LEVEL == 3: - settings.GENERATE_DWARF = 1 - if settings.DEBUG_LEVEL == 4: - settings.GENERATE_SOURCE_MAP = 1 - diagnostics.warning('deprecated', 'please replace -g4 with -gsource-map') - else: - if requested_level.startswith('force_dwarf'): - exit_with_error('gforce_dwarf was a temporary option and is no longer necessary (use -g)') - elif requested_level.startswith('separate-dwarf'): - # emit full DWARF but also emit it in a file on the side - newargs[i] = '-g' - # if a file is provided, use that; otherwise use the default location - # (note that we do not know the default location until all args have - # been parsed, so just note True for now). - if requested_level != 'separate-dwarf': - if not requested_level.startswith('separate-dwarf=') or requested_level.count('=') != 1: - exit_with_error('invalid -gseparate-dwarf=FILENAME notation') - settings.SEPARATE_DWARF = requested_level.split('=')[1] - else: - settings.SEPARATE_DWARF = True - settings.GENERATE_DWARF = 1 - elif requested_level == 'source-map': - settings.GENERATE_SOURCE_MAP = 1 - newargs[i] = '-g' - else: - # Other non-integer levels (e.g. -gline-tables-only or -gdwarf-5) are - # usually clang flags that emit DWARF. So we pass them through to - # clang and make the emscripten code treat it like any other DWARF. - settings.GENERATE_DWARF = 1 - # In all cases set the emscripten debug level to 3 so that we do not - # strip during link (during compile, this does not make a difference). - settings.DEBUG_LEVEL = 3 - elif check_flag('-profiling') or check_flag('--profiling'): - settings.DEBUG_LEVEL = max(settings.DEBUG_LEVEL, 2) - elif check_flag('-profiling-funcs') or check_flag('--profiling-funcs'): - settings.EMIT_NAME_SECTION = 1 - elif newargs[i] == '--tracing' or newargs[i] == '--memoryprofiler': - if newargs[i] == '--memoryprofiler': - options.memory_profiler = True - newargs[i] = '' - settings_changes.append('EMSCRIPTEN_TRACING=1') - settings.JS_LIBRARIES.append((0, 'library_trace.js')) - elif check_flag('--emit-symbol-map'): - options.emit_symbol_map = True - settings.EMIT_SYMBOL_MAP = 1 - elif check_arg('--embed-file'): - options.embed_files.append(consume_arg()) - elif check_arg('--preload-file'): - options.preload_files.append(consume_arg()) - elif check_arg('--exclude-file'): - options.exclude_files.append(consume_arg()) - elif check_flag('--use-preload-cache'): - options.use_preload_cache = True - elif check_flag('--no-heap-copy'): - diagnostics.warning('legacy-settings', 'ignoring legacy flag --no-heap-copy (that is the only mode supported now)') - elif check_flag('--use-preload-plugins'): - options.use_preload_plugins = True - elif check_flag('--ignore-dynamic-linking'): - options.ignore_dynamic_linking = True - elif arg == '-v': - shared.PRINT_SUBPROCS = True - elif arg == '-###': - shared.SKIP_SUBPROCS = True - elif check_arg('--shell-file'): - options.shell_path = consume_arg_file() - elif check_arg('--source-map-base'): - options.source_map_base = consume_arg() - elif check_arg('--embind-emit-tsd'): - diagnostics.warning('deprecated', '--embind-emit-tsd is deprecated. Use --emit-tsd instead.') - options.emit_tsd = consume_arg() - elif check_arg('--emit-tsd'): - options.emit_tsd = consume_arg() - elif check_flag('--no-entry'): - options.no_entry = True - elif check_arg('--js-library'): - settings.JS_LIBRARIES.append((i + 1, os.path.abspath(consume_arg_file()))) - elif check_flag('--remove-duplicates'): - diagnostics.warning('legacy-settings', '--remove-duplicates is deprecated as it is no longer needed. If you cannot link without it, file a bug with a testcase') - elif check_flag('--jcache'): - logger.error('jcache is no longer supported') - elif check_arg('--cache'): - config.CACHE = os.path.abspath(consume_arg()) - cache.setup() - # Ensure child processes share the same cache (e.g. when using emcc to compiler system - # libraries) - os.environ['EM_CACHE'] = config.CACHE - elif check_flag('--clear-cache'): - logger.info('clearing cache as requested by --clear-cache: `%s`', cache.cachedir) - cache.erase() - shared.perform_sanity_checks() # this is a good time for a sanity check - should_exit = True - elif check_flag('--clear-ports'): - logger.info('clearing ports and cache as requested by --clear-ports') - ports.clear() - cache.erase() - shared.perform_sanity_checks() # this is a good time for a sanity check - should_exit = True - elif check_flag('--check'): - print(version_string(), file=sys.stderr) - shared.check_sanity(force=True) - should_exit = True - elif check_flag('--show-ports'): - ports.show_ports() - should_exit = True - elif check_arg('--memory-init-file'): - exit_with_error('--memory-init-file is no longer supported') - elif check_flag('--proxy-to-worker'): - settings_changes.append('PROXY_TO_WORKER=1') - elif check_arg('--valid-abspath'): - options.valid_abspaths.append(consume_arg()) - elif check_flag('--separate-asm'): - exit_with_error('cannot --separate-asm with the wasm backend, since not emitting asm.js') - elif arg.startswith(('-I', '-L')): - path_name = arg[2:] - if os.path.isabs(path_name) and not is_valid_abspath(options, path_name): - # Of course an absolute path to a non-system-specific library or header - # is fine, and you can ignore this warning. The danger are system headers - # that are e.g. x86 specific and non-portable. The emscripten bundled - # headers are modified to be portable, local system ones are generally not. - diagnostics.warning( - 'absolute-paths', f'-I or -L of an absolute path "{arg}" ' - 'encountered. If this is to a local system header/library, it may ' - 'cause problems (local system files make sense for compiling natively ' - 'on your system, but not necessarily to JavaScript).') - elif check_flag('--emrun'): - options.emrun = True - elif check_flag('--cpuprofiler'): - options.cpu_profiler = True - elif check_flag('--threadprofiler'): - settings_changes.append('PTHREADS_PROFILING=1') - elif arg == '-fno-exceptions': - settings.DISABLE_EXCEPTION_CATCHING = 1 - settings.DISABLE_EXCEPTION_THROWING = 1 - settings.WASM_EXCEPTIONS = 0 - elif arg == '-mbulk-memory': - settings.BULK_MEMORY = 1 - elif arg == '-mno-bulk-memory': - settings.BULK_MEMORY = 0 - elif arg == '-fexceptions': - # TODO Currently -fexceptions only means Emscripten EH. Switch to wasm - # exception handling by default when -fexceptions is given when wasm - # exception handling becomes stable. - settings.DISABLE_EXCEPTION_THROWING = 0 - settings.DISABLE_EXCEPTION_CATCHING = 0 - elif arg == '-fwasm-exceptions': - settings.WASM_EXCEPTIONS = 1 - elif arg == '-fignore-exceptions': - settings.DISABLE_EXCEPTION_CATCHING = 1 - elif check_arg('--default-obj-ext'): - exit_with_error('--default-obj-ext is no longer supported by emcc') - elif arg.startswith('-fsanitize=cfi'): - exit_with_error('emscripten does not currently support -fsanitize=cfi') - elif check_arg('--output_eol'): - style = consume_arg() - if style.lower() == 'windows': - options.output_eol = '\r\n' - elif style.lower() == 'linux': - options.output_eol = '\n' - else: - exit_with_error(f'Invalid value "{style}" to --output_eol!') - # Record PTHREADS setting because it controls whether --shared-memory is passed to lld - elif arg == '-pthread': - settings.PTHREADS = 1 - # Also set the legacy setting name, in case use JS code depends on it. - settings.USE_PTHREADS = 1 - elif arg == '-no-pthread': - settings.PTHREADS = 0 - # Also set the legacy setting name, in case use JS code depends on it. - settings.USE_PTHREADS = 0 - elif arg == '-pthreads': - exit_with_error('unrecognized command-line option `-pthreads`; did you mean `-pthread`?') - elif arg in ('-fno-diagnostics-color', '-fdiagnostics-color=never'): - colored_logger.disable() - diagnostics.color_enabled = False - elif arg == '-fno-rtti': - settings.USE_RTTI = 0 - elif arg == '-frtti': - settings.USE_RTTI = 1 - elif arg.startswith('-jsD'): - key = removeprefix(arg, '-jsD') - if '=' in key: - key, value = key.split('=') - else: - value = '1' - if key in settings.keys(): - exit_with_error(f'{arg}: cannot change built-in settings values with a -jsD directive. Pass -s{key}={value} instead!') - user_js_defines += [(key, value)] - newargs[i] = '' - elif check_flag('-shared'): - options.shared = True - elif check_flag('-r'): - options.relocatable = True - elif check_arg('-o'): - options.output_file = consume_arg() - elif arg.startswith('-o'): - options.output_file = removeprefix(arg, '-o') - newargs[i] = '' - elif check_arg('-target') or check_arg('--target'): - options.target = consume_arg() - if options.target not in ('wasm32', 'wasm64', 'wasm64-unknown-emscripten', 'wasm32-unknown-emscripten'): - exit_with_error(f'unsupported target: {options.target} (emcc only supports wasm64-unknown-emscripten and wasm32-unknown-emscripten)') - elif check_arg('--use-port'): - ports.handle_use_port_arg(settings, consume_arg()) - elif arg == '-mllvm': - # Ignore the next argument rather than trying to parse it. This is needed - # because llvm args could, for example, start with `-o` and we don't want - # to confuse that with a normal `-o` flag. - skip = True - - if should_exit: - sys.exit(0) - - newargs = [a for a in newargs if a] - return options, settings_changes, user_js_defines, newargs - - -def is_valid_abspath(options, path_name): - # Any path that is underneath the emscripten repository root must be ok. - if utils.normalize_path(path_name).startswith(utils.normalize_path(utils.path_from_root())): - return True - - def in_directory(root, child): - # make both path absolute - root = os.path.realpath(root) - child = os.path.realpath(child) - - # return true, if the common prefix of both is equal to directory - # e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b - return os.path.commonprefix([root, child]) == root - - for valid_abspath in options.valid_abspaths: - if in_directory(valid_abspath, path_name): - return True - return False - - -def parse_symbol_list_file(contents): - """Parse contents of one-symbol-per-line response file. This format can by used - with, for example, -sEXPORTED_FUNCTIONS=@filename and avoids the need for any - kind of quoting or escaping. - """ - values = contents.splitlines() - return [v.strip() for v in values if not v.startswith('#')] - - -def parse_value(text, expected_type): - # Note that using response files can introduce whitespace, if the file - # has a newline at the end. For that reason, we rstrip() in relevant - # places here. - def parse_string_value(text): - first = text[0] - if first == "'" or first == '"': - text = text.rstrip() - if text[-1] != text[0] or len(text) < 2: - raise ValueError(f'unclosed quoted string. expected final character to be "{text[0]}" and length to be greater than 1 in "{text[0]}"') - return text[1:-1] - return text - - def parse_string_list_members(text): - sep = ',' - values = text.split(sep) - result = [] - index = 0 - while True: - current = values[index].lstrip() # Cannot safely rstrip for cases like: "HERE-> ," - if not len(current): - raise ValueError('empty value in string list') - first = current[0] - if not (first == "'" or first == '"'): - result.append(current.rstrip()) - else: - start = index - while True: # Continue until closing quote found - if index >= len(values): - raise ValueError(f"unclosed quoted string. expected final character to be '{first}' in '{values[start]}'") - new = values[index].rstrip() - if new and new[-1] == first: - if start == index: - result.append(current.rstrip()[1:-1]) - else: - result.append((current + sep + new)[1:-1]) - break - else: - current += sep + values[index] - index += 1 - - index += 1 - if index >= len(values): - break - return result - - def parse_string_list(text): - text = text.rstrip() - if text and text[0] == '[': - if text[-1] != ']': - raise ValueError('unterminated string list. expected final character to be "]"') - text = text[1:-1] - if text.strip() == "": - return [] - return parse_string_list_members(text) - - if expected_type == list or (text and text[0] == '['): - # if json parsing fails, we fall back to our own parser, which can handle a few - # simpler syntaxes - try: - parsed = json.loads(text) - except ValueError: - return parse_string_list(text) - - # if we succeeded in parsing as json, check some properties of it before returning - if type(parsed) not in (str, list): - raise ValueError(f'settings must be strings or lists (not ${type(parsed)})') - if type(parsed) is list: - for elem in parsed: - if type(elem) is not str: - raise ValueError(f'list members in settings must be strings (not ${type(elem)})') - - return parsed - - if expected_type == float: - try: - return float(text) - except ValueError: pass - try: - if text.startswith('0x'): - base = 16 - else: - base = 10 - return int(text, base) - except ValueError: - return parse_string_value(text) - - -def validate_arg_level(level_string, max_level, err_msg, clamp=False): - try: - level = int(level_string) - except ValueError: - exit_with_error(err_msg) - if clamp: - if level > max_level: - logger.warning("optimization level '-O" + level_string + "' is not supported; using '-O" + str(max_level) + "' instead") - level = max_level - if not 0 <= level <= max_level: - exit_with_error(err_msg) - return level - - -def is_int(s): - try: - int(s) - return True - except ValueError: - return False + return [f.value for f in linker_args] @ToolchainProfiler.profile() diff --git a/emcmake b/emcmake deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/emcmake +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/emcmake.bat b/emcmake.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/emcmake.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/emcmake.py b/emcmake.py index a9dc2294b91c2..9fe49270014fd 100755 --- a/emcmake.py +++ b/emcmake.py @@ -5,12 +5,12 @@ # found in the LICENSE file. import os +import shlex import shutil import sys from tools import shared from tools import config from tools import utils -from subprocess import CalledProcessError # @@ -44,6 +44,11 @@ def has_substr(args, substr): # See https://github.com/emscripten-core/emscripten/issues/15522 args.append(f'-DCMAKE_CROSSCOMPILING_EMULATOR={node_js}') + # Print a better error if we have no CMake executable on the PATH + if not os.path.dirname(args[0]) and not shutil.which(args[0]): + print(f'emcmake: cmake executable not found on PATH: `{args[0]}`', file=sys.stderr) + return 1 + # On Windows specify MinGW Makefiles or ninja if we have them and no other # toolchain was specified, to keep CMake from pulling in a native Visual # Studio, or Unix Makefiles. @@ -56,12 +61,8 @@ def has_substr(args, substr): print('emcmake: no compatible cmake generator found; Please install ninja or mingw32-make, or specify a generator explicitly using -G', file=sys.stderr) return 1 - print('configure: ' + shared.shlex_join(args), file=sys.stderr) - try: - shared.check_call(args) - return 0 - except CalledProcessError as e: - return e.returncode + print(f'emcmake: {shlex.join(args)} in directory {os.getcwd()}', file=sys.stderr) + shared.exec_process(args) if __name__ == '__main__': diff --git a/emconfigure b/emconfigure deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/emconfigure +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/emconfigure.bat b/emconfigure.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/emconfigure.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/emconfigure.py b/emconfigure.py index deba798069bb3..4e2d7e95875fb 100755 --- a/emconfigure.py +++ b/emconfigure.py @@ -16,10 +16,11 @@ tests will work properly. """ +import os +import shlex import sys from tools import building from tools import shared -from subprocess import CalledProcessError # @@ -47,12 +48,9 @@ def run(): # compilation with emcc, but instead do builds natively with Clang. This # is a heuristic emulation that may or may not work. env['EMMAKEN_JUST_CONFIGURE'] = '1' - print('configure: ' + shared.shlex_join(args), file=sys.stderr) - try: - shared.check_call(args, env=env) - return 0 - except CalledProcessError as e: - return e.returncode + print(f'emconfigure: {shlex.join(args)} in directory {os.getcwd()}', file=sys.stderr) + os.environ.update(env) + shared.exec_process(args) if __name__ == '__main__': diff --git a/emdump b/emdump deleted file mode 100755 index 82f0e77a5c669..0000000000000 --- a/emdump +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$(dirname $0)/tools/emdump.py" "$@" diff --git a/emdump.bat b/emdump.bat deleted file mode 100644 index 0f19bc7842dc2..0000000000000 --- a/emdump.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (tools\emdump.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to tools\emdump.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%tools\emdump.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%tools\emdump.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%tools\emdump.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%tools\emdump.py" %* diff --git a/emdwp b/emdwp deleted file mode 100755 index 2f93cfaded2e4..0000000000000 --- a/emdwp +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$(dirname $0)/tools/emdwp.py" "$@" diff --git a/emdwp.bat b/emdwp.bat deleted file mode 100644 index 5e4d8658ab9ae..0000000000000 --- a/emdwp.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (tools\emdwp.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to tools\emdwp.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%tools\emdwp.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%tools\emdwp.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%tools\emdwp.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%tools\emdwp.py" %* diff --git a/emmake b/emmake deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/emmake +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/emmake.bat b/emmake.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/emmake.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/emmake.py b/emmake.py index 426e5cc4cd63e..ca349fb145e4d 100755 --- a/emmake.py +++ b/emmake.py @@ -21,12 +21,13 @@ generate JavaScript. """ +import os +import shlex import shutil import sys from tools import building from tools import shared from tools import utils -from subprocess import CalledProcessError # @@ -56,12 +57,12 @@ def run(): # On Windows, run the execution through shell to get PATH expansion and # executable extension lookup, e.g. 'sdl2-config' will match with # 'sdl2-config.bat' in PATH. - print('make: ' + ' '.join(args), file=sys.stderr) - try: - shared.check_call(args, shell=utils.WINDOWS, env=env) - return 0 - except CalledProcessError as e: - return e.returncode + print(f'emmake: "{shlex.join(args)}" in "{os.getcwd()}"', file=sys.stderr) + if utils.WINDOWS: + return shared.run_process(args, check=False, shell=True, env=env).returncode + else: + os.environ.update(env) + shared.exec_process(args) if __name__ == '__main__': diff --git a/emnm b/emnm deleted file mode 100755 index 19fba1368d2b4..0000000000000 --- a/emnm +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$(dirname $0)/tools/emnm.py" "$@" diff --git a/emnm.bat b/emnm.bat deleted file mode 100644 index 80c49452b38f9..0000000000000 --- a/emnm.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (tools\emnm.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to tools\emnm.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%tools\emnm.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%tools\emnm.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%tools\emnm.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%tools\emnm.py" %* diff --git a/emprofile b/emprofile deleted file mode 100755 index 7a35f7a83c39d..0000000000000 --- a/emprofile +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$(dirname $0)/tools/emprofile.py" "$@" diff --git a/emprofile.bat b/emprofile.bat deleted file mode 100644 index 5f5be5c32710c..0000000000000 --- a/emprofile.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (tools\emprofile.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to tools\emprofile.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%tools\emprofile.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%tools\emprofile.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%tools\emprofile.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%tools\emprofile.py" %* diff --git a/emranlib b/emranlib deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/emranlib +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/emranlib.bat b/emranlib.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/emranlib.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/emrun b/emrun deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/emrun +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/emrun.bat b/emrun.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/emrun.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/emrun.py b/emrun.py index f7bc13df34044..fa689e4ecd1bf 100644 --- a/emrun.py +++ b/emrun.py @@ -4,6 +4,10 @@ # University of Illinois/NCSA Open Source License. Both these licenses can be # found in the LICENSE file. +# This file needs to run on older version of python too (even python 2!) so +# suppress these upgrade warnings: +# ruff: noqa: UP015, UP024, UP021, UP025 + """emrun: Implements machinery that allows running a .html page as if it was a standard executable file. @@ -17,6 +21,7 @@ import argparse import atexit import json +import math import os import platform import re @@ -224,8 +229,9 @@ def create_emrun_safe_firefox_profile(): temp_firefox_profile_dir = tempfile.mkdtemp(prefix='temp_emrun_firefox_profile_') with open(os.path.join(temp_firefox_profile_dir, 'prefs.js'), 'w') as f: f.write(''' -// Lift the default max 20 workers limit to something higher to avoid hangs when page needs to spawn a lot of threads. -user_pref("dom.workers.maxPerDomain", 100); +// Old Firefox browsers have a maxPerDomain limit of 20. Newer Firefox browsers default to 512. Match the new +// default here to help test spawning a lot of threads also on older Firefox versions. +user_pref("dom.workers.maxPerDomain", 512); // Always allow opening popups user_pref("browser.popups.showPopupBlocker", false); user_pref("dom.disable_open_during_load", false); @@ -238,6 +244,11 @@ def create_emrun_safe_firefox_profile(): user_pref("browser.sessionstore.restore_on_demand", false); user_pref("browser.sessionstore.max_resumed_crashes", -1); user_pref("toolkit.startup.max_resumed_crashes", -1); +// Ease shutting down browser instances in the parallel browser harness +user_pref("browser.warnOnQuit", false); +user_pref("browser.warnOnQuitShortcut", false); +// Hide about:config confirmation prompt - devs are advanced users +user_pref("browser.aboutConfig.showWarning", false); // Don't show the slow script dialog popup user_pref("dom.max_script_run_time", 0); user_pref("dom.max_chrome_script_run_time", 0); @@ -302,8 +313,15 @@ def create_emrun_safe_firefox_profile(): user_pref("extensions.getAddons.cache.enabled", false); // Enable wasm user_pref("javascript.options.wasm", true); -// Enable SharedArrayBuffer (this profile is for a testing environment, so Spectre/Meltdown don't apply) +// Enable SharedArrayBuffer, and ignore COOP/COEP (this profile is for a testing environment, so Spectre/Meltdown don't apply) user_pref("javascript.options.shared_memory", true); +user_pref("dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", true); +// Enable OffscreenCanvas support +user_pref("gfx.offscreencanvas.enabled", true); +// Enable Wasm64 +user_pref("javascript.options.wasm_memory64", true); +// Do not ask user consent to enable audio playback (0: Allow autoplay for all media) +user_pref("media.autoplay.default", 0); ''') if emrun_options.private_browsing: f.write(''' @@ -565,8 +583,9 @@ def shutdown(self): # Processes HTTP request back to the browser. class HTTPHandler(SimpleHTTPRequestHandler): + protocol_version = 'HTTP/1.1' # noqa: DC01 + def send_head(self): - self.protocol_version = 'HTTP/1.1' global page_last_served_time path = self.translate_path(self.path) f = None @@ -650,14 +669,13 @@ def log_request(self, code): if code != 200: SimpleHTTPRequestHandler.log_request(self, code) - def log_message(self, format, *args): + def log_message(self, format, *args): # noqa: DC04 msg = '%s - - [%s] %s\n' % (self.address_string(), self.log_date_time_string(), format % args) # Filter out 404 messages on favicon.ico not being found to remove noise. if 'favicon.ico' not in msg: sys.stderr.write(msg) - def do_POST(self): - self.protocol_version = 'HTTP/1.1' + def do_POST(self): # # noqa: DC04 global page_exit_code, have_received_messages (_, _, path, query, _) = urlsplit(self.path) @@ -704,6 +722,8 @@ def do_POST(self): if not emrun_options.serve_after_exit: page_exit_code = int(data[6:]) logv('Web page has quit with a call to exit() with return code ' + str(page_exit_code) + '. Shutting down web server. Pass --serve-after-exit to keep serving even after the page terminates with exit().') + # Set server socket to nonblocking on shutdown to avoid sporadic deadlocks + self.server.socket.setblocking(False) self.server.shutdown() return else: @@ -767,12 +787,11 @@ def get_cpu_info(): logical_cores = int(check_output(['sysctl', '-n', 'machdep.cpu.thread_count']).strip()) frequency = int(check_output(['sysctl', '-n', 'hw.cpufrequency']).strip()) // 1000000 elif LINUX: - all_info = check_output(['cat', '/proc/cpuinfo']).strip() - for line in all_info.split("\n"): + for line in open('/proc/cpuinfo').readlines(): if 'model name' in line: cpu_name = re.sub('.*model name.*:', '', line, count=1).strip() lscpu = check_output(['lscpu']) - frequency = int(float(re.search('CPU MHz: (.*)', lscpu).group(1).strip()) + 0.5) + frequency = math.ceil(float(re.search('CPU (max )?MHz: (.*)', lscpu).group(2).strip())) sockets = int(re.search(r'Socket\(s\): (.*)', lscpu).group(1).strip()) physical_cores = sockets * int(re.search(r'Core\(s\) per socket: (.*)', lscpu).group(1).strip()) logical_cores = physical_cores * int(re.search(r'Thread\(s\) per core: (.*)', lscpu).group(1).strip()) @@ -782,13 +801,13 @@ def get_cpu_info(): return {'model': 'Unknown ("' + str(e) + '")', 'physicalCores': 1, 'logicalCores': 1, - 'frequency': 0 + 'frequency': 0, } return {'model': platform.machine() + ', ' + cpu_name, 'physicalCores': physical_cores, 'logicalCores': logical_cores, - 'frequency': frequency + 'frequency': frequency, } @@ -815,7 +834,7 @@ def find_gpu_model(model): return gpu return None - for i in range(0, 16): + for i in range(16): try: hHardwareReg = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'HARDWARE') hDeviceMapReg = winreg.OpenKey(hHardwareReg, 'DEVICEMAP') @@ -961,7 +980,7 @@ def get_browser_build_date(filename): info = plistlib.readPlist(plistfile) # Data in Info.plists is a bit odd, this check combo gives best information on each browser. if 'firefox' in filename.lower(): - return '20' + '-'.join(map((lambda x: x.zfill(2)), info['CFBundleVersion'][2:].split('.'))) + return '20' + '-'.join(x.zfill(2) for x in info['CFBundleVersion'][2:].split('.')) except Exception as e: logv(e) @@ -980,7 +999,7 @@ def get_browser_info(filename, format_json): return json.dumps({ 'name': browser_display_name(filename), 'version': get_executable_version(filename), - 'buildDate': get_browser_build_date(filename) + 'buildDate': get_browser_build_date(filename), }, indent=2) else: return 'Browser: ' + browser_display_name(filename) + ' ' + get_executable_version(filename) + ', build ' + get_browser_build_date(filename) @@ -1331,10 +1350,10 @@ def browser_display_name(browser): def subprocess_env(): e = os.environ.copy() - # https://bugzilla.mozilla.org/show_bug.cgi?id=745154 + # https://bugzil.la/745154 e['MOZ_DISABLE_AUTO_SAFE_MODE'] = '1' - e['MOZ_DISABLE_SAFE_MODE_KEY'] = '1' # https://bugzilla.mozilla.org/show_bug.cgi?id=653410#c9 - e['JIT_OPTION_asmJSAtomicsEnable'] = 'true' # https://bugzilla.mozilla.org/show_bug.cgi?id=1299359#c0 + e['MOZ_DISABLE_SAFE_MODE_KEY'] = '1' # https://bugzil.la/653410#c9 + e['JIT_OPTION_asmJSAtomicsEnable'] = 'true' # https://bugzil.la/1299359#c0 return e @@ -1359,7 +1378,7 @@ def get_system_info(format_json): return json.dumps({'model': get_android_model(), 'os': get_android_os_version(), 'ram': get_system_memory(), - 'cpu': get_android_cpu_infoline() + 'cpu': get_android_cpu_infoline(), }, indent=2) else: info = 'Model: ' + get_android_model() + '\n' @@ -1389,14 +1408,15 @@ def get_system_info(format_json): else: cpu = get_cpu_info() gpus = get_gpu_info() - info = 'Computer name: ' + socket.gethostname() + '\n' # http://stackoverflow.com/questions/799767/getting-name-of-windows-computer-running-python-script + # http://stackoverflow.com/questions/799767/getting-name-of-windows-computer-running-python-script + info = 'Computer name: ' + socket.gethostname() + '\n' info += 'Model: ' + get_computer_model() + '\n' info += 'OS: ' + get_os_version() + ' with ' + str(get_system_memory() // 1024 // 1024) + ' MB of System RAM\n' info += 'CPU: ' + cpu['model'] + ', ' + str(cpu['frequency']) + ' MHz, ' + str(cpu['physicalCores']) + ' physical cores, ' + str(cpu['logicalCores']) + ' logical cores\n' if len(gpus) == 1: info += 'GPU: ' + gpus[0]['model'] + ' with ' + str(gpus[0]['ram'] // 1024 // 1024) + " MB of VRAM\n" elif len(gpus) > 1: - for i in range(0, len(gpus)): + for i in range(len(gpus)): info += 'GPU' + str(i) + ": " + gpus[i]['model'] + ' with ' + str(gpus[i]['ram'] // 1024 // 1024) + ' MBs of VRAM\n' info += 'UUID: ' + unique_system_id return info.strip() @@ -1425,7 +1445,6 @@ def list_processes_by_name(exe_full_path): except Exception: # Fail gracefully if psutil not available logv('import psutil failed, unable to detect browser processes') - pass logv('Searching for processes by full path name "' + exe_full_path + '".. found ' + str(len(pids)) + ' entries') @@ -1581,7 +1600,15 @@ def parse_args(args): return parser.parse_args(args) -def run(args): +def run(args): # noqa: C901, PLR0912, PLR0915 + """Future modifications should consider refactoring to reduce complexity. + + * The McCabe cyclomatiic complexity is currently 74 vs 10 recommended. + * There are currently 86 branches vs 12 recommended. + * There are currently 202 statements vs 50 recommended. + + To revalidate these numbers, run `ruff check --select=C901,PLR091`. + """ global browser_process, browser_exe, processname_killed_atexit, emrun_options, emrun_not_enabled_nag_printed options = emrun_options = parse_args(args) @@ -1627,7 +1654,7 @@ def run(args): file_to_serve = options.serve else: file_to_serve = '.' - file_to_serve_is_url = file_to_serve.startswith('file://') or file_to_serve.startswith('http://') or file_to_serve.startswith('https://') + file_to_serve_is_url = file_to_serve.startswith(('file://', 'http://', 'https://')) if options.serve_root: serve_dir = os.path.abspath(options.serve_root) @@ -1635,7 +1662,7 @@ def run(args): if file_to_serve == '.' or file_to_serve_is_url: serve_dir = os.path.abspath('.') else: - if file_to_serve.endswith('/') or file_to_serve.endswith('\\') or os.path.isdir(file_to_serve): + if file_to_serve.endswith(('/', '\\')) or os.path.isdir(file_to_serve): serve_dir = file_to_serve else: serve_dir = os.path.dirname(os.path.abspath(file_to_serve)) @@ -1674,7 +1701,7 @@ def run(args): return 1 elif options.browser == 'firefox': browser_app = 'org.mozilla.firefox/org.mozilla.gecko.BrowserApp' - elif options.browser == 'firefox_nightly' or options.browser == 'fenix': + elif options.browser in {'firefox_nightly', 'fenix'}: browser_app = 'org.mozilla.fenix/org.mozilla.gecko.BrowserApp' elif options.browser == 'chrome': browser_app = 'com.android.chrome/com.google.android.apps.chrome.Main' diff --git a/emscan-deps.py b/emscan-deps.py new file mode 100755 index 0000000000000..c4d789da589e3 --- /dev/null +++ b/emscan-deps.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# Copyright 2025 The Emscripten Authors. All rights reserved. +# Emscripten is available under two separate licenses, the MIT license and the +# University of Illinois/NCSA Open Source License. Both these licenses can be +# found in the LICENSE file. + +"""emscan-deps - clang-scan-deps helper script + +This script acts as a frontend replacement for clang-scan-deps. +""" + +import sys + +from tools import shared, cmdline, compile + +argv = sys.argv[1:] + +# Parse and discard any emcc-specific flags (e.g. -sXXX). +newargs = cmdline.parse_arguments(argv)[1] + +# Add any clang flags that emcc would add. +newargs += compile.get_cflags(tuple(argv)) + +shared.exec_process([shared.CLANG_SCAN_DEPS] + newargs) diff --git a/emscons b/emscons deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/emscons +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/emscons.bat b/emscons.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/emscons.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/emscripten-version.txt b/emscripten-version.txt index 81e70ed9946ff..64f9c2914b19f 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1 +1 @@ -3.1.65-git +4.0.17-git diff --git a/emsize b/emsize deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/emsize +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/emsize.bat b/emsize.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/emsize.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/emsize.py b/emsize.py index 8bf92a3843c01..ce46649f6f3aa 100755 --- a/emsize.py +++ b/emsize.py @@ -28,7 +28,7 @@ from tools import shared -LLVM_SIZE = os.path.expanduser(shared.build_llvm_tool_path(shared.exe_suffix('llvm-size'))) +LLVM_SIZE = shared.llvm_tool_path('llvm-size') def error(text): diff --git a/emstrip b/emstrip deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/emstrip +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/emstrip.bat b/emstrip.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/emstrip.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/emsymbolizer b/emsymbolizer deleted file mode 100755 index eef0f00c6730f..0000000000000 --- a/emsymbolizer +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Copyright 2020 The Emscripten Authors. All rights reserved. -# Emscripten is available under two separate licenses, the MIT license and the -# University of Illinois/NCSA Open Source License. Both these licenses can be -# found in the LICENSE file. -# -# Entry point for running python scripts on UNIX systems. -# -# Automatically generated by `create_entry_points.py`; DO NOT EDIT. -# -# To make modifications to this file, edit `tools/run_python.sh` and then run -# `tools/maint/create_entry_points.py` - -# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -# of cpython used in cross compilation via setup.py. -unset _PYTHON_SYSCONFIGDATA_NAME - -if [ -z "$PYTHON" ]; then - PYTHON=$EMSDK_PYTHON -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python3 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - PYTHON=$(command -v python 2> /dev/null) -fi - -if [ -z "$PYTHON" ]; then - echo 'unable to find python in $PATH' - exit 1 -fi - -exec "$PYTHON" -E "$0.py" "$@" diff --git a/emsymbolizer.bat b/emsymbolizer.bat deleted file mode 100644 index 83edd646f7918..0000000000000 --- a/emsymbolizer.bat +++ /dev/null @@ -1,86 +0,0 @@ -:: Entry point for running python scripts on windows systems. -:: -:: Automatically generated by `create_entry_points.py`; DO NOT EDIT. -:: -:: To make modifications to this file, edit `tools/run_python.bat` and then run -:: `tools/maint/create_entry_points.py` - -:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks, -:: or there will be a parsing error. - -:: All env. vars specified in this file are to be local only to this script. -@setlocal -:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal -:: of cpython used in cross compilation via setup.py. -@set _PYTHON_SYSCONFIGDATA_NAME= -@set EM_PY=%EMSDK_PYTHON% -@if "%EM_PY%"=="" ( - set EM_PY=python -) - -:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this -:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and -:: %~dp0 expansions will not work. -:: So first try if %~dp0 might work, and if not, manually look up this script from PATH. -@if exist "%~f0" ( - set MYDIR=%~dp0 - goto FOUND_MYDIR -) -@for %%I in (%~n0.bat) do ( - @if exist %%~$PATH:I ( - set MYDIR=%%~dp$PATH:I - ) else ( - echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat. - echo To help this issue, try removing unnecessary quotes in the invocation of emcc, - echo or add Emscripten directory to PATH. - echo See github.com/microsoft/terminal/issues/15212 and - echo github.com/emscripten-core/emscripten/issues/19207 for more details. - ) -) -:FOUND_MYDIR - -:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a -:: shared stdin handle from the parent process, and that parent process stdin handle is in -:: a certain state, running python.exe might hang here. To work around this, if -:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid -:: sharing the parent's stdin handle to it, avoiding the hang. - -:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel, -:: even when the python executable above did succeed and quit with errorlevel 0 above. -:: On Windows 8 and newer, this issue has not been observed. It is possible that this -:: issue is related to the above python bug, but this has not been conclusively confirmed, -:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known -:: workaround this issue, which is to explicitly quit the calling process with the previous -:: errorlevel from the above command. - -:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from -:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that -:: would throw off the parsing of the cmdline arg. -@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto NORMAL - ) else ( - goto NORMAL_EXIT - ) -) else ( - @if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" ( - goto MUTE_STDIN - ) else ( - goto MUTE_STDIN_EXIT - ) -) - -:NORMAL_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* -@exit %ERRORLEVEL% - -:MUTE_STDIN -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit /b %ERRORLEVEL% - -:MUTE_STDIN_EXIT -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL -@exit %ERRORLEVEL% - -:NORMAL -@"%EM_PY%" -E "%MYDIR%%~n0.py" %* diff --git a/emsymbolizer.py b/emsymbolizer.py deleted file mode 100755 index 1a9cf27f25d56..0000000000000 --- a/emsymbolizer.py +++ /dev/null @@ -1,273 +0,0 @@ -#!/usr/bin/env python3 - -# This is a utility for looking up the symbol names and/or file+line numbers -# of code addresses. There are several possible sources of this information, -# with varying granularity (listed here in approximate preference order). - -# If the wasm has DWARF info, llvm-symbolizer can show the symbol, file, and -# line/column number, potentially including inlining. -# If the wasm has separate DWARF info, do the above with the side file -# If there is a source map, we can parse it to get file and line number. -# If there is an emscripten symbol map, we can use that to get the symbol name -# If there is a name section or symbol table, llvm-symbolizer can show the -# symbol name. -# Separate DWARF and emscripten symbol maps are not supported yet. - -import argparse -import json -import os -import re -import subprocess -import sys -from tools import shared -from tools import webassembly - -LLVM_SYMBOLIZER = os.path.expanduser( - shared.build_llvm_tool_path(shared.exe_suffix('llvm-symbolizer'))) - - -class Error(BaseException): - pass - - -# Class to treat location info in a uniform way across information sources. -class LocationInfo(object): - def __init__(self, source=None, line=0, column=0, func=None): - self.source = source - self.line = line - self.column = column - self.func = func - - def print(self): - source = self.source if self.source else '??' - func = self.func if self.func else '??' - print(f'{func}\n{source}:{self.line}:{self.column}') - - -def get_codesec_offset(module): - sec = module.get_section(webassembly.SecType.CODE) - if not sec: - raise Error(f'No code section found in {module.filename}') - return sec.offset - - -def has_debug_line_section(module): - return module.get_custom_section('.debug_line') is not None - - -def has_name_section(module): - return module.get_custom_section('name') is not None - - -def has_linking_section(module): - return module.get_custom_section('linking') is not None - - -def symbolize_address_symbolizer(module, address, is_dwarf): - if is_dwarf: - vma_adjust = get_codesec_offset(module) - else: - vma_adjust = 0 - cmd = [LLVM_SYMBOLIZER, '-e', module.filename, f'--adjust-vma={vma_adjust}', - str(address)] - out = shared.run_process(cmd, stdout=subprocess.PIPE).stdout.strip() - out_lines = out.splitlines() - - # Source location regex, e.g., /abc/def.c:3:5 - SOURCE_LOC_RE = re.compile(r'(.+):(\d+):(\d+)$') - # llvm-symbolizer prints two lines per location. The first line contains a - # function name, and the second contains a source location like - # '/abc/def.c:3:5'. If the function or source info is not available, it will - # be printed as '??', in which case we store None. If the line and column info - # is not available, they will be printed as 0, which we store as is. - for i in range(0, len(out_lines), 2): - func, loc_str = out_lines[i], out_lines[i + 1] - m = SOURCE_LOC_RE.match(loc_str) - source, line, column = m.group(1), m.group(2), m.group(3) - if func == '??': - func = None - if source == '??': - source = None - LocationInfo(source, line, column, func).print() - - -def get_sourceMappingURL_section(module): - for sec in module.sections(): - if sec.name == "sourceMappingURL": - return sec - return None - - -class WasmSourceMap(object): - class Location(object): - def __init__(self, source=None, line=0, column=0, func=None): - self.source = source - self.line = line - self.column = column - self.func = func - - def __init__(self): - self.version = None - self.sources = [] - self.mappings = {} - self.offsets = [] - - def parse(self, filename): - with open(filename) as f: - source_map_json = json.loads(f.read()) - if shared.DEBUG: - print(source_map_json) - - self.version = source_map_json['version'] - self.sources = source_map_json['sources'] - - vlq_map = {} - chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' - for i, c in enumerate(chars): - vlq_map[c] = i - - def decodeVLQ(string): - result = [] - shift = 0 - value = 0 - for c in string: - try: - integer = vlq_map[c] - except ValueError: - raise Error(f'Invalid character ({c}) in VLQ') - value += (integer & 31) << shift - if integer & 32: - shift += 5 - else: - negate = value & 1 - value >>= 1 - result.append(-value if negate else value) - value = shift = 0 - return result - - offset = 0 - src = 0 - line = 1 - col = 1 - for segment in source_map_json['mappings'].split(','): - data = decodeVLQ(segment) - info = [] - - offset += data[0] - if len(data) >= 2: - src += data[1] - info.append(src) - if len(data) >= 3: - line += data[2] - info.append(line) - if len(data) >= 4: - col += data[3] - info.append(col) - # TODO: see if we need the name, which is the next field (data[4]) - - self.mappings[offset] = WasmSourceMap.Location(*info) - self.offsets.append(offset) - self.offsets.sort() - - def find_offset(self, offset): - # Find the largest mapped offset <= the search offset - lo = 0 - hi = len(self.offsets) - - while lo < hi: - mid = (lo + hi) // 2 - if self.offsets[mid] > offset: - hi = mid - else: - lo = mid + 1 - return self.offsets[lo - 1] - - def lookup(self, offset): - nearest = self.find_offset(offset) - assert nearest in self.mappings, 'Sourcemap has an offset with no mapping' - info = self.mappings[nearest] - return LocationInfo( - self.sources[info.source] if info.source is not None else None, - info.line, - info.column - ) - - -def symbolize_address_sourcemap(module, address, force_file): - URL = force_file - if not URL: - # If a sourcemap file is not forced, read it from the wasm module - section = get_sourceMappingURL_section(module) - assert section - module.seek(section.offset) - assert module.read_string() == 'sourceMappingURL' - # TODO: support stripping/replacing a prefix from the URL - URL = module.read_string() - - if shared.DEBUG: - print(f'Source Mapping URL: {URL}') - sm = WasmSourceMap() - sm.parse(URL) - if shared.DEBUG: - csoff = get_codesec_offset(module) - print(sm.mappings) - # Print with section offsets to easily compare against dwarf - for k, v in sm.mappings.items(): - print(f'{k-csoff:x}: {v}') - sm.lookup(address).print() - - -def main(args): - with webassembly.Module(args.wasm_file) as module: - base = 16 if args.address.lower().startswith('0x') else 10 - address = int(args.address, base) - - if args.addrtype == 'code': - address += get_codesec_offset(module) - - if ((has_debug_line_section(module) and not args.source) or - 'dwarf' in args.source): - symbolize_address_symbolizer(module, address, is_dwarf=True) - elif ((get_sourceMappingURL_section(module) and not args.source) or - 'sourcemap' in args.source): - symbolize_address_sourcemap(module, address, args.file) - elif ((has_name_section(module) and not args.source) or - 'names' in args.source): - symbolize_address_symbolizer(module, address, is_dwarf=False) - elif ((has_linking_section(module) and not args.source) or - 'symtab' in args.source): - symbolize_address_symbolizer(module, address, is_dwarf=False) - else: - raise Error('No .debug_line or sourceMappingURL section found in ' - f'{module.filename}.' - " I don't know how to symbolize this file yet") - - -def get_args(): - parser = argparse.ArgumentParser() - parser.add_argument('-s', '--source', choices=['dwarf', 'sourcemap', - 'names', 'symtab'], - help='Force debug info source type', default=()) - parser.add_argument('-f', '--file', action='store', - help='Force debug info source file') - parser.add_argument('-t', '--addrtype', choices=['code', 'file'], - default='file', - help='Address type (code section or file offset)') - parser.add_argument('-v', '--verbose', action='store_true', - help='Print verbose info for debugging this script') - parser.add_argument('wasm_file', help='Wasm file') - parser.add_argument('address', help='Address to lookup') - args = parser.parse_args() - if args.verbose: - shared.PRINT_SUBPROCS = 1 - shared.DEBUG = True - return args - - -if __name__ == '__main__': - try: - rv = main(get_args()) - except (Error, webassembly.InvalidWasmError, OSError) as e: - print(f'{sys.argv[0]}: {str(e)}', file=sys.stderr) - rv = 1 - sys.exit(rv) diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000000000..fa52f28f45ce5 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,95 @@ +import globals from 'globals'; +import js from '@eslint/js'; +import { FlatCompat } from '@eslint/eslintrc'; +import { loadDefaultSettings } from './src/utility.mjs'; + +const compat = new FlatCompat({ + baseDirectory: import.meta.dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all +}); + + +// Emscripten settings are made available to the compiler as global +// variables. Make sure eslint knows about them. +const settings = loadDefaultSettings(); +const settingsGlobals = {}; +for (const name of Object.keys(settings)) { + settingsGlobals[name] = 'writable'; +} + +export default [{ + ignores: [ + '**/out/', + '**/site/', + '**/cache/', + '**/third_party/', + '**/test/', + 'src/polyfill/', + 'src/lib/', + 'src/minimum_runtime_check.js', + 'src/runtime_*.js', + 'src/shell*.js', + 'src/modularize.js', + 'src/preamble*.js', + 'src/postlibrary.js', + 'src/postamble*.js', + 'src/closure-externs/', + 'src/embind/', + 'src/pthread_esm_startup.mjs', + 'src/emrun_postjs.js', + 'src/wasm_worker.js', + 'src/audio_worklet.js', + 'src/wasm2js.js', + 'src/webGLClient.js', + 'src/webGLWorker.js', + 'src/*_shell_read.js', + 'src/threadprofiler.js', + 'src/cpuprofiler.js', + 'src/memoryprofiler.js', + 'src/gl-matrix.js', + 'src/source_map_support.js', + 'src/Fetch.js', + 'src/settings.js', + 'src/settings_internal.js', + 'src/emrun_prejs.js', + 'src/deterministic.js', + 'src/proxyWorker.js', + 'src/proxyClient.js', + 'src/IDBStore.js', + 'tools/experimental', + ], +}, ...compat.extends('prettier'), js.configs.recommended, { + languageOptions: { + globals: { + ...globals.browser, + ...globals.node, + ...settingsGlobals, + }, + + ecmaVersion: 'latest', + sourceType: 'module', + }, + + rules: { + 'max-len': 'off', + 'no-multi-spaces': 'off', + 'require-jsdoc': 'off', + 'arrow-body-style': ['error', 'as-needed'], + 'space-infix-ops': 'error', + 'no-prototype-builtins': 'off', + + quotes: ['error', 'single', { + avoidEscape: true, + }], + }, +}, { + files: ['**/*.mjs'], + + rules: { + 'no-unused-vars': ['error', { + argsIgnorePattern: '^_', + destructuredArrayIgnorePattern: '^_', + }], + }, +}]; diff --git a/package-lock.json b/package-lock.json index 8e9fafb362e76..992d1a776333d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,56 +1,40 @@ { "name": "emscripten", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "dependencies": { - "@babel/cli": "^7.24.7", - "@babel/core": "^7.24.7", - "@babel/preset-env": "^7.24.7", - "acorn": "^8.12.0", - "google-closure-compiler": "20230802.0.0", + "@babel/cli": "^7.28.3", + "@babel/core": "^7.28.4", + "@babel/preset-env": "^7.28.3", + "acorn": "^8.15.0", + "google-closure-compiler": "20240317.0.0", "html-minifier-terser": "7.2.0" }, "devDependencies": { - "es-check": "^7.2.1", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "prettier": "^3.3.2", - "source-map": "0.7.4", - "typescript": "^5.5.2", - "webpack": "^5.92.1", - "webpack-cli": "^5.1.4", - "ws": "^8.17.1" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.36.0", + "es-check": "^9.4.4", + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", + "globals": "^16.4.0", + "prettier": "^3.6.2", + "rollup": "^4.52.3", + "source-map": "0.7.6", + "typescript": "^5.9.3", + "vite": "^7.1.7", + "webpack": "^5.102.0", + "webpack-cli": "^6.0.1", + "ws": "^8.18.3" } }, "node_modules/@babel/cli": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.24.7.tgz", - "integrity": "sha512-8dfPprJgV4O14WTx+AQyEA+opgUKPrsIXX/MdL50J1n06EQJ6m1T+CdsJe0qEC0B/Xl85i+Un5KVAxd/PACX9A==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.3.tgz", + "integrity": "sha512-n1RU5vuCX0CsaqaXm9I0KUCNKNQMy5epmzl/xdSSm70bSqhg9GWhgeosypyQLc0bK24+Xpk1WGzZlI9pJtkZdg==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", + "@jridgewell/trace-mapping": "^0.3.28", "commander": "^6.2.0", "convert-source-map": "^2.0.0", "fs-readdir-recursive": "^1.1.0", @@ -67,47 +51,48 @@ }, "optionalDependencies": { "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0" + "chokidar": "^3.6.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -122,59 +107,40 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "dependencies": { - "@babel/types": "^7.24.7", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.27.3" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -182,27 +148,17 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", - "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", "semver": "^6.3.1" }, "engines": { @@ -212,21 +168,13 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "regexpu-core": "^5.3.1", + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "engines": { @@ -236,97 +184,61 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -336,32 +248,32 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -371,13 +283,13 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -386,109 +298,74 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", + "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/types": "^7.28.4" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -497,12 +374,26 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -512,11 +403,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -526,13 +417,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -542,12 +433,12 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", + "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -560,6 +451,7 @@ "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", "engines": { "node": ">=6.9.0" }, @@ -567,70 +459,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -640,127 +474,11 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -773,6 +491,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -785,11 +504,11 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -799,14 +518,13 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -816,13 +534,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -832,11 +550,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -846,11 +564,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -860,12 +578,12 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -875,13 +593,12 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", + "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-create-class-features-plugin": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -891,18 +608,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", + "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "globals": "^11.1.0" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.4" }, "engines": { "node": ">=6.9.0" @@ -912,12 +627,12 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -927,11 +642,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -941,12 +657,12 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -956,11 +672,11 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -969,13 +685,42 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -985,12 +730,11 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1000,12 +744,11 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1015,12 +758,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1030,13 +773,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1046,12 +789,11 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1061,11 +803,11 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1075,12 +817,11 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1090,11 +831,11 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1104,12 +845,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1119,13 +860,12 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1135,14 +875,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1152,12 +892,12 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1167,12 +907,12 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1182,11 +922,11 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1196,12 +936,11 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1211,12 +950,11 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1226,14 +964,15 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", + "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1243,12 +982,12 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1258,12 +997,11 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1273,13 +1011,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1289,11 +1026,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1303,12 +1040,12 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1318,14 +1055,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1335,11 +1071,11 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1349,12 +1085,11 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz", + "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "regenerator-transform": "^0.15.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1363,12 +1098,27 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1378,11 +1128,11 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1392,12 +1142,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1407,11 +1157,11 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1421,11 +1171,11 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1435,11 +1185,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1449,11 +1199,11 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1463,12 +1213,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1478,12 +1228,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1493,12 +1243,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1508,90 +1258,79 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", - "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz", + "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==", + "dependencies": { + "@babel/compat-data": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.28.3", + "@babel/plugin-transform-classes": "^7.28.3", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.3", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "engines": { @@ -1601,18 +1340,11 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/preset-modules": { "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -1622,63 +1354,43 @@ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, - "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", - "debug": "^4.3.1", - "globals": "^11.1.0" + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1689,6 +1401,7 @@ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.1.90" } @@ -1698,6 +1411,7 @@ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", "dev": true, + "license": "MIT", "dependencies": { "colorspace": "1.1.x", "enabled": "2.0.x", @@ -1705,6016 +1419,2660 @@ } }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=14.17.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=18" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10.10.0" + "node": ">=18" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", - "optional": true - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">=18" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">=18" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">=18" } }, - "node_modules/@types/eslint": { - "version": "8.44.8", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.8.tgz", - "integrity": "sha512-4K8GavROwhrYl2QXDXm0Rv9epkA8GBFu0EI+XrrnnuCl7u8CWBRusX7fXJfanhZTDWSAL24gDI/UqXyUM0Injw==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.14.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.7.tgz", - "integrity": "sha512-uTr2m2IbJJucF3KUxgnGOZvYbN0QgkGyWxG6973HCpMYFy2KfcgYuIwkJQMQkt1VbBMlvWRbpshFTLxnxCZjKQ==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "undici-types": "~5.26.4" + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "dev": true - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "dev": true + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, + "license": "MIT", "dependencies": { - "@xtuc/long": "4.2.2" + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@xtuc/long": "4.2.2" + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=14.15.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18" }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "node_modules/@eslint/js": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } }, - "node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", - "bin": { - "acorn": "bin/acorn" + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" }, "engines": { - "node": ">=0.4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, - "peerDependencies": { - "acorn": "^8" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" }, "funding": { "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "optional": true, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=6.0.0" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "license": "MIT", + "optional": true }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "optional": true, "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node": ">= 8" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "devOptional": true, - "dependencies": { - "fill-range": "^7.0.1" - }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" - }, - "bin": { - "browserslist": "cli.js" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">= 8" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", + "cpu": [ + "arm" + ], "dev": true, - "engines": { - "node": ">=6" - } + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/caniuse-lite": { - "version": "1.0.30001636", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz", - "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" ] }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", + "cpu": [ + "arm64" ], + "dev": true, + "license": "MIT", "optional": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } + "os": [ + "freebsd" + ] }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", + "cpu": [ + "x64" + ], "dev": true, - "engines": { - "node": ">=6.0" - } + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/clean-css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "engines": { - "node": ">=0.8" - } + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", - "engines": { - "node": ">= 0.10" - } + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", + "cpu": [ + "loong64" + ], "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==" - }, - "node_modules/cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dependencies": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" - } - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dev": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "dev": true, - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "node_modules/core-js-compat": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", - "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", - "dependencies": { - "browserslist": "^4.23.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.807", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.807.tgz", - "integrity": "sha512-kSmJl2ZwhNf/bcIuCH/imtNOKlpkLDn2jqT5FJ+/0CXjhnFaOa9cOe9gHKKy71eM49izwuQjZhKk+lWQ1JxB7A==" - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "dev": true - }, - "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/envinfo": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz", - "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==", - "dev": true, - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/es-check": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/es-check/-/es-check-7.2.1.tgz", - "integrity": "sha512-4sxU2OZ1aYYRRX2ajL3hDDBaY96Yr/OcH6MTRerIuOSyil6SQYQQ0b48uqVfYGRCiI0NgJbtY6Sbmf75oPaTeQ==", - "dev": true, - "dependencies": { - "acorn": "8.11.3", - "commander": "12.0.0", - "fast-glob": "^3.3.2", - "supports-color": "^8.1.1", - "winston": "3.13.0" - }, - "bin": { - "es-check": "index.js" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/es-check/node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/es-check/node_modules/commander": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", - "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/es-check/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/es-check/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/es-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", - "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "dev": true - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "devOptional": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "dev": true - }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/google-closure-compiler": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20230802.0.0.tgz", - "integrity": "sha512-o2fYoc8lqOBdhm95Ick0vWrtwH2Icd5yLZhbTcQ0T7NfGiBepYvx1BB63hR8ebgzEZemz9Fh+O6Kg/3Mjm28ww==", - "dependencies": { - "chalk": "4.x", - "google-closure-compiler-java": "^20230802.0.0", - "minimist": "1.x", - "vinyl": "2.x", - "vinyl-sourcemaps-apply": "^0.2.0" - }, - "bin": { - "google-closure-compiler": "cli.js" - }, - "engines": { - "node": ">=10" - }, - "optionalDependencies": { - "google-closure-compiler-linux": "^20230802.0.0", - "google-closure-compiler-osx": "^20230802.0.0", - "google-closure-compiler-windows": "^20230802.0.0" - } - }, - "node_modules/google-closure-compiler-java": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20230802.0.0.tgz", - "integrity": "sha512-PWKLMLwj7pR/U0yYbiy649LLqAscu+F1gyY4Y/jK6CmSLb8cIJbL8BTJd00828TzTNfWnYwxbkcQw0y9C2YsGw==" - }, - "node_modules/google-closure-compiler-linux": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20230802.0.0.tgz", - "integrity": "sha512-F13U4iSXiWeGtHOFS25LVem1s6zI+pJvXVPVR7zSib5ppoUJ0JXnABJQezUR3FnpxmnkALG4oIGW0syH9zPLZA==", - "cpu": [ - "x32", - "x64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/google-closure-compiler-osx": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20230802.0.0.tgz", - "integrity": "sha512-ANAi/ux92Tt+Na7vFDLeK2hRzotjC5j+nxoPtE0OcuNcbjji5dREKoJxkq7r0YwRTCzAFZszK5ip/NPdTOdCEg==", - "cpu": [ - "x32", - "x64", - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/google-closure-compiler-windows": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20230802.0.0.tgz", - "integrity": "sha512-ZQPujoNiiUyTGl8zEGR/0yAygWnbMtX/NQ/S/EHVgq5nmYkvDEVuiVbgpPAmO9lzBTq0hvUTRRATZbTU2ISxgA==", - "cpu": [ - "x32", - "x64" - ], - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/google-closure-compiler/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/google-closure-compiler/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/google-closure-compiler/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/google-closure-compiler/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/google-closure-compiler/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/google-closure-compiler/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-minifier-terser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", - "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "~5.3.2", - "commander": "^10.0.0", - "entities": "^4.4.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.15.1" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - } - }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "engines": { - "node": ">=14" - } - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "optional": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "dev": true - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/logform": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", - "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", - "dev": true, - "dependencies": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dev": true, - "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "optional": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==" - }, - "node_modules/replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.26.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz", - "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "dev": true - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "dev": true, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/vinyl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", - "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "dependencies": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vinyl-sourcemaps-apply": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", - "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", - "dependencies": { - "source-map": "^0.5.1" - } - }, - "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", - "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", - "dev": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", - "dev": true, - "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", - "colorette": "^2.0.14", - "commander": "^10.0.1", - "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true - }, - "node_modules/winston": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", - "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", - "dev": true, - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.4.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.7.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", - "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", - "dev": true, - "dependencies": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/winston/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/cli": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.24.7.tgz", - "integrity": "sha512-8dfPprJgV4O14WTx+AQyEA+opgUKPrsIXX/MdL50J1n06EQJ6m1T+CdsJe0qEC0B/Xl85i+Un5KVAxd/PACX9A==", - "requires": { - "@jridgewell/trace-mapping": "^0.3.25", - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0", - "commander": "^6.2.0", - "convert-source-map": "^2.0.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - } - }, - "@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "requires": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - } - }, - "@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==" - }, - "@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", - "requires": { - "@babel/types": "^7.24.7", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", - "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", - "requires": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", - "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", - "requires": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "requires": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", - "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", - "requires": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==" - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" - } - }, - "@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", - "requires": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" - } - }, - "@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", - "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==" - }, - "@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==" - }, - "@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==" - }, - "@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", - "requires": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", - "requires": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "requires": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - } - }, - "@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==" - }, - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", - "requires": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" - } - }, - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", - "requires": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "requires": {} - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", - "requires": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", - "requires": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", - "requires": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", - "requires": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", - "requires": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", - "requires": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", - "requires": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", - "requires": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" - } - }, - "@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "regenerator-transform": "^0.15.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", - "requires": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, - "@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, - "@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", - "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", - "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", - "debug": "^4.3.1", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", - "requires": { - "@babel/helper-string-parser": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" - } - }, - "@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "dev": true - }, - "@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "dev": true, - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", + "cpu": [ + "ppc64" + ], "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", + "cpu": [ + "riscv64" + ], "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - } - } - }, - "@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", + "cpu": [ + "riscv64" + ], "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "dev": true + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "requires": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - } + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==" + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] }, - "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", - "optional": true + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", + "cpu": [ + "ia32" + ], "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "@types/eslint": { - "version": "8.44.8", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.8.tgz", - "integrity": "sha512-4K8GavROwhrYl2QXDXm0Rv9epkA8GBFu0EI+XrrnnuCl7u8CWBRusX7fXJfanhZTDWSAL24gDI/UqXyUM0Injw==", + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, - "@types/eslint-scope": { + "node_modules/@types/eslint-scope": { "version": "3.7.7", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@types/eslint": "*", "@types/estree": "*" } }, - "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, - "@types/json-schema": { + "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "dev": true, + "license": "MIT" }, - "@types/node": { - "version": "20.14.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.7.tgz", - "integrity": "sha512-uTr2m2IbJJucF3KUxgnGOZvYbN0QgkGyWxG6973HCpMYFy2KfcgYuIwkJQMQkt1VbBMlvWRbpshFTLxnxCZjKQ==", + "node_modules/@types/node": { + "version": "22.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", + "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", "dev": true, - "requires": { - "undici-types": "~5.26.4" + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" } }, - "@types/triple-beam": { + "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "dev": true - }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" }, - "@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "dev": true + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, - "@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, - "@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, - "requires": { + "license": "Apache-2.0", + "dependencies": { "@xtuc/long": "4.2.2" } }, - "@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } }, - "@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.12.1", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, - "@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "node_modules/@webpack-cli/configtest": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", "dev": true, - "requires": {} + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } }, - "@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "node_modules/@webpack-cli/info": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", "dev": true, - "requires": {} + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } }, - "@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "node_modules/@webpack-cli/serve": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", "dev": true, - "requires": {} + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } }, - "@xtuc/ieee754": { + "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, - "@xtuc/long": { + "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, - "acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==" + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } }, - "acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "node_modules/acorn-import-phases": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.3.tgz", + "integrity": "sha512-jtKLnfoOzm28PazuQ4dVBcE9Jeo6ha1GAJvq3N0LlNOszmTfx+wSycBehn+FN0RnyeR77IBxN/qVYMw0Rlj0Xw==", "dev": true, - "requires": {} + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } }, - "acorn-jsx": { + "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "requires": {} + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } }, - "ajv": { + "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, - "requires": {} + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "anymatch": { + "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", "optional": true, - "requires": { + "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "argparse": { + "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, - "async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" }, - "babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", - "requires": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "dependencies": { + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "optional": true + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { + "license": "MIT", + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "devOptional": true, - "requires": { - "fill-range": "^7.0.1" + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", - "requires": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "buffer-from": { + "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" }, - "callsites": { + "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "camel-case": { + "node_modules/camel-case": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "requires": { + "license": "MIT", + "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" } }, - "caniuse-lite": { - "version": "1.0.30001636", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz", - "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==" + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", "optional": true, - "requires": { + "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } }, - "clean-css": { + "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "requires": { + "license": "MIT", + "dependencies": { "source-map": "~0.6.0" }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } }, - "clone": { + "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } }, - "clone-buffer": { + "node_modules/clone-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==" + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } }, - "clone-deep": { + "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "clone-stats": { + "node_modules/clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==" + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "license": "MIT" }, - "cloneable-readable": { + "node_modules/cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "requires": { + "license": "MIT", + "dependencies": { "inherits": "^2.0.1", "process-nextick-args": "^2.0.0", "readable-stream": "^2.3.5" } }, - "color": { + "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "color-convert": "^1.9.3", "color-string": "^1.6.0" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, - "color-string": { + "node_modules/color-string": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, - "colorette": { + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true + "dev": true, + "license": "MIT" }, - "colorspace": { + "node_modules/colorspace": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "color": "^3.1.3", "text-hex": "1.0.x" } }, - "commander": { + "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" }, - "convert-source-map": { + "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" }, - "core-js-compat": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", - "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", - "requires": { - "browserslist": "^4.23.0" + "node_modules/core-js-compat": { + "version": "3.44.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.44.0.tgz", + "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", + "dependencies": { + "browserslist": "^4.25.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "deep-is": { + "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "requires": { - "esutils": "^2.0.2" - } + "license": "MIT" }, - "dot-case": { + "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "requires": { + "license": "MIT", + "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, - "electron-to-chromium": { - "version": "1.4.807", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.807.tgz", - "integrity": "sha512-kSmJl2ZwhNf/bcIuCH/imtNOKlpkLDn2jqT5FJ+/0CXjhnFaOa9cOe9gHKKy71eM49izwuQjZhKk+lWQ1JxB7A==" + "node_modules/electron-to-chromium": { + "version": "1.5.180", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.180.tgz", + "integrity": "sha512-ED+GEyEh3kYMwt2faNmgMB0b8O5qtATGgR4RmRsIp4T6p7B8vdMbIedYndnvZfsaXvSzegtpfqRMDNCjjiSduA==" }, - "enabled": { + "node_modules/enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" } }, - "entities": { + "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, - "envinfo": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz", - "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==", - "dev": true + "node_modules/envinfo": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-check": { + "version": "9.4.4", + "resolved": "https://registry.npmjs.org/es-check/-/es-check-9.4.4.tgz", + "integrity": "sha512-Ppp6r1diw1jy0t5uQX47HN3JqvosoAymZshdimrwpxCY1GQfZvqTqb9tHiiDbkm+aqGLdVDCZoL9okue7tdXZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "8.15.0", + "acorn-walk": "^8.3.4", + "browserslist": "^4.23.3", + "commander": "14.0.1", + "fast-brake": "^0.1.4", + "fast-glob": "^3.3.3", + "lilconfig": "^3.1.3", + "source-map": "^0.7.4", + "supports-color": "8.1.1", + "winston": "3.17.0" + }, + "bin": { + "es-check": "index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/es-check/node_modules/commander": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.1.tgz", + "integrity": "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "license": "MIT" }, - "es-check": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/es-check/-/es-check-7.2.1.tgz", - "integrity": "sha512-4sxU2OZ1aYYRRX2ajL3hDDBaY96Yr/OcH6MTRerIuOSyil6SQYQQ0b48uqVfYGRCiI0NgJbtY6Sbmf75oPaTeQ==", + "node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "dev": true, - "requires": { - "acorn": "8.11.3", - "commander": "12.0.0", - "fast-glob": "^3.3.2", - "supports-color": "^8.1.1", - "winston": "3.13.0" + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" }, - "dependencies": { - "acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true - }, - "commander": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", - "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" } }, - "es-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", - "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", - "dev": true + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } }, - "escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - }, - "eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true } } }, - "eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, - "requires": {} + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } }, - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "requires": { + "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "requires": { - "acorn": "^8.9.0", + "dependencies": { + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "requires": { + "license": "BSD-3-Clause", + "dependencies": { "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" } }, - "esrecurse": { + "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "requires": { + "license": "BSD-2-Clause", + "dependencies": { "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" } }, - "estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } }, - "esutils": { + "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } }, - "events": { + "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-brake": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/fast-brake/-/fast-brake-0.1.6.tgz", + "integrity": "sha512-V3j0HTIs70OOxRpbqT0bWVdrmP86s6N8TBPw/WyKJqdJ2Fwh3CuMmlO83uIIDpqik6m1Io9fT6qwNn69EqvXYA==", + "dev": true, + "license": "MIT", + "workspaces": [ + ".", + "site", + "utils/*" + ] }, - "fast-deep-equal": { + "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, - "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" } }, - "fast-json-stable-stringify": { + "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, - "fast-levenshtein": { + "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, - "fastest-levenshtein": { + "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "dev": true, - "requires": { + "license": "ISC", + "dependencies": { "reusify": "^1.0.4" } }, - "fecha": { + "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "dev": true + "dev": true, + "license": "MIT" }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, - "requires": { - "flat-cache": "^3.0.4" + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "devOptional": true, - "requires": { + "license": "MIT", + "dependencies": { "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "find-up": { + "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "flat": { + "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } }, - "flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" } }, - "flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" }, - "fn.name": { + "node_modules/fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "dev": true + "dev": true, + "license": "MIT" }, - "fs-readdir-recursive": { + "node_modules/fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==" + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "license": "MIT" }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" }, - "fsevents": { + "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "optional": true + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "function-bind": { + "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "gensync": { + "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "glob": { + "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-parent": { + "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "devOptional": true, - "requires": { + "license": "ISC", + "dependencies": { "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "glob-to-regexp": { + "node_modules/glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + "node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "google-closure-compiler": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20230802.0.0.tgz", - "integrity": "sha512-o2fYoc8lqOBdhm95Ick0vWrtwH2Icd5yLZhbTcQ0T7NfGiBepYvx1BB63hR8ebgzEZemz9Fh+O6Kg/3Mjm28ww==", - "requires": { + "node_modules/google-closure-compiler": { + "version": "20240317.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20240317.0.0.tgz", + "integrity": "sha512-PlC5aU2vwsypKbxyFNXOW4psDZfhDoOr2dCwuo8VcgQji+HVIgRi2lviO66x2SfTi0ilm3kI6rq/RSdOMFczcQ==", + "license": "Apache-2.0", + "dependencies": { "chalk": "4.x", - "google-closure-compiler-java": "^20230802.0.0", - "google-closure-compiler-linux": "^20230802.0.0", - "google-closure-compiler-osx": "^20230802.0.0", - "google-closure-compiler-windows": "^20230802.0.0", + "google-closure-compiler-java": "^20240317.0.0", "minimist": "1.x", "vinyl": "2.x", "vinyl-sourcemaps-apply": "^0.2.0" }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } + "bin": { + "google-closure-compiler": "cli.js" + }, + "engines": { + "node": ">=10" + }, + "optionalDependencies": { + "google-closure-compiler-linux": "^20240317.0.0", + "google-closure-compiler-osx": "^20240317.0.0", + "google-closure-compiler-windows": "^20240317.0.0" } }, - "google-closure-compiler-java": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20230802.0.0.tgz", - "integrity": "sha512-PWKLMLwj7pR/U0yYbiy649LLqAscu+F1gyY4Y/jK6CmSLb8cIJbL8BTJd00828TzTNfWnYwxbkcQw0y9C2YsGw==" + "node_modules/google-closure-compiler-java": { + "version": "20240317.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20240317.0.0.tgz", + "integrity": "sha512-oWURPChjcCrVfiQOuVtpSoUJVvtOYo41JGEQ2qtArsTGmk/DpWh40vS6hitwKRM/0YzJX/jYUuyt9ibuXXJKmg==", + "license": "Apache-2.0" }, - "google-closure-compiler-linux": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20230802.0.0.tgz", - "integrity": "sha512-F13U4iSXiWeGtHOFS25LVem1s6zI+pJvXVPVR7zSib5ppoUJ0JXnABJQezUR3FnpxmnkALG4oIGW0syH9zPLZA==", - "optional": true + "node_modules/google-closure-compiler-linux": { + "version": "20240317.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20240317.0.0.tgz", + "integrity": "sha512-dYLtcbbJdbbBS0lTy9SzySdVv/aGkpyTekQiW4ADhT/i1p1b4r0wQTKj6kpVVmFvbZ6t9tW/jbXc9EXXNUahZw==", + "cpu": [ + "x32", + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ] }, - "google-closure-compiler-osx": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20230802.0.0.tgz", - "integrity": "sha512-ANAi/ux92Tt+Na7vFDLeK2hRzotjC5j+nxoPtE0OcuNcbjji5dREKoJxkq7r0YwRTCzAFZszK5ip/NPdTOdCEg==", - "optional": true + "node_modules/google-closure-compiler-osx": { + "version": "20240317.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20240317.0.0.tgz", + "integrity": "sha512-0mABwjD4HP11rikFd8JRIb9OgPqn9h3o3wS0otufMfmbwS7zRpnnoJkunifhORl3VoR1gFm6vcTC9YziTEFdOw==", + "cpu": [ + "x32", + "x64", + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] }, - "google-closure-compiler-windows": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20230802.0.0.tgz", - "integrity": "sha512-ZQPujoNiiUyTGl8zEGR/0yAygWnbMtX/NQ/S/EHVgq5nmYkvDEVuiVbgpPAmO9lzBTq0hvUTRRATZbTU2ISxgA==", - "optional": true + "node_modules/google-closure-compiler-windows": { + "version": "20240317.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20240317.0.0.tgz", + "integrity": "sha512-fTueVFzNOWURFlXZmrFkAB7yA+jzpA2TeDOYeBEFwVlVGHwi8PV3Q9vCIWlbkE8wLpukKEg5wfRHYrLwVPINCA==", + "cpu": [ + "x32", + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ] }, - "graceful-fs": { + "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "dev": true, + "license": "ISC" }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "requires": { + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "html-minifier-terser": { + "node_modules/html-minifier-terser": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", - "requires": { + "license": "MIT", + "dependencies": { "camel-case": "^4.1.2", "clean-css": "~5.3.2", "commander": "^10.0.0", @@ -7723,619 +4081,945 @@ "relateurl": "^0.2.7", "terser": "^5.15.1" }, - "dependencies": { - "commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" - } + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" } }, - "ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "engines": { + "node": ">=14" + } }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "requires": { + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "imurmurhash": { + "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, - "interpret": { + "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } }, - "is-arrayish": { + "node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "is-binary-path": { + "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", "optional": true, - "requires": { + "dependencies": { "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "requires": { - "hasown": "^2.0.0" + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "is-glob": { + "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "devOptional": true, - "requires": { + "license": "MIT", + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-number": { + "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } }, - "is-plain-object": { + "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-stream": { + "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "isarray": { + "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, - "isobject": { + "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "jest-worker": { + "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": ">= 10.13.0" } }, - "js-tokens": { + "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, - "js-yaml": { + "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" } }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "json-buffer": { + "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "json-parse-even-better-errors": { + "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "license": "MIT" }, - "json-schema-traverse": { + "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, - "json-stable-stringify-without-jsonify": { + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, - "json5": { + "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } }, - "keyv": { + "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "json-buffer": "3.0.1" } }, - "kind-of": { + "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "kuler": { + "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "dev": true + "dev": true, + "license": "MIT" }, - "levn": { + "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, - "loader-runner": { + "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } }, - "locate-path": { + "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "lodash.debounce": { + "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, - "lodash.merge": { + "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "logform": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", - "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" } }, - "lower-case": { + "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^2.0.3" } }, - "lru-cache": { + "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { + "dependencies": { "yallist": "^3.0.2" } }, - "make-dir": { + "node_modules/make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "requires": { + "license": "MIT", + "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" } }, - "merge-stream": { + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, - "merge2": { + "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "requires": { - "braces": "^3.0.2", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "mime-db": { + "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { + "license": "ISC", + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "ms": { + "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } }, - "natural-compare": { + "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, - "neo-async": { + "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "dev": true, + "license": "MIT" }, - "no-case": { + "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "requires": { + "license": "MIT", + "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, - "node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" }, - "normalize-path": { + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "optional": true + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { + "license": "ISC", + "dependencies": { "wrappy": "1" } }, - "one-time": { + "node_modules/one-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "fn.name": "1.x.x" } }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", + "license": "MIT", + "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" } }, - "p-limit": { + "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { + "node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-try": { + "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "param-case": { + "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "requires": { + "license": "MIT", + "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, - "parent-module": { + "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "pascal-case": { + "node_modules/pascal-case": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "requires": { + "license": "MIT", + "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, - "path-exists": { + "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" }, - "picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, - "picomatch": { + "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "pify": { + "node_modules/pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "requires": { - "find-up": "^4.0.0" + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } + { + "type": "github", + "url": "https://github.com/sponsors/ai" } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, - "prelude-ls": { + "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } }, - "prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", - "dev": true + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "process-nextick-args": { + "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" }, - "punycode": { + "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "queue-microtask": { + "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "randombytes": { + "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "safe-buffer": "^5.1.0" } }, - "readable-stream": { + "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "requires": { + "license": "MIT", + "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", @@ -8345,490 +5029,955 @@ "util-deprecate": "~1.0.1" } }, - "readdirp": { + "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", "optional": true, - "requires": { + "dependencies": { "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "rechoir": { + "node_modules/rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" } }, - "regenerate": { + "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" }, - "regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "requires": { + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "license": "MIT", + "dependencies": { "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" } }, - "regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "requires": { - "@babel/regjsgen": "^0.8.0", + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "license": "MIT", + "dependencies": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" } }, - "regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "requires": { - "jsesc": "~0.5.0" - }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "license": "BSD-2-Clause", "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" - } + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" } }, - "relateurl": { + "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } }, - "remove-trailing-separator": { + "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==" + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "license": "ISC" }, - "replace-ext": { + "node_modules/replace-ext": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==" - }, - "resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "requires": { - "is-core-module": "^2.13.0", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-cwd": { + "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "resolve-from": "^5.0.0" }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, - "resolve-from": { + "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "reusify": { + "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/rollup": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, - "requires": { - "glob": "^7.1.3" + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, - "requires": { - "queue-microtask": "^1.2.2" + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "dev": true - }, - "schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } + "license": "MIT" }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } }, - "serialize-javascript": { + "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, - "requires": { + "license": "BSD-3-Clause", + "dependencies": { "randombytes": "^2.1.0" } }, - "shallow-clone": { + "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" } }, - "shebang-command": { + "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "simple-swizzle": { + "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "is-arrayish": "^0.3.1" } }, - "slash": { + "node_modules/slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "source-map-support": { + "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "requires": { + "license": "MIT", + "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, - "stack-trace": { + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } }, - "string_decoder": { + "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { + "license": "MIT", + "dependencies": { "safe-buffer": "~5.1.0" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "supports-preserve-symlinks-flag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true + "node_modules/tapable": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } }, - "terser": { - "version": "5.26.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz", - "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", - "requires": { + "node_modules/terser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "license": "BSD-2-Clause", + "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - } + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" } }, - "terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "node_modules/terser-webpack-plugin": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.20", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, - "text-hex": { + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "dev": true + "dev": true, + "license": "MIT" }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "to-regex-range": { + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "devOptional": true, - "requires": { + "license": "MIT", + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "triple-beam": { + "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, - "type-check": { + "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", - "dev": true + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "unicode-match-property-ecmascript": { + "node_modules/unicode-match-property-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "requires": { + "license": "MIT", + "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "unicode-property-aliases-ecmascript": { + "node_modules/unicode-property-aliases-ecmascript": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", - "requires": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "uri-js": { + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "license": "BSD-2-Clause", + "dependencies": { "punycode": "^2.1.0" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, - "vinyl": { + "node_modules/vinyl": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "requires": { + "license": "MIT", + "dependencies": { "clone": "^2.1.1", "clone-buffer": "^1.0.0", "clone-stats": "^1.0.0", "cloneable-readable": "^1.0.0", "remove-trailing-separator": "^1.0.1", "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "vinyl-sourcemaps-apply": { + "node_modules/vinyl-sourcemaps-apply": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", - "requires": { + "license": "ISC", + "dependencies": { "source-map": "^0.5.1" - }, + } + }, + "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vite": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz", + "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", + "dev": true, + "license": "MIT", "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true } } }, - "watchpack": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", - "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" } }, - "webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "node_modules/webpack": { + "version": "5.102.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.0.tgz", + "integrity": "sha512-hUtqAR3ZLVEYDEABdBioQCIqSoguHbFn1K7WlPPWSuXmx0031BD73PSE35jKyftdSh4YLDoQNgK4pqBt5Q82MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.24.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.3", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -8838,170 +5987,274 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "dependencies": { - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "schema-utils": "^4.3.2", + "tapable": "^2.2.3", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.4", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true } } }, - "webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "node_modules/webpack-cli": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, - "requires": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", "colorette": "^2.0.14", - "commander": "^10.0.1", + "commander": "^12.1.0", "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", + "envinfo": "^7.14.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^3.1.1", "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" + "webpack-merge": "^6.0.1" }, - "dependencies": { - "commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.82.0" + }, + "peerDependenciesMeta": { + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true } } }, - "webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "node_modules/webpack-cli/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", - "wildcard": "^2.0.0" + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" } }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true + "node_modules/webpack-sources": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "license": "ISC", + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "wildcard": { + "node_modules/wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "winston": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", - "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.4.0", + "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.7.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" } }, - "winston-transport": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", - "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", "dev": true, - "requires": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", "dependencies": { - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, - "ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, - "requires": {} + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } }, - "yallist": { + "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 33e1dac516154..62a5ea3b0ee63 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,32 @@ { "private": true, "devDependencies": { - "es-check": "^7.2.1", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "prettier": "^3.3.2", - "source-map": "0.7.4", - "typescript": "^5.5.2", - "webpack": "^5.92.1", - "webpack-cli": "^5.1.4", - "ws": "^8.17.1" + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.36.0", + "es-check": "^9.4.4", + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", + "globals": "^16.4.0", + "prettier": "^3.6.2", + "rollup": "^4.52.3", + "source-map": "0.7.6", + "typescript": "^5.9.3", + "vite": "^7.1.7", + "webpack": "^5.102.0", + "webpack-cli": "^6.0.1", + "ws": "^8.18.3" }, "dependencies": { - "@babel/cli": "^7.24.7", - "@babel/core": "^7.24.7", - "@babel/preset-env": "^7.24.7", - "acorn": "^8.12.0", - "google-closure-compiler": "20230802.0.0", + "@babel/cli": "^7.28.3", + "@babel/core": "^7.28.4", + "@babel/preset-env": "^7.28.3", + "acorn": "^8.15.0", + "google-closure-compiler": "20240317.0.0", "html-minifier-terser": "7.2.0" }, "scripts": { - "lint": "eslint --ext .js --ext .mjs .", - "fmt": "prettier --write src/*.mjs tools/*.mjs", - "check": "prettier --check src/*.mjs tools/*.mjs" + "lint": "eslint .", + "fmt": "prettier --write src/*.mjs tools/*.mjs --ignore-path src/pthread_esm_startup.mjs", + "check": "prettier --check src/*.mjs tools/*.mjs --ignore-path src/pthread_esm_startup.mjs" } } diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000..ba2a20533640a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,116 @@ +[project] +requires-python = ">=3.8" + +[tool.ruff] +exclude = [ + "./cache/", + "./node_modules/", + "./site/source/_themes/", + "./site/source/conf.py", + "./system/lib/", + "./test/third_party/", + "./third_party/", + "./tools/filelock.py", + "./tools/scons/", + ".git", +] + +lint.select = [ + "ARG", + "ASYNC", + "B", + "C4", + "C90", + "COM", + "E", + "F", + "PERF", + "PIE", + "PL", + "UP", + "W", + "YTT", +] +lint.external = [ "D" ] +lint.ignore = [ + "B011", # See https://github.com/PyCQA/flake8-bugbear/issues/66 + "B023", + "B026", + "E402", + "E501", + "E721", + "E741", + "PERF203", + "PERF401", + "PLR1704", + "PLR5501", + "PLW0602", + "PLW0603", + "PLW1510", + "PLW2901", + "UP030", # TODO + "UP031", # TODO + "UP032", # TODO +] +lint.per-file-ignores."emrun.py" = [ "PLE0704" ] +lint.per-file-ignores."tools/ports/*.py" = [ "ARG001", "ARG005" ] +lint.per-file-ignores."test/other/ports/*.py" = [ "ARG001" ] +lint.per-file-ignores."test/parallel_testsuite.py" = [ "ARG002" ] +lint.per-file-ignores."test/test_benchmark.py" = [ "ARG002" ] +lint.mccabe.max-complexity = 51 # Recommended: 10 +lint.pylint.allow-magic-value-types = [ + "bytes", + "float", + "int", + "str", +] +lint.pylint.max-args = 15 # Recommended: 5 +lint.pylint.max-branches = 50 # Recommended: 12 +lint.pylint.max-returns = 16 # Recommended: 6 +lint.pylint.max-statements = 142 # Recommended: 50 + +[tool.coverage.run] +source = [ "." ] +omit = [ + "./test/*", + "./third_party/*", + "./tools/emcoverage.py", + "test.py", +] + +[tool.mypy] +mypy_path = "third_party/,third_party/ply,third_party/websockify" +files = [ "." ] +exclude = ''' +(?x)( +cache | +third_party | +conf\.py | +emrun\.py | +site/source/_themes/ | +tools/scons/site_scons/site_tools/emscripten/__init__\.py | +tools/maint/create_dom_pk_codes.py | +site/source/get_wiki\.py | +test/parse_benchmark_output\.py +)''' + +[[tool.mypy.overrides]] +module = [ + "tools.webidl_binder", + "tools.toolchain_profiler", + "tools.filelock", + "tools.find_bigvars", + "leb128", + "ply.*", +] +ignore_errors = true + +[[tool.mypy.overrides]] +module = ["psutil", "win32con", "win32gui", "win32process"] +ignore_missing_imports = true + +[tool.deadcode] +exclude = ["out", "cache", "third_party", "test/third_party", "node_modules", "site/source/_themes", "site/source/conf.py"] + +[tool.vulture] +exclude = ["out", "cache", "third_party", "test/third_party", "node_modules", "site/source/_themes", "site/source/conf.py"] diff --git a/requirements-dev.txt b/requirements-dev.txt index 267dee10ff076..082d48c83c2cc 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,36 +1,22 @@ # TODO(sbc): switch to using Pipenv since it seems like that way to go # these day managing python deps. -# These requirements are only needed for developers who want to run flake8 on +# These requirements are only needed for developers who want to run ruff on # the codebase and generate docs using Sphinx, not for users of emscripten. # Install with `pip3 install -r requirements-dev.txt` -flake8==5.0.4 -flake8-bugbear==22.9.23 -flake8-unused-arguments==0.0.11 -coverage==5.5 -mypy==0.971 -types-requests==2.27.14 -unittest-xml-reporting==3.1.0 - -# See https://github.com/emscripten-core/emscripten/issues/19785 -lxml==4.9.2 +coverage[toml]==6.5 +mypy==1.14 +psutil==7.0.0 +ruff==0.11.7 +types-requests==2.32.0.20241016 +unittest-xml-reporting==3.2.0 +deadcode==2.3.1 +vulture==2.14 # This version is mentioned in `site/source/docs/site/about.rst`. # Please keep them in sync. -sphinx==2.4.4 -# See https://github.com/emscripten-core/emscripten/issues/21590 -sphinxcontrib-applehelp<=1.0.4 -sphinxcontrib-devhelp<=1.0.2 -sphinxcontrib-htmlhelp<=2.0.0 -sphinxcontrib-serializinghtml<=1.1.5 -sphinxcontrib-qthelp<=1.0.3 -alabaster<=0.7.12 -pygments==2.17.2 -# See https://github.com/readthedocs/readthedocs.org/issues/9038 -jinja2<3.1 - -# Pin docutils because newer versions are not compatible with sphinx 2.4.4 -docutils==0.17.1 +sphinx==7.1.2 +sphinxcontrib-jquery==4.0 # Needed by test/test_sockets.py -websockify==0.10.0 +websockify==0.12.0 diff --git a/site/Makefile b/site/Makefile index 8ac46ed3d9a85..c83abbd936f66 100644 --- a/site/Makefile +++ b/site/Makefile @@ -4,13 +4,13 @@ # You can set these variables from the command line. SPHINXOPTS = -W SPHINXBUILD = sphinx-build -SPHINXVERSION = 2.4.4 +SPHINXVERSION = 7.1.2 PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx version 2.4.4 installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx version 7.1.2 installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif ifneq ($(shell $(SPHINXBUILD) --version), $(SPHINXBUILD) $(SPHINXVERSION)) diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/__init__.py b/site/source/_themes/emscripten_sphinx_rtd_theme/__init__.py index 6630f217a154b..7938cf1c900f6 100644 --- a/site/source/_themes/emscripten_sphinx_rtd_theme/__init__.py +++ b/site/source/_themes/emscripten_sphinx_rtd_theme/__init__.py @@ -3,20 +3,109 @@ # University of Illinois/NCSA Open Source License. Both these licenses can be # found in the LICENSE file. -"""Sphinx ReadTheDocs theme. +""" +Sphinx Read the Docs theme. From https://github.com/ryan-roemer/sphinx-bootstrap-theme. - """ + import os +from os import path +from sys import version_info as python_version -VERSION = (0, 1, 6) +from sphinx import version_info as sphinx_version +from sphinx.locale import _ +from sphinx.util.logging import getLogger -__version__ = ".".join(str(v) for v in VERSION) + +__version__ = '3.0.1' __version_full__ = __version__ +logger = getLogger(__name__) + def get_html_theme_path(): """Return list of HTML theme paths.""" - cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + logger.warning( + _('Calling get_html_theme_path is deprecated. If you are calling it to define html_theme_path, you are safe to remove that code.') + ) + + cur_dir = path.abspath(path.dirname(path.dirname(__file__))) return cur_dir + + +def config_initiated(app, config): + theme_options = config.html_theme_options or {} + if theme_options.get('canonical_url'): + logger.warning( + _('The canonical_url option is deprecated, use the html_baseurl option from Sphinx instead.') + ) + + if theme_options.get("analytics_id"): + logger.warning( + _('The analytics_id option is deprecated, use the sphinxcontrib-googleanalytics extension instead.') + ) + + if theme_options.get("analytics_anonymize_ip"): + logger.warning( + _('The analytics_anonymize_ip option is deprecated, use the sphinxcontrib-googleanalytics extension instead.') + ) + + if "extra_css_files" in config.html_context: + logger.warning( + _('The extra_css_file option is deprecated, use the html_css_files option from Sphinx instead.') + ) + + +def extend_html_context(app, pagename, templatename, context, doctree): + # Add ``sphinx_version_info`` tuple for use in Jinja templates + context['sphinx_version_info'] = sphinx_version + + # Inject all the Read the Docs environment variables in the context: + # https://docs.readthedocs.io/en/stable/reference/environment-variables.html + context['READTHEDOCS'] = os.environ.get("READTHEDOCS", False) == "True" + if context['READTHEDOCS']: + for key, value in os.environ.items(): + if key.startswith("READTHEDOCS_"): + context[key] = value + + + +# See http://www.sphinx-doc.org/en/stable/theming.html#distribute-your-theme-as-a-python-package +def setup(app): + if python_version[0] < 3: + logger.error("Python 2 is not supported with sphinx_rtd_theme, update to Python 3.") + + app.require_sphinx('6.0') + if app.config.html4_writer: + logger.error("'html4_writer' is not supported with sphinx_rtd_theme.") + + # Since Sphinx 6, jquery isn't bundled anymore and we need to ensure that + # the sphinxcontrib-jquery extension is enabled. + # See: https://dev.readthedocs.io/en/latest/design/sphinx-jquery.html + if sphinx_version >= (6, 0, 0): + # Documentation of Sphinx guarantees that an extension is added and + # enabled at most once. + # See: https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx.application.Sphinx.setup_extension + app.setup_extension("sphinxcontrib.jquery") + # However, we need to call the extension's callback since setup_extension doesn't do it + # See: https://github.com/sphinx-contrib/jquery/issues/23 + from sphinxcontrib.jquery import add_js_files as jquery_add_js_files + jquery_add_js_files(app, app.config) + + # Register the theme that can be referenced without adding a theme path + app.add_html_theme('sphinx_rtd_theme', path.abspath(path.dirname(__file__))) + + # Add Sphinx message catalog for newer versions of Sphinx + # See http://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx.application.Sphinx.add_message_catalog + rtd_locale_path = path.join(path.abspath(path.dirname(__file__)), 'locale') + app.add_message_catalog('sphinx', rtd_locale_path) + app.connect('config-inited', config_initiated) + + # sphinx emits the permalink icon for headers, so choose one more in keeping with our theme + app.config.html_permalinks_icon = "\uf0c1" + + # Extend the default context when rendering the templates. + app.connect("html-page-context", extend_html_context) + + return {'parallel_read_safe': True, 'parallel_write_safe': True} diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/layout.html b/site/source/_themes/emscripten_sphinx_rtd_theme/layout.html index e8a0e17931b30..1eac43b058f78 100644 --- a/site/source/_themes/emscripten_sphinx_rtd_theme/layout.html +++ b/site/source/_themes/emscripten_sphinx_rtd_theme/layout.html @@ -6,203 +6,255 @@ {%- else %} {%- set titlesuffix = "" %} {%- endif %} - +{%- set lang_attr = 'en' if language == None else (language | replace('_', '-')) %} + +{# Build sphinx_version_info tuple from sphinx_version string in pure Jinja #} +{%- set (_ver_major, _ver_minor) = (sphinx_version.split('.') | list)[:2] | map('int') -%} +{%- set sphinx_version_info = (_ver_major, _ver_minor, -1) -%} + - - += (7, 2) %} data-content_root="{{ content_root }}"{% endif %}> - - - {% block htmltitle %} + + {%- if READTHEDOCS and not embedded %} + + {%- endif %} + {{- metatags }} + + {%- block htmltitle %} {{ title|striptags|e }}{{ titlesuffix }} - {% endblock %} + {%- endblock -%} - {# FAVICON #} - {% if favicon %} - - {% endif %} + {#- CSS #} + {%- for css_file in css_files %} + {%- if css_file|attr("filename") %} + {{ css_tag(css_file) }} + {%- else %} + + {%- endif %} + {%- endfor %} - {# CSS #} - + {# + "extra_css_files" is an undocumented Read the Docs theme specific option. + There is no need to check for ``|attr("filename")`` here because it's always a string. + Note that this option should be removed in favor of regular ``html_css_files``: + https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_css_files + #} + {%- for css_file in extra_css_files %} + + {%- endfor -%} - {# OPENSEARCH #} - {% if not embedded %} - {% if use_opensearch %} - - {% endif %} + {#- FAVICON #} + {%- if favicon_url %} + + {%- endif %} - {% endif %} + {#- CANONICAL URL (deprecated) #} + {%- if theme_canonical_url and not pageurl %} + + {%- endif -%} - {# RTD hosts this file, so just load on non RTD builds #} - {% if not READTHEDOCS %} - - {% endif %} + {#- CANONICAL URL #} + {%- if pageurl %} + + {%- endif -%} - {% for cssfile in css_files %} - - {% endfor %} + {#- JAVASCRIPTS #} + {%- block scripts %} + {%- if not embedded %} + {%- for scriptfile in script_files %} + {{ js_tag(scriptfile) }} + {%- endfor %} + + + {%- if READTHEDOCS or DEBUG %} + + {%- endif %} + + {#- OPENSEARCH #} + {%- if use_opensearch %} + + {%- endif %} + {%- endif %} + {%- endblock %} {%- block linktags %} {%- if hasdoc('about') %} - + {%- endif %} {%- if hasdoc('genindex') %} - + {%- endif %} {%- if hasdoc('search') %} - + {%- endif %} {%- if hasdoc('copyright') %} - - {%- endif %} - - {%- if parents %} - + {%- endif %} {%- if next %} - + {%- endif %} {%- if prev %} - + {%- endif %} {%- endblock %} {%- block extrahead %} {% endblock %} - - {# Keep modernizr in head - http://modernizr.com/docs/#installing #} - - - - +
- + + {%- block extrabody %} {% endblock %}
- + {#- SIDE NAV, TOGGLES ON MOBILE #} + - +
- {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} -
-
- {% include "versions.html" %} - - {% if not embedded %} - - - {%- for scriptfile in script_files %} - - {%- endfor %} + {% include "versions.html" -%} - {% endif %} - - {# RTD hosts this file, so just load on non RTD builds #} - {% if not READTHEDOCS %} - - {% endif %} - - {# STICKY NAVIGATION #} - {% if theme_sticky_navigation %} - - {% endif %} + + {#- Do not conflict with RTD insertion of analytics script #} + {%- if not READTHEDOCS %} + {%- if theme_analytics_id %} + + + + + {%- endif %} + {%- endif %} {%- block footer %} {% endblock %}
diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/search.html b/site/source/_themes/emscripten_sphinx_rtd_theme/search.html index e3aa9b5c6e75b..e71589509b133 100644 --- a/site/source/_themes/emscripten_sphinx_rtd_theme/search.html +++ b/site/source/_themes/emscripten_sphinx_rtd_theme/search.html @@ -1,50 +1,56 @@ {# - basic/search.html - ~~~~~~~~~~~~~~~~~ + basic/search.html + ~~~~~~~~~~~~~~~~~ - Template for the search page. + Template for the search page. - :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see https://github.com/sphinx-doc/sphinx/blob/master/LICENSE for details. #} {%- extends "layout.html" %} {% set title = _('Search') %} -{% set script_files = script_files + ['_static/searchtools.js'] %} -{% block footer %} - - {# this is used when loading the search index using $.ajax fails, - such as on Chrome for documents on localhost #} - +{% set display_vcs_links = False %} +{%- block scripts %} {{ super() }} + + +{%- endblock %} +{% block footer %} + +{# this is used when loading the search index using $.ajax fails, + such as on Chrome for documents on localhost #} + +{{ super() }} {% endblock %} {% block body %} - + - {% if search_performed %} -

{{ _('Search Results') }}

- {% if not search_results %} -

{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

- {% endif %} +{% if search_performed %} + {# Translators: Search is a noun, not a verb #} +

{{ _('Search Results') }}

+ {% if not search_results %} +

{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

{% endif %} -
- {% if search_results %} -
    - {% for href, caption, context in search_results %} -
  • - {{ caption }} -

    {{ context|e }}

    -
  • - {% endfor %} -
- {% endif %} -
-{% endblock %} +{% endif %} +
+{% if search_results %} +
    + {% for href, caption, context in search_results %} +
  • + {{ caption }} +

    {{ context|e }}

    +
  • + {% endfor %} +
+{% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/searchbox.html b/site/source/_themes/emscripten_sphinx_rtd_theme/searchbox.html index 24418d32bcb05..a16ab2c899afa 100644 --- a/site/source/_themes/emscripten_sphinx_rtd_theme/searchbox.html +++ b/site/source/_themes/emscripten_sphinx_rtd_theme/searchbox.html @@ -1,7 +1,9 @@ +{%- if 'singlehtml' not in builder %}
-
- + +
+{%- endif %} diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/badge_only.css b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/badge_only.css index 4868a00277d1c..88ba55b965c28 100644 --- a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/badge_only.css +++ b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/badge_only.css @@ -1 +1 @@ -.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:"\f02d"}.icon-book:before{content:"\f02d"}.fa-caret-down:before{content:"\f0d7"}.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px} \ No newline at end of file diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Bold.woff b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 0000000000000..6cb60000181db Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Bold.woff2 b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 0000000000000..7059e23142aae Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Regular.woff b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 0000000000000..f815f63f99da8 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Regular.woff2 b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 0000000000000..f2c76e5bda18a Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.eot b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000000..e9f60ca953f93 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.eot differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.svg b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000000000..855c845e538b6 --- /dev/null +++ b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.ttf b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000..35acda2fa1196 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.ttf differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.woff b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000000000..400014a4b06ee Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.woff differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.woff2 b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000000000..4d13fc60404b9 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold-italic.woff b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold-italic.woff new file mode 100644 index 0000000000000..88ad05b9ff413 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold-italic.woff differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold-italic.woff2 b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 0000000000000..c4e3d804b57b6 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold-italic.woff2 differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold.woff b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold.woff new file mode 100644 index 0000000000000..c6dff51f063cc Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold.woff differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold.woff2 b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold.woff2 new file mode 100644 index 0000000000000..bb195043cfc07 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-bold.woff2 differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal-italic.woff b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal-italic.woff new file mode 100644 index 0000000000000..76114bc033622 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal-italic.woff differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal-italic.woff2 b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 0000000000000..3404f37e2e312 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal-italic.woff2 differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal.woff b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal.woff new file mode 100644 index 0000000000000..ae1307ff5f4c4 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal.woff differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal.woff2 b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal.woff2 new file mode 100644 index 0000000000000..3bf9843328a63 Binary files /dev/null and b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/fonts/lato-normal.woff2 differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/theme.css b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/theme.css index a7b7eba2dc89c..9c78dc4f7f563 100644 --- a/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/theme.css +++ b/site/source/_themes/emscripten_sphinx_rtd_theme/static/css/theme.css @@ -1,394 +1,8002 @@ -*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%} - -body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:20px 0;padding:0}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical} -table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline} -.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""} -pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}} -.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1} -.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome +html { + box-sizing: border-box +} + +*, +:after, +:before { + box-sizing: inherit +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block +} + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1 +} + +[hidden], +audio:not([controls]) { + display: none +} + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100% +} + +body { + margin: 0 +} + +a:active, +a:hover { + outline: 0 +} + +abbr[title] { + border-bottom: 1px dotted +} + +b, +strong { + font-weight: 700 +} + +blockquote { + margin: 0 +} + +dfn { + font-style: italic +} + +ins { + background: #ff9; + text-decoration: none +} + +ins, +mark { + color: #000 +} + +mark { + background: #ff0; + font-style: italic; + font-weight: 700 +} + +.rst-content code, +.rst-content tt, +code, +kbd, +pre, +samp { + font-family: monospace, serif; + _font-family: courier new, monospace; + font-size: 1em +} + +pre { + white-space: pre +} + +q { + quotes: none +} + +q:after, +q:before { + content: ""; + content: none +} + +small { + font-size: 85% +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sup { + top: -.5em +} + +sub { + bottom: -.25em +} + +dl, +ol, +ul { + margin: 0; + padding: 0; + list-style: none; + list-style-image: none +} + +li { + list-style: none +} + +dd { + margin: 0 +} + +img { + border: 0; + -ms-interpolation-mode: bicubic; + vertical-align: middle; + max-width: 100% +} + +svg:not(:root) { + overflow: hidden +} + +figure, +form { + margin: 0 +} + +label { + cursor: pointer +} + +button, +input, +select, +textarea { + font-size: 100%; + margin: 0; + vertical-align: baseline; + *vertical-align: middle +} + +button, +input { + line-height: normal +} + +button, +input[type=button], +input[type=reset], +input[type=submit] { + cursor: pointer; + -webkit-appearance: button; + *overflow: visible +} + +button[disabled], +input[disabled] { + cursor: default +} + +input[type=search] { + -webkit-appearance: textfield; + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + box-sizing: content-box +} + +textarea { + resize: vertical +} + +table { + border-collapse: collapse; + border-spacing: 0 +} + +td { + vertical-align: top +} + +.chromeframe { + margin: .2em 0; + background: #ccc; + color: #000; + padding: .2em 0 +} + +.ir { + display: block; + border: 0; + text-indent: -999em; + overflow: hidden; + background-color: transparent; + background-repeat: no-repeat; + text-align: left; + direction: ltr; + *line-height: 0 +} + +.ir br { + display: none +} + +.hidden { + display: none !important; + visibility: hidden +} + +.visuallyhidden { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px +} + +.visuallyhidden.focusable:active, +.visuallyhidden.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto +} + +.invisible { + visibility: hidden +} + +.relative { + position: relative +} + +big, +small { + font-size: 100% +} + +@media print { + + body, + html, + section { + background: none !important + } + + * { + box-shadow: none !important; + text-shadow: none !important; + filter: none !important; + -ms-filter: none !important + } + + a, + a:visited { + text-decoration: underline + } + + .ir a:after, + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: "" + } + + blockquote, + pre { + page-break-inside: avoid + } + + thead { + display: table-header-group + } + + img, + tr { + page-break-inside: avoid + } + + img { + max-width: 100% !important + } + + @page { + margin: .5cm + } + + .rst-content .toctree-wrapper>p.caption, + h2, + h3, + p { + orphans: 3; + widows: 3 + } + + .rst-content .toctree-wrapper>p.caption, + h2, + h3 { + page-break-after: avoid + } +} + +.btn, +.fa:before, +.icon:before, +.rst-content .admonition, +.rst-content .admonition-title:before, +.rst-content .admonition-todo, +.rst-content .attention, +.rst-content .caution, +.rst-content .code-block-caption .headerlink:before, +.rst-content .danger, +.rst-content .eqno .headerlink:before, +.rst-content .error, +.rst-content .hint, +.rst-content .important, +.rst-content .note, +.rst-content .seealso, +.rst-content .tip, +.rst-content .warning, +.rst-content code.download span:first-child:before, +.rst-content dl dt .headerlink:before, +.rst-content h1 .headerlink:before, +.rst-content h2 .headerlink:before, +.rst-content h3 .headerlink:before, +.rst-content h4 .headerlink:before, +.rst-content h5 .headerlink:before, +.rst-content h6 .headerlink:before, +.rst-content p.caption .headerlink:before, +.rst-content p .headerlink:before, +.rst-content table>caption .headerlink:before, +.rst-content tt.download span:first-child:before, +.wy-alert, +.wy-dropdown .caret:before, +.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before, +.wy-inline-validate.wy-inline-validate-info .wy-input-context:before, +.wy-inline-validate.wy-inline-validate-success .wy-input-context:before, +.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before, +.wy-menu-vertical li.current>a button.toctree-expand:before, +.wy-menu-vertical li.on a button.toctree-expand:before, +.wy-menu-vertical li button.toctree-expand:before, +input[type=color], +input[type=date], +input[type=datetime-local], +input[type=datetime], +input[type=email], +input[type=month], +input[type=number], +input[type=password], +input[type=search], +input[type=tel], +input[type=text], +input[type=time], +input[type=url], +input[type=week], +select, +textarea { + -webkit-font-smoothing: antialiased +} + +.clearfix { + *zoom: 1 +} + +.clearfix:after, +.clearfix:before { + display: table; + content: "" +} + +.clearfix:after { + clear: both +} + +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.0.3");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.0.3") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.0.3") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.pull-left.icon{margin-right:.3em}.fa.pull-right,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before,.icon-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before,.icon-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:"\f057"}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before,.icon-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before,.icon-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:"\f0a8"}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before,.icon-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before,.icon-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .icon,.nav .fa,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .icon{display:inline}.btn .fa.fa-large,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a{color:#2980b9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:60px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:60px}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27ae60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27ae60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#e74c3c !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#e67e22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980b9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:0.5em 1em 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:0.5em}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{display:block;float:left;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{display:block;float:left;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:0.5em 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#ccc;font-size:70%;margin-top:0.3125em;font-style:italic}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e74c3c}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:0.8em;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #e74c3c}.wy-control-group.wy-control-group-error textarea{border:solid 1px #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}} - - @media screen and (max-width: 768px){.tablet-hide{display:none}} - @media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px;margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0} - - .wy-table-responsive{ - margin-bottom:24px; - max-width:100%; - overflow:auto - } - -.wy-table-responsive table{ - margin-bottom:0 !important - } - -.wy-table-responsive table td {white-space:nowrap} - -.wy-table-responsive table th{white-space:nowrap} - -a{color:#2980b9;text-decoration:none}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%;overflow-x:hidden} - -body{ - /* font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif; */ - font-family:"proxima-nova","Helvetica Neue",Arial,sans-serif; - font-weight:normal; - color:#404040; - min-height:100%; - overflow-x:hidden; - background:#edf0f2 - } - -.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980b9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27ae60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#e74c3c !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%} - -code, .rst-content tt - { - white-space:nowrap; - max-width:100%; - background:#fff; - border:solid 1px #e1e4e5; - /* font-size:75%; */ - font-size:90%; - padding:0 5px; - font-family:"Inconsolata","Consolata","Monaco",monospace;color:#e74c3c; - overflow-x:auto - } - -code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px} -.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li { - list-style:disc;margin-left:24px} - -.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px} - -/* .wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li { */ -.wy-plain-list-decimal ol li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li { - list-style:decimal;margin-left:24px - } - -/* HamishW - add to fix unordered bullets inside decimal lists */ -.rst-content ol.arabic ul li { - list-style:disc; - } - - - -.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9b59b6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:"Inconsolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:"Inconsolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#eaf2f5} - .wy-breadcrumbs li{display:inline-block} - .wy-breadcrumbs li.wy-breadcrumbs-aside { - float:right; - padding-left:5px; - font-size:0.8em; - } - - .wy-breadcrumbs li a{ - display:inline-block;padding:5px - } - - .wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block} - - @media screen and (max-width: 480px) - {.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980b9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3} - - .wy-menu-vertical li.current a{ - color:gray; - border-right:solid 1px #c9c9c9; - padding:0.4045em 2.427em - } - - .wy-menu-vertical li.current a:hover{background:#d6d6d6} - - .wy-menu-vertical li.on a { - color:#404040; - padding:0.4045em 1.618em; - font-weight:bold; - position:relative; - background:#fcfcfc; - border:none; - border-bottom:solid 1px #c9c9c9; - border-top:solid 1px #c9c9c9; - padding-left:1.618em -4px - } - - -.wy-menu-vertical li.current>a { - color:#404040; - padding:0.4045em 1.618em; - font-weight:bold; - position:relative; - background:#fcfcfc; - border:none; - border-bottom:solid 1px #c9c9c9; - border-top:solid 1px #c9c9c9; - padding-left:1.618em -4px - } - - .wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc} - .wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff} - - .wy-side-nav-search {z-index:200; - background-color:#2980b9; - text-align:center; - /* padding:0.809em; */ - /* padding-top: 0.809em;*/ - padding-right: 0.809em; - padding-bottom: 0.809em; - padding-left: 0.809em; - display:block; - color:#fcfcfc; - margin-bottom:0.809em - } - - .wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4} - - - .wy-side-nav-search img{ - display:block; - margin:auto auto 0.809em auto; - /*height:45px;*/ - /*width:45px;*/ - width:200px; - /*background-color:#2980b9;*/ - padding:5px; - /*border-radius:100%*/ - } - - .wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a { - color:#fcfcfc; - font-size:100%; - font-weight:bold; - display:inline-block; - padding-top: 4px; - padding-right: 6px; - /*padding-bottom: 4px;*/ - padding-left: 6px; - /* margin-bottom:0.809em */ - } - - .wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0} - - .wy-body-for-nav{ - /* background:left repeat-y #F1F0F0; */ - /* - background:left repeat-y rgb(97, 6, 6); - background-size:300px 1px - */ /* This is deep red colour - removed */ - /* background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC); */ - - } - - .wy-grid-for-nav { - /* position:absolute; */ - position:relative; /* Make left column full length */ - width:100%; - height:100% - } - - .wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200} - - .wy-nav-top{ - display:none; - background:#2980b9; - color:#fff; - padding:0.4045em 0.809em; - position:relative; - line-height:50px; - text-align:center; - font-size:100%; - *zoom:1} - - .wy-nav-top:before,.wy-nav-top:after{display:table;content:""} - - .wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold} - - .wy-nav-top img{ - margin-right:12px; - /*height:45px; */ - /*width:45px;*/ - width:200px; - background-color:#2980b9; - padding:5px; - /*border-radius:100%*/ - } - - .wy-nav-top i{font-size:30px;float:left;cursor:pointer} - .wy-nav-content-wrap{ - margin-left:300px; - /* background:#fcfcfc; */ - min-height:100% - } - - .wy-nav-content{ + */ +@font-face { + font-family: FontAwesome; + src: url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713); + src: url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"), url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"), url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"), url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"), url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg"); + font-weight: 400; + font-style: normal +} + +.fa, +.icon, +.rst-content .admonition-title, +.rst-content .code-block-caption .headerlink, +.rst-content .eqno .headerlink, +.rst-content code.download span:first-child, +.rst-content dl dt .headerlink, +.rst-content h1 .headerlink, +.rst-content h2 .headerlink, +.rst-content h3 .headerlink, +.rst-content h4 .headerlink, +.rst-content h5 .headerlink, +.rst-content h6 .headerlink, +.rst-content p.caption .headerlink, +.rst-content p .headerlink, +.rst-content table>caption .headerlink, +.rst-content tt.download span:first-child, +.wy-menu-vertical li.current>a button.toctree-expand, +.wy-menu-vertical li.on a button.toctree-expand, +.wy-menu-vertical li button.toctree-expand { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale +} + +.fa-lg { + font-size: 1.33333em; + line-height: .75em; + vertical-align: -15% +} + +.fa-2x { + font-size: 2em +} + +.fa-3x { + font-size: 3em +} + +.fa-4x { + font-size: 4em +} + +.fa-5x { + font-size: 5em +} + +.fa-fw { + width: 1.28571em; + text-align: center +} + +.fa-ul { + padding-left: 0; + margin-left: 2.14286em; + list-style-type: none +} + +.fa-ul>li { + position: relative +} + +.fa-li { + position: absolute; + left: -2.14286em; + width: 2.14286em; + top: .14286em; + text-align: center +} + +.fa-li.fa-lg { + left: -1.85714em +} + +.fa-border { + padding: .2em .25em .15em; + border: .08em solid #eee; + border-radius: .1em +} + +.fa-pull-left { + float: left +} + +.fa-pull-right { + float: right +} + +.fa-pull-left.icon, +.fa.fa-pull-left, +.rst-content .code-block-caption .fa-pull-left.headerlink, +.rst-content .eqno .fa-pull-left.headerlink, +.rst-content .fa-pull-left.admonition-title, +.rst-content code.download span.fa-pull-left:first-child, +.rst-content dl dt .fa-pull-left.headerlink, +.rst-content h1 .fa-pull-left.headerlink, +.rst-content h2 .fa-pull-left.headerlink, +.rst-content h3 .fa-pull-left.headerlink, +.rst-content h4 .fa-pull-left.headerlink, +.rst-content h5 .fa-pull-left.headerlink, +.rst-content h6 .fa-pull-left.headerlink, +.rst-content p .fa-pull-left.headerlink, +.rst-content table>caption .fa-pull-left.headerlink, +.rst-content tt.download span.fa-pull-left:first-child, +.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand, +.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand, +.wy-menu-vertical li button.fa-pull-left.toctree-expand { + margin-right: .3em +} + +.fa-pull-right.icon, +.fa.fa-pull-right, +.rst-content .code-block-caption .fa-pull-right.headerlink, +.rst-content .eqno .fa-pull-right.headerlink, +.rst-content .fa-pull-right.admonition-title, +.rst-content code.download span.fa-pull-right:first-child, +.rst-content dl dt .fa-pull-right.headerlink, +.rst-content h1 .fa-pull-right.headerlink, +.rst-content h2 .fa-pull-right.headerlink, +.rst-content h3 .fa-pull-right.headerlink, +.rst-content h4 .fa-pull-right.headerlink, +.rst-content h5 .fa-pull-right.headerlink, +.rst-content h6 .fa-pull-right.headerlink, +.rst-content p .fa-pull-right.headerlink, +.rst-content table>caption .fa-pull-right.headerlink, +.rst-content tt.download span.fa-pull-right:first-child, +.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand, +.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand, +.wy-menu-vertical li button.fa-pull-right.toctree-expand { + margin-left: .3em +} + +.pull-right { + float: right +} + +.pull-left { + float: left +} + +.fa.pull-left, +.pull-left.icon, +.rst-content .code-block-caption .pull-left.headerlink, +.rst-content .eqno .pull-left.headerlink, +.rst-content .pull-left.admonition-title, +.rst-content code.download span.pull-left:first-child, +.rst-content dl dt .pull-left.headerlink, +.rst-content h1 .pull-left.headerlink, +.rst-content h2 .pull-left.headerlink, +.rst-content h3 .pull-left.headerlink, +.rst-content h4 .pull-left.headerlink, +.rst-content h5 .pull-left.headerlink, +.rst-content h6 .pull-left.headerlink, +.rst-content p .pull-left.headerlink, +.rst-content table>caption .pull-left.headerlink, +.rst-content tt.download span.pull-left:first-child, +.wy-menu-vertical li.current>a button.pull-left.toctree-expand, +.wy-menu-vertical li.on a button.pull-left.toctree-expand, +.wy-menu-vertical li button.pull-left.toctree-expand { + margin-right: .3em +} + +.fa.pull-right, +.pull-right.icon, +.rst-content .code-block-caption .pull-right.headerlink, +.rst-content .eqno .pull-right.headerlink, +.rst-content .pull-right.admonition-title, +.rst-content code.download span.pull-right:first-child, +.rst-content dl dt .pull-right.headerlink, +.rst-content h1 .pull-right.headerlink, +.rst-content h2 .pull-right.headerlink, +.rst-content h3 .pull-right.headerlink, +.rst-content h4 .pull-right.headerlink, +.rst-content h5 .pull-right.headerlink, +.rst-content h6 .pull-right.headerlink, +.rst-content p .pull-right.headerlink, +.rst-content table>caption .pull-right.headerlink, +.rst-content tt.download span.pull-right:first-child, +.wy-menu-vertical li.current>a button.pull-right.toctree-expand, +.wy-menu-vertical li.on a button.pull-right.toctree-expand, +.wy-menu-vertical li button.pull-right.toctree-expand { + margin-left: .3em +} + +.fa-spin { + -webkit-animation: fa-spin 2s linear infinite; + animation: fa-spin 2s linear infinite +} + +.fa-pulse { + -webkit-animation: fa-spin 1s steps(8) infinite; + animation: fa-spin 1s steps(8) infinite +} + +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg) + } + + to { + -webkit-transform: rotate(359deg); + transform: rotate(359deg) + } +} + +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg) + } + + to { + -webkit-transform: rotate(359deg); + transform: rotate(359deg) + } +} + +.fa-rotate-90 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg) +} + +.fa-rotate-180 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg) +} + +.fa-rotate-270 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg) +} + +.fa-flip-horizontal { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; + -webkit-transform: scaleX(-1); + -ms-transform: scaleX(-1); + transform: scaleX(-1) +} + +.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scaleY(-1); + -ms-transform: scaleY(-1); + transform: scaleY(-1) +} + +:root .fa-flip-horizontal, +:root .fa-flip-vertical, +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270 { + filter: none +} + +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle +} + +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center +} + +.fa-stack-1x { + line-height: inherit +} + +.fa-stack-2x { + font-size: 2em +} + +.fa-inverse { + color: #fff +} + +.fa-glass:before { + content: "" +} + +.fa-music:before { + content: "" +} + +.fa-search:before, +.icon-search:before { + content: "" +} + +.fa-envelope-o:before { + content: "" +} + +.fa-heart:before { + content: "" +} + +.fa-star:before { + content: "" +} + +.fa-star-o:before { + content: "" +} + +.fa-user:before { + content: "" +} + +.fa-film:before { + content: "" +} + +.fa-th-large:before { + content: "" +} + +.fa-th:before { + content: "" +} + +.fa-th-list:before { + content: "" +} + +.fa-check:before { + content: "" +} + +.fa-close:before, +.fa-remove:before, +.fa-times:before { + content: "" +} + +.fa-search-plus:before { + content: "" +} + +.fa-search-minus:before { + content: "" +} + +.fa-power-off:before { + content: "" +} + +.fa-signal:before { + content: "" +} + +.fa-cog:before, +.fa-gear:before { + content: "" +} + +.fa-trash-o:before { + content: "" +} + +.fa-home:before, +.icon-home:before { + content: "" +} + +.fa-file-o:before { + content: "" +} + +.fa-clock-o:before { + content: "" +} + +.fa-road:before { + content: "" +} + +.fa-download:before, +.rst-content code.download span:first-child:before, +.rst-content tt.download span:first-child:before { + content: "" +} + +.fa-arrow-circle-o-down:before { + content: "" +} + +.fa-arrow-circle-o-up:before { + content: "" +} + +.fa-inbox:before { + content: "" +} + +.fa-play-circle-o:before { + content: "" +} + +.fa-repeat:before, +.fa-rotate-right:before { + content: "" +} + +.fa-refresh:before { + content: "" +} + +.fa-list-alt:before { + content: "" +} + +.fa-lock:before { + content: "" +} + +.fa-flag:before { + content: "" +} + +.fa-headphones:before { + content: "" +} + +.fa-volume-off:before { + content: "" +} + +.fa-volume-down:before { + content: "" +} + +.fa-volume-up:before { + content: "" +} + +.fa-qrcode:before { + content: "" +} + +.fa-barcode:before { + content: "" +} + +.fa-tag:before { + content: "" +} + +.fa-tags:before { + content: "" +} + +.fa-book:before, +.icon-book:before { + content: "" +} + +.fa-bookmark:before { + content: "" +} + +.fa-print:before { + content: "" +} + +.fa-camera:before { + content: "" +} + +.fa-font:before { + content: "" +} + +.fa-bold:before { + content: "" +} + +.fa-italic:before { + content: "" +} + +.fa-text-height:before { + content: "" +} + +.fa-text-width:before { + content: "" +} + +.fa-align-left:before { + content: "" +} + +.fa-align-center:before { + content: "" +} + +.fa-align-right:before { + content: "" +} + +.fa-align-justify:before { + content: "" +} + +.fa-list:before { + content: "" +} + +.fa-dedent:before, +.fa-outdent:before { + content: "" +} + +.fa-indent:before { + content: "" +} + +.fa-video-camera:before { + content: "" +} + +.fa-image:before, +.fa-photo:before, +.fa-picture-o:before { + content: "" +} + +.fa-pencil:before { + content: "" +} + +.fa-map-marker:before { + content: "" +} + +.fa-adjust:before { + content: "" +} + +.fa-tint:before { + content: "" +} + +.fa-edit:before, +.fa-pencil-square-o:before { + content: "" +} + +.fa-share-square-o:before { + content: "" +} + +.fa-check-square-o:before { + content: "" +} + +.fa-arrows:before { + content: "" +} + +.fa-step-backward:before { + content: "" +} + +.fa-fast-backward:before { + content: "" +} + +.fa-backward:before { + content: "" +} + +.fa-play:before { + content: "" +} + +.fa-pause:before { + content: "" +} + +.fa-stop:before { + content: "" +} + +.fa-forward:before { + content: "" +} + +.fa-fast-forward:before { + content: "" +} + +.fa-step-forward:before { + content: "" +} + +.fa-eject:before { + content: "" +} + +.fa-chevron-left:before { + content: "" +} + +.fa-chevron-right:before { + content: "" +} + +.fa-plus-circle:before { + content: "" +} + +.fa-minus-circle:before { + content: "" +} + +.fa-times-circle:before, +.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before { + content: "" +} + +.fa-check-circle:before, +.wy-inline-validate.wy-inline-validate-success .wy-input-context:before { + content: "" +} + +.fa-question-circle:before { + content: "" +} + +.fa-info-circle:before { + content: "" +} + +.fa-crosshairs:before { + content: "" +} + +.fa-times-circle-o:before { + content: "" +} + +.fa-check-circle-o:before { + content: "" +} + +.fa-ban:before { + content: "" +} + +.fa-arrow-left:before { + content: "" +} + +.fa-arrow-right:before { + content: "" +} + +.fa-arrow-up:before { + content: "" +} + +.fa-arrow-down:before { + content: "" +} + +.fa-mail-forward:before, +.fa-share:before { + content: "" +} + +.fa-expand:before { + content: "" +} + +.fa-compress:before { + content: "" +} + +.fa-plus:before { + content: "" +} + +.fa-minus:before { + content: "" +} + +.fa-asterisk:before { + content: "" +} + +.fa-exclamation-circle:before, +.rst-content .admonition-title:before, +.wy-inline-validate.wy-inline-validate-info .wy-input-context:before, +.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before { + content: "" +} + +.fa-gift:before { + content: "" +} + +.fa-leaf:before { + content: "" +} + +.fa-fire:before, +.icon-fire:before { + content: "" +} + +.fa-eye:before { + content: "" +} + +.fa-eye-slash:before { + content: "" +} + +.fa-exclamation-triangle:before, +.fa-warning:before { + content: "" +} + +.fa-plane:before { + content: "" +} + +.fa-calendar:before { + content: "" +} + +.fa-random:before { + content: "" +} + +.fa-comment:before { + content: "" +} + +.fa-magnet:before { + content: "" +} + +.fa-chevron-up:before { + content: "" +} + +.fa-chevron-down:before { + content: "" +} + +.fa-retweet:before { + content: "" +} + +.fa-shopping-cart:before { + content: "" +} + +.fa-folder:before { + content: "" +} + +.fa-folder-open:before { + content: "" +} + +.fa-arrows-v:before { + content: "" +} + +.fa-arrows-h:before { + content: "" +} + +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "" +} + +.fa-twitter-square:before { + content: "" +} + +.fa-facebook-square:before { + content: "" +} + +.fa-camera-retro:before { + content: "" +} + +.fa-key:before { + content: "" +} + +.fa-cogs:before, +.fa-gears:before { + content: "" +} + +.fa-comments:before { + content: "" +} + +.fa-thumbs-o-up:before { + content: "" +} + +.fa-thumbs-o-down:before { + content: "" +} + +.fa-star-half:before { + content: "" +} + +.fa-heart-o:before { + content: "" +} + +.fa-sign-out:before { + content: "" +} + +.fa-linkedin-square:before { + content: "" +} + +.fa-thumb-tack:before { + content: "" +} + +.fa-external-link:before { + content: "" +} + +.fa-sign-in:before { + content: "" +} + +.fa-trophy:before { + content: "" +} + +.fa-github-square:before { + content: "" +} + +.fa-upload:before { + content: "" +} + +.fa-lemon-o:before { + content: "" +} + +.fa-phone:before { + content: "" +} + +.fa-square-o:before { + content: "" +} + +.fa-bookmark-o:before { + content: "" +} + +.fa-phone-square:before { + content: "" +} + +.fa-twitter:before { + content: "" +} + +.fa-facebook-f:before, +.fa-facebook:before { + content: "" +} + +.fa-github:before, +.icon-github:before { + content: "" +} + +.fa-unlock:before { + content: "" +} + +.fa-credit-card:before { + content: "" +} + +.fa-feed:before, +.fa-rss:before { + content: "" +} + +.fa-hdd-o:before { + content: "" +} + +.fa-bullhorn:before { + content: "" +} + +.fa-bell:before { + content: "" +} + +.fa-certificate:before { + content: "" +} + +.fa-hand-o-right:before { + content: "" +} + +.fa-hand-o-left:before { + content: "" +} + +.fa-hand-o-up:before { + content: "" +} + +.fa-hand-o-down:before { + content: "" +} + +.fa-arrow-circle-left:before, +.icon-circle-arrow-left:before { + content: "" +} + +.fa-arrow-circle-right:before, +.icon-circle-arrow-right:before { + content: "" +} + +.fa-arrow-circle-up:before { + content: "" +} + +.fa-arrow-circle-down:before { + content: "" +} + +.fa-globe:before { + content: "" +} + +.fa-wrench:before { + content: "" +} + +.fa-tasks:before { + content: "" +} + +.fa-filter:before { + content: "" +} + +.fa-briefcase:before { + content: "" +} + +.fa-arrows-alt:before { + content: "" +} + +.fa-group:before, +.fa-users:before { + content: "" +} + +.fa-chain:before, +.fa-link:before, +.icon-link:before { + content: "" +} + +.fa-cloud:before { + content: "" +} + +.fa-flask:before { + content: "" +} + +.fa-cut:before, +.fa-scissors:before { + content: "" +} + +.fa-copy:before, +.fa-files-o:before { + content: "" +} + +.fa-paperclip:before { + content: "" +} + +.fa-floppy-o:before, +.fa-save:before { + content: "" +} + +.fa-square:before { + content: "" +} + +.fa-bars:before, +.fa-navicon:before, +.fa-reorder:before { + content: "" +} + +.fa-list-ul:before { + content: "" +} + +.fa-list-ol:before { + content: "" +} + +.fa-strikethrough:before { + content: "" +} + +.fa-underline:before { + content: "" +} + +.fa-table:before { + content: "" +} + +.fa-magic:before { + content: "" +} + +.fa-truck:before { + content: "" +} + +.fa-pinterest:before { + content: "" +} + +.fa-pinterest-square:before { + content: "" +} + +.fa-google-plus-square:before { + content: "" +} + +.fa-google-plus:before { + content: "" +} + +.fa-money:before { + content: "" +} + +.fa-caret-down:before, +.icon-caret-down:before, +.wy-dropdown .caret:before { + content: "" +} + +.fa-caret-up:before { + content: "" +} + +.fa-caret-left:before { + content: "" +} + +.fa-caret-right:before { + content: "" +} + +.fa-columns:before { + content: "" +} + +.fa-sort:before, +.fa-unsorted:before { + content: "" +} + +.fa-sort-desc:before, +.fa-sort-down:before { + content: "" +} + +.fa-sort-asc:before, +.fa-sort-up:before { + content: "" +} + +.fa-envelope:before { + content: "" +} + +.fa-linkedin:before { + content: "" +} + +.fa-rotate-left:before, +.fa-undo:before { + content: "" +} + +.fa-gavel:before, +.fa-legal:before { + content: "" +} + +.fa-dashboard:before, +.fa-tachometer:before { + content: "" +} + +.fa-comment-o:before { + content: "" +} + +.fa-comments-o:before { + content: "" +} + +.fa-bolt:before, +.fa-flash:before { + content: "" +} + +.fa-sitemap:before { + content: "" +} + +.fa-umbrella:before { + content: "" +} + +.fa-clipboard:before, +.fa-paste:before { + content: "" +} + +.fa-lightbulb-o:before { + content: "" +} + +.fa-exchange:before { + content: "" +} + +.fa-cloud-download:before { + content: "" +} + +.fa-cloud-upload:before { + content: "" +} + +.fa-user-md:before { + content: "" +} + +.fa-stethoscope:before { + content: "" +} + +.fa-suitcase:before { + content: "" +} + +.fa-bell-o:before { + content: "" +} + +.fa-coffee:before { + content: "" +} + +.fa-cutlery:before { + content: "" +} + +.fa-file-text-o:before { + content: "" +} + +.fa-building-o:before { + content: "" +} + +.fa-hospital-o:before { + content: "" +} + +.fa-ambulance:before { + content: "" +} + +.fa-medkit:before { + content: "" +} + +.fa-fighter-jet:before { + content: "" +} + +.fa-beer:before { + content: "" +} + +.fa-h-square:before { + content: "" +} + +.fa-plus-square:before { + content: "" +} + +.fa-angle-double-left:before { + content: "" +} + +.fa-angle-double-right:before { + content: "" +} + +.fa-angle-double-up:before { + content: "" +} + +.fa-angle-double-down:before { + content: "" +} + +.fa-angle-left:before { + content: "" +} + +.fa-angle-right:before { + content: "" +} + +.fa-angle-up:before { + content: "" +} + +.fa-angle-down:before { + content: "" +} + +.fa-desktop:before { + content: "" +} + +.fa-laptop:before { + content: "" +} + +.fa-tablet:before { + content: "" +} + +.fa-mobile-phone:before, +.fa-mobile:before { + content: "" +} + +.fa-circle-o:before { + content: "" +} + +.fa-quote-left:before { + content: "" +} + +.fa-quote-right:before { + content: "" +} + +.fa-spinner:before { + content: "" +} + +.fa-circle:before { + content: "" +} + +.fa-mail-reply:before, +.fa-reply:before { + content: "" +} + +.fa-github-alt:before { + content: "" +} + +.fa-folder-o:before { + content: "" +} + +.fa-folder-open-o:before { + content: "" +} + +.fa-smile-o:before { + content: "" +} + +.fa-frown-o:before { + content: "" +} + +.fa-meh-o:before { + content: "" +} + +.fa-gamepad:before { + content: "" +} + +.fa-keyboard-o:before { + content: "" +} + +.fa-flag-o:before { + content: "" +} + +.fa-flag-checkered:before { + content: "" +} + +.fa-terminal:before { + content: "" +} + +.fa-code:before { + content: "" +} + +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "" +} + +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "" +} + +.fa-location-arrow:before { + content: "" +} + +.fa-crop:before { + content: "" +} + +.fa-code-fork:before { + content: "" +} + +.fa-chain-broken:before, +.fa-unlink:before { + content: "" +} + +.fa-question:before { + content: "" +} + +.fa-info:before { + content: "" +} + +.fa-exclamation:before { + content: "" +} + +.fa-superscript:before { + content: "" +} + +.fa-subscript:before { + content: "" +} + +.fa-eraser:before { + content: "" +} + +.fa-puzzle-piece:before { + content: "" +} + +.fa-microphone:before { + content: "" +} + +.fa-microphone-slash:before { + content: "" +} + +.fa-shield:before { + content: "" +} + +.fa-calendar-o:before { + content: "" +} + +.fa-fire-extinguisher:before { + content: "" +} + +.fa-rocket:before { + content: "" +} + +.fa-maxcdn:before { + content: "" +} + +.fa-chevron-circle-left:before { + content: "" +} + +.fa-chevron-circle-right:before { + content: "" +} + +.fa-chevron-circle-up:before { + content: "" +} + +.fa-chevron-circle-down:before { + content: "" +} + +.fa-html5:before { + content: "" +} + +.fa-css3:before { + content: "" +} + +.fa-anchor:before { + content: "" +} + +.fa-unlock-alt:before { + content: "" +} + +.fa-bullseye:before { + content: "" +} + +.fa-ellipsis-h:before { + content: "" +} + +.fa-ellipsis-v:before { + content: "" +} + +.fa-rss-square:before { + content: "" +} + +.fa-play-circle:before { + content: "" +} + +.fa-ticket:before { + content: "" +} + +.fa-minus-square:before { + content: "" +} + +.fa-minus-square-o:before, +.wy-menu-vertical li.current>a button.toctree-expand:before, +.wy-menu-vertical li.on a button.toctree-expand:before { + content: "" +} + +.fa-level-up:before { + content: "" +} + +.fa-level-down:before { + content: "" +} + +.fa-check-square:before { + content: "" +} + +.fa-pencil-square:before { + content: "" +} + +.fa-external-link-square:before { + content: "" +} + +.fa-share-square:before { + content: "" +} + +.fa-compass:before { + content: "" +} + +.fa-caret-square-o-down:before, +.fa-toggle-down:before { + content: "" +} + +.fa-caret-square-o-up:before, +.fa-toggle-up:before { + content: "" +} + +.fa-caret-square-o-right:before, +.fa-toggle-right:before { + content: "" +} + +.fa-eur:before, +.fa-euro:before { + content: "" +} + +.fa-gbp:before { + content: "" +} + +.fa-dollar:before, +.fa-usd:before { + content: "" +} + +.fa-inr:before, +.fa-rupee:before { + content: "" +} + +.fa-cny:before, +.fa-jpy:before, +.fa-rmb:before, +.fa-yen:before { + content: "" +} + +.fa-rouble:before, +.fa-rub:before, +.fa-ruble:before { + content: "" +} + +.fa-krw:before, +.fa-won:before { + content: "" +} + +.fa-bitcoin:before, +.fa-btc:before { + content: "" +} + +.fa-file:before { + content: "" +} + +.fa-file-text:before { + content: "" +} + +.fa-sort-alpha-asc:before { + content: "" +} + +.fa-sort-alpha-desc:before { + content: "" +} + +.fa-sort-amount-asc:before { + content: "" +} + +.fa-sort-amount-desc:before { + content: "" +} + +.fa-sort-numeric-asc:before { + content: "" +} + +.fa-sort-numeric-desc:before { + content: "" +} + +.fa-thumbs-up:before { + content: "" +} + +.fa-thumbs-down:before { + content: "" +} + +.fa-youtube-square:before { + content: "" +} + +.fa-youtube:before { + content: "" +} + +.fa-xing:before { + content: "" +} + +.fa-xing-square:before { + content: "" +} + +.fa-youtube-play:before { + content: "" +} + +.fa-dropbox:before { + content: "" +} + +.fa-stack-overflow:before { + content: "" +} + +.fa-instagram:before { + content: "" +} + +.fa-flickr:before { + content: "" +} + +.fa-adn:before { + content: "" +} + +.fa-bitbucket:before, +.icon-bitbucket:before { + content: "" +} + +.fa-bitbucket-square:before { + content: "" +} + +.fa-tumblr:before { + content: "" +} + +.fa-tumblr-square:before { + content: "" +} + +.fa-long-arrow-down:before { + content: "" +} + +.fa-long-arrow-up:before { + content: "" +} + +.fa-long-arrow-left:before { + content: "" +} + +.fa-long-arrow-right:before { + content: "" +} + +.fa-apple:before { + content: "" +} + +.fa-windows:before { + content: "" +} + +.fa-android:before { + content: "" +} + +.fa-linux:before { + content: "" +} + +.fa-dribbble:before { + content: "" +} + +.fa-skype:before { + content: "" +} + +.fa-foursquare:before { + content: "" +} + +.fa-trello:before { + content: "" +} + +.fa-female:before { + content: "" +} + +.fa-male:before { + content: "" +} + +.fa-gittip:before, +.fa-gratipay:before { + content: "" +} + +.fa-sun-o:before { + content: "" +} + +.fa-moon-o:before { + content: "" +} + +.fa-archive:before { + content: "" +} + +.fa-bug:before { + content: "" +} + +.fa-vk:before { + content: "" +} + +.fa-weibo:before { + content: "" +} + +.fa-renren:before { + content: "" +} + +.fa-pagelines:before { + content: "" +} + +.fa-stack-exchange:before { + content: "" +} + +.fa-arrow-circle-o-right:before { + content: "" +} + +.fa-arrow-circle-o-left:before { + content: "" +} + +.fa-caret-square-o-left:before, +.fa-toggle-left:before { + content: "" +} + +.fa-dot-circle-o:before { + content: "" +} + +.fa-wheelchair:before { + content: "" +} + +.fa-vimeo-square:before { + content: "" +} + +.fa-try:before, +.fa-turkish-lira:before { + content: "" +} + +.fa-plus-square-o:before, +.wy-menu-vertical li button.toctree-expand:before { + content: "" +} + +.fa-space-shuttle:before { + content: "" +} + +.fa-slack:before { + content: "" +} + +.fa-envelope-square:before { + content: "" +} + +.fa-wordpress:before { + content: "" +} + +.fa-openid:before { + content: "" +} + +.fa-bank:before, +.fa-institution:before, +.fa-university:before { + content: "" +} + +.fa-graduation-cap:before, +.fa-mortar-board:before { + content: "" +} + +.fa-yahoo:before { + content: "" +} + +.fa-google:before { + content: "" +} + +.fa-reddit:before { + content: "" +} + +.fa-reddit-square:before { + content: "" +} + +.fa-stumbleupon-circle:before { + content: "" +} + +.fa-stumbleupon:before { + content: "" +} + +.fa-delicious:before { + content: "" +} + +.fa-digg:before { + content: "" +} + +.fa-pied-piper-pp:before { + content: "" +} + +.fa-pied-piper-alt:before { + content: "" +} + +.fa-drupal:before { + content: "" +} + +.fa-joomla:before { + content: "" +} + +.fa-language:before { + content: "" +} + +.fa-fax:before { + content: "" +} + +.fa-building:before { + content: "" +} + +.fa-child:before { + content: "" +} + +.fa-paw:before { + content: "" +} + +.fa-spoon:before { + content: "" +} + +.fa-cube:before { + content: "" +} + +.fa-cubes:before { + content: "" +} + +.fa-behance:before { + content: "" +} + +.fa-behance-square:before { + content: "" +} + +.fa-steam:before { + content: "" +} + +.fa-steam-square:before { + content: "" +} + +.fa-recycle:before { + content: "" +} + +.fa-automobile:before, +.fa-car:before { + content: "" +} + +.fa-cab:before, +.fa-taxi:before { + content: "" +} + +.fa-tree:before { + content: "" +} + +.fa-spotify:before { + content: "" +} + +.fa-deviantart:before { + content: "" +} + +.fa-soundcloud:before { + content: "" +} + +.fa-database:before { + content: "" +} + +.fa-file-pdf-o:before { + content: "" +} + +.fa-file-word-o:before { + content: "" +} + +.fa-file-excel-o:before { + content: "" +} + +.fa-file-powerpoint-o:before { + content: "" +} + +.fa-file-image-o:before, +.fa-file-photo-o:before, +.fa-file-picture-o:before { + content: "" +} + +.fa-file-archive-o:before, +.fa-file-zip-o:before { + content: "" +} + +.fa-file-audio-o:before, +.fa-file-sound-o:before { + content: "" +} + +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "" +} + +.fa-file-code-o:before { + content: "" +} + +.fa-vine:before { + content: "" +} + +.fa-codepen:before { + content: "" +} + +.fa-jsfiddle:before { + content: "" +} + +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-ring:before, +.fa-life-saver:before, +.fa-support:before { + content: "" +} + +.fa-circle-o-notch:before { + content: "" +} + +.fa-ra:before, +.fa-rebel:before, +.fa-resistance:before { + content: "" +} + +.fa-empire:before, +.fa-ge:before { + content: "" +} + +.fa-git-square:before { + content: "" +} + +.fa-git:before { + content: "" +} + +.fa-hacker-news:before, +.fa-y-combinator-square:before, +.fa-yc-square:before { + content: "" +} + +.fa-tencent-weibo:before { + content: "" +} + +.fa-qq:before { + content: "" +} + +.fa-wechat:before, +.fa-weixin:before { + content: "" +} + +.fa-paper-plane:before, +.fa-send:before { + content: "" +} + +.fa-paper-plane-o:before, +.fa-send-o:before { + content: "" +} + +.fa-history:before { + content: "" +} + +.fa-circle-thin:before { + content: "" +} + +.fa-header:before { + content: "" +} + +.fa-paragraph:before { + content: "" +} + +.fa-sliders:before { + content: "" +} + +.fa-share-alt:before { + content: "" +} + +.fa-share-alt-square:before { + content: "" +} + +.fa-bomb:before { + content: "" +} + +.fa-futbol-o:before, +.fa-soccer-ball-o:before { + content: "" +} + +.fa-tty:before { + content: "" +} + +.fa-binoculars:before { + content: "" +} + +.fa-plug:before { + content: "" +} + +.fa-slideshare:before { + content: "" +} + +.fa-twitch:before { + content: "" +} + +.fa-yelp:before { + content: "" +} + +.fa-newspaper-o:before { + content: "" +} + +.fa-wifi:before { + content: "" +} + +.fa-calculator:before { + content: "" +} + +.fa-paypal:before { + content: "" +} + +.fa-google-wallet:before { + content: "" +} + +.fa-cc-visa:before { + content: "" +} + +.fa-cc-mastercard:before { + content: "" +} + +.fa-cc-discover:before { + content: "" +} + +.fa-cc-amex:before { + content: "" +} + +.fa-cc-paypal:before { + content: "" +} + +.fa-cc-stripe:before { + content: "" +} + +.fa-bell-slash:before { + content: "" +} + +.fa-bell-slash-o:before { + content: "" +} + +.fa-trash:before { + content: "" +} + +.fa-copyright:before { + content: "" +} + +.fa-at:before { + content: "" +} + +.fa-eyedropper:before { + content: "" +} + +.fa-paint-brush:before { + content: "" +} + +.fa-birthday-cake:before { + content: "" +} + +.fa-area-chart:before { + content: "" +} + +.fa-pie-chart:before { + content: "" +} + +.fa-line-chart:before { + content: "" +} + +.fa-lastfm:before { + content: "" +} + +.fa-lastfm-square:before { + content: "" +} + +.fa-toggle-off:before { + content: "" +} + +.fa-toggle-on:before { + content: "" +} + +.fa-bicycle:before { + content: "" +} + +.fa-bus:before { + content: "" +} + +.fa-ioxhost:before { + content: "" +} + +.fa-angellist:before { + content: "" +} + +.fa-cc:before { + content: "" +} + +.fa-ils:before, +.fa-shekel:before, +.fa-sheqel:before { + content: "" +} + +.fa-meanpath:before { + content: "" +} + +.fa-buysellads:before { + content: "" +} + +.fa-connectdevelop:before { + content: "" +} + +.fa-dashcube:before { + content: "" +} + +.fa-forumbee:before { + content: "" +} + +.fa-leanpub:before { + content: "" +} + +.fa-sellsy:before { + content: "" +} + +.fa-shirtsinbulk:before { + content: "" +} + +.fa-simplybuilt:before { + content: "" +} + +.fa-skyatlas:before { + content: "" +} + +.fa-cart-plus:before { + content: "" +} + +.fa-cart-arrow-down:before { + content: "" +} + +.fa-diamond:before { + content: "" +} + +.fa-ship:before { + content: "" +} + +.fa-user-secret:before { + content: "" +} + +.fa-motorcycle:before { + content: "" +} + +.fa-street-view:before { + content: "" +} + +.fa-heartbeat:before { + content: "" +} + +.fa-venus:before { + content: "" +} + +.fa-mars:before { + content: "" +} + +.fa-mercury:before { + content: "" +} + +.fa-intersex:before, +.fa-transgender:before { + content: "" +} + +.fa-transgender-alt:before { + content: "" +} + +.fa-venus-double:before { + content: "" +} + +.fa-mars-double:before { + content: "" +} + +.fa-venus-mars:before { + content: "" +} + +.fa-mars-stroke:before { + content: "" +} + +.fa-mars-stroke-v:before { + content: "" +} + +.fa-mars-stroke-h:before { + content: "" +} + +.fa-neuter:before { + content: "" +} + +.fa-genderless:before { + content: "" +} + +.fa-facebook-official:before { + content: "" +} + +.fa-pinterest-p:before { + content: "" +} + +.fa-whatsapp:before { + content: "" +} + +.fa-server:before { + content: "" +} + +.fa-user-plus:before { + content: "" +} + +.fa-user-times:before { + content: "" +} + +.fa-bed:before, +.fa-hotel:before { + content: "" +} + +.fa-viacoin:before { + content: "" +} + +.fa-train:before { + content: "" +} + +.fa-subway:before { + content: "" +} + +.fa-medium:before { + content: "" +} + +.fa-y-combinator:before, +.fa-yc:before { + content: "" +} + +.fa-optin-monster:before { + content: "" +} + +.fa-opencart:before { + content: "" +} + +.fa-expeditedssl:before { + content: "" +} + +.fa-battery-4:before, +.fa-battery-full:before, +.fa-battery:before { + content: "" +} + +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "" +} + +.fa-battery-2:before, +.fa-battery-half:before { + content: "" +} + +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "" +} + +.fa-battery-0:before, +.fa-battery-empty:before { + content: "" +} + +.fa-mouse-pointer:before { + content: "" +} + +.fa-i-cursor:before { + content: "" +} + +.fa-object-group:before { + content: "" +} + +.fa-object-ungroup:before { + content: "" +} + +.fa-sticky-note:before { + content: "" +} + +.fa-sticky-note-o:before { + content: "" +} + +.fa-cc-jcb:before { + content: "" +} + +.fa-cc-diners-club:before { + content: "" +} + +.fa-clone:before { + content: "" +} + +.fa-balance-scale:before { + content: "" +} + +.fa-hourglass-o:before { + content: "" +} + +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "" +} + +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "" +} + +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "" +} + +.fa-hourglass:before { + content: "" +} + +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "" +} + +.fa-hand-paper-o:before, +.fa-hand-stop-o:before { + content: "" +} + +.fa-hand-scissors-o:before { + content: "" +} + +.fa-hand-lizard-o:before { + content: "" +} + +.fa-hand-spock-o:before { + content: "" +} + +.fa-hand-pointer-o:before { + content: "" +} + +.fa-hand-peace-o:before { + content: "" +} + +.fa-trademark:before { + content: "" +} + +.fa-registered:before { + content: "" +} + +.fa-creative-commons:before { + content: "" +} + +.fa-gg:before { + content: "" +} + +.fa-gg-circle:before { + content: "" +} + +.fa-tripadvisor:before { + content: "" +} + +.fa-odnoklassniki:before { + content: "" +} + +.fa-odnoklassniki-square:before { + content: "" +} + +.fa-get-pocket:before { + content: "" +} + +.fa-wikipedia-w:before { + content: "" +} + +.fa-safari:before { + content: "" +} + +.fa-chrome:before { + content: "" +} + +.fa-firefox:before { + content: "" +} + +.fa-opera:before { + content: "" +} + +.fa-internet-explorer:before { + content: "" +} + +.fa-television:before, +.fa-tv:before { + content: "" +} + +.fa-contao:before { + content: "" +} + +.fa-500px:before { + content: "" +} + +.fa-amazon:before { + content: "" +} + +.fa-calendar-plus-o:before { + content: "" +} + +.fa-calendar-minus-o:before { + content: "" +} + +.fa-calendar-times-o:before { + content: "" +} + +.fa-calendar-check-o:before { + content: "" +} + +.fa-industry:before { + content: "" +} + +.fa-map-pin:before { + content: "" +} + +.fa-map-signs:before { + content: "" +} + +.fa-map-o:before { + content: "" +} + +.fa-map:before { + content: "" +} + +.fa-commenting:before { + content: "" +} + +.fa-commenting-o:before { + content: "" +} + +.fa-houzz:before { + content: "" +} + +.fa-vimeo:before { + content: "" +} + +.fa-black-tie:before { + content: "" +} + +.fa-fonticons:before { + content: "" +} + +.fa-reddit-alien:before { + content: "" +} + +.fa-edge:before { + content: "" +} + +.fa-credit-card-alt:before { + content: "" +} + +.fa-codiepie:before { + content: "" +} + +.fa-modx:before { + content: "" +} + +.fa-fort-awesome:before { + content: "" +} + +.fa-usb:before { + content: "" +} + +.fa-product-hunt:before { + content: "" +} + +.fa-mixcloud:before { + content: "" +} + +.fa-scribd:before { + content: "" +} + +.fa-pause-circle:before { + content: "" +} + +.fa-pause-circle-o:before { + content: "" +} + +.fa-stop-circle:before { + content: "" +} + +.fa-stop-circle-o:before { + content: "" +} + +.fa-shopping-bag:before { + content: "" +} + +.fa-shopping-basket:before { + content: "" +} + +.fa-hashtag:before { + content: "" +} + +.fa-bluetooth:before { + content: "" +} + +.fa-bluetooth-b:before { + content: "" +} + +.fa-percent:before { + content: "" +} + +.fa-gitlab:before, +.icon-gitlab:before { + content: "" +} + +.fa-wpbeginner:before { + content: "" +} + +.fa-wpforms:before { + content: "" +} + +.fa-envira:before { + content: "" +} + +.fa-universal-access:before { + content: "" +} + +.fa-wheelchair-alt:before { + content: "" +} + +.fa-question-circle-o:before { + content: "" +} + +.fa-blind:before { + content: "" +} + +.fa-audio-description:before { + content: "" +} + +.fa-volume-control-phone:before { + content: "" +} + +.fa-braille:before { + content: "" +} + +.fa-assistive-listening-systems:before { + content: "" +} + +.fa-american-sign-language-interpreting:before, +.fa-asl-interpreting:before { + content: "" +} + +.fa-deaf:before, +.fa-deafness:before, +.fa-hard-of-hearing:before { + content: "" +} + +.fa-glide:before { + content: "" +} + +.fa-glide-g:before { + content: "" +} + +.fa-sign-language:before, +.fa-signing:before { + content: "" +} + +.fa-low-vision:before { + content: "" +} + +.fa-viadeo:before { + content: "" +} + +.fa-viadeo-square:before { + content: "" +} + +.fa-snapchat:before { + content: "" +} + +.fa-snapchat-ghost:before { + content: "" +} + +.fa-snapchat-square:before { + content: "" +} + +.fa-pied-piper:before { + content: "" +} + +.fa-first-order:before { + content: "" +} + +.fa-yoast:before { + content: "" +} + +.fa-themeisle:before { + content: "" +} + +.fa-google-plus-circle:before, +.fa-google-plus-official:before { + content: "" +} + +.fa-fa:before, +.fa-font-awesome:before { + content: "" +} + +.fa-handshake-o:before { + content: "" +} + +.fa-envelope-open:before { + content: "" +} + +.fa-envelope-open-o:before { + content: "" +} + +.fa-linode:before { + content: "" +} + +.fa-address-book:before { + content: "" +} + +.fa-address-book-o:before { + content: "" +} + +.fa-address-card:before, +.fa-vcard:before { + content: "" +} + +.fa-address-card-o:before, +.fa-vcard-o:before { + content: "" +} + +.fa-user-circle:before { + content: "" +} + +.fa-user-circle-o:before { + content: "" +} + +.fa-user-o:before { + content: "" +} + +.fa-id-badge:before { + content: "" +} + +.fa-drivers-license:before, +.fa-id-card:before { + content: "" +} + +.fa-drivers-license-o:before, +.fa-id-card-o:before { + content: "" +} + +.fa-quora:before { + content: "" +} + +.fa-free-code-camp:before { + content: "" +} + +.fa-telegram:before { + content: "" +} + +.fa-thermometer-4:before, +.fa-thermometer-full:before, +.fa-thermometer:before { + content: "" +} + +.fa-thermometer-3:before, +.fa-thermometer-three-quarters:before { + content: "" +} + +.fa-thermometer-2:before, +.fa-thermometer-half:before { + content: "" +} + +.fa-thermometer-1:before, +.fa-thermometer-quarter:before { + content: "" +} + +.fa-thermometer-0:before, +.fa-thermometer-empty:before { + content: "" +} + +.fa-shower:before { + content: "" +} + +.fa-bath:before, +.fa-bathtub:before, +.fa-s15:before { + content: "" +} + +.fa-podcast:before { + content: "" +} + +.fa-window-maximize:before { + content: "" +} + +.fa-window-minimize:before { + content: "" +} + +.fa-window-restore:before { + content: "" +} + +.fa-times-rectangle:before, +.fa-window-close:before { + content: "" +} + +.fa-times-rectangle-o:before, +.fa-window-close-o:before { + content: "" +} + +.fa-bandcamp:before { + content: "" +} + +.fa-grav:before { + content: "" +} + +.fa-etsy:before { + content: "" +} + +.fa-imdb:before { + content: "" +} + +.fa-ravelry:before { + content: "" +} + +.fa-eercast:before { + content: "" +} + +.fa-microchip:before { + content: "" +} + +.fa-snowflake-o:before { + content: "" +} + +.fa-superpowers:before { + content: "" +} + +.fa-wpexplorer:before { + content: "" +} + +.fa-meetup:before { + content: "" +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0 +} + +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto +} + +.fa, +.icon, +.rst-content .admonition-title, +.rst-content .code-block-caption .headerlink, +.rst-content .eqno .headerlink, +.rst-content code.download span:first-child, +.rst-content dl dt .headerlink, +.rst-content h1 .headerlink, +.rst-content h2 .headerlink, +.rst-content h3 .headerlink, +.rst-content h4 .headerlink, +.rst-content h5 .headerlink, +.rst-content h6 .headerlink, +.rst-content p.caption .headerlink, +.rst-content p .headerlink, +.rst-content table>caption .headerlink, +.rst-content tt.download span:first-child, +.wy-dropdown .caret, +.wy-inline-validate.wy-inline-validate-danger .wy-input-context, +.wy-inline-validate.wy-inline-validate-info .wy-input-context, +.wy-inline-validate.wy-inline-validate-success .wy-input-context, +.wy-inline-validate.wy-inline-validate-warning .wy-input-context, +.wy-menu-vertical li.current>a button.toctree-expand, +.wy-menu-vertical li.on a button.toctree-expand, +.wy-menu-vertical li button.toctree-expand { + font-family: inherit +} + +.fa:before, +.icon:before, +.rst-content .admonition-title:before, +.rst-content .code-block-caption .headerlink:before, +.rst-content .eqno .headerlink:before, +.rst-content code.download span:first-child:before, +.rst-content dl dt .headerlink:before, +.rst-content h1 .headerlink:before, +.rst-content h2 .headerlink:before, +.rst-content h3 .headerlink:before, +.rst-content h4 .headerlink:before, +.rst-content h5 .headerlink:before, +.rst-content h6 .headerlink:before, +.rst-content p.caption .headerlink:before, +.rst-content p .headerlink:before, +.rst-content table>caption .headerlink:before, +.rst-content tt.download span:first-child:before, +.wy-dropdown .caret:before, +.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before, +.wy-inline-validate.wy-inline-validate-info .wy-input-context:before, +.wy-inline-validate.wy-inline-validate-success .wy-input-context:before, +.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before, +.wy-menu-vertical li.current>a button.toctree-expand:before, +.wy-menu-vertical li.on a button.toctree-expand:before, +.wy-menu-vertical li button.toctree-expand:before { + font-family: FontAwesome; + display: inline-block; + font-style: normal; + font-weight: 400; + line-height: 1; + text-decoration: inherit +} + +.rst-content .code-block-caption a .headerlink, +.rst-content .eqno a .headerlink, +.rst-content a .admonition-title, +.rst-content code.download a span:first-child, +.rst-content dl dt a .headerlink, +.rst-content h1 a .headerlink, +.rst-content h2 a .headerlink, +.rst-content h3 a .headerlink, +.rst-content h4 a .headerlink, +.rst-content h5 a .headerlink, +.rst-content h6 a .headerlink, +.rst-content p.caption a .headerlink, +.rst-content p a .headerlink, +.rst-content table>caption a .headerlink, +.rst-content tt.download a span:first-child, +.wy-menu-vertical li.current>a button.toctree-expand, +.wy-menu-vertical li.on a button.toctree-expand, +.wy-menu-vertical li a button.toctree-expand, +a .fa, +a .icon, +a .rst-content .admonition-title, +a .rst-content .code-block-caption .headerlink, +a .rst-content .eqno .headerlink, +a .rst-content code.download span:first-child, +a .rst-content dl dt .headerlink, +a .rst-content h1 .headerlink, +a .rst-content h2 .headerlink, +a .rst-content h3 .headerlink, +a .rst-content h4 .headerlink, +a .rst-content h5 .headerlink, +a .rst-content h6 .headerlink, +a .rst-content p.caption .headerlink, +a .rst-content p .headerlink, +a .rst-content table>caption .headerlink, +a .rst-content tt.download span:first-child, +a .wy-menu-vertical li button.toctree-expand { + display: inline-block; + text-decoration: inherit +} + +.btn .fa, +.btn .icon, +.btn .rst-content .admonition-title, +.btn .rst-content .code-block-caption .headerlink, +.btn .rst-content .eqno .headerlink, +.btn .rst-content code.download span:first-child, +.btn .rst-content dl dt .headerlink, +.btn .rst-content h1 .headerlink, +.btn .rst-content h2 .headerlink, +.btn .rst-content h3 .headerlink, +.btn .rst-content h4 .headerlink, +.btn .rst-content h5 .headerlink, +.btn .rst-content h6 .headerlink, +.btn .rst-content p .headerlink, +.btn .rst-content table>caption .headerlink, +.btn .rst-content tt.download span:first-child, +.btn .wy-menu-vertical li.current>a button.toctree-expand, +.btn .wy-menu-vertical li.on a button.toctree-expand, +.btn .wy-menu-vertical li button.toctree-expand, +.nav .fa, +.nav .icon, +.nav .rst-content .admonition-title, +.nav .rst-content .code-block-caption .headerlink, +.nav .rst-content .eqno .headerlink, +.nav .rst-content code.download span:first-child, +.nav .rst-content dl dt .headerlink, +.nav .rst-content h1 .headerlink, +.nav .rst-content h2 .headerlink, +.nav .rst-content h3 .headerlink, +.nav .rst-content h4 .headerlink, +.nav .rst-content h5 .headerlink, +.nav .rst-content h6 .headerlink, +.nav .rst-content p .headerlink, +.nav .rst-content table>caption .headerlink, +.nav .rst-content tt.download span:first-child, +.nav .wy-menu-vertical li.current>a button.toctree-expand, +.nav .wy-menu-vertical li.on a button.toctree-expand, +.nav .wy-menu-vertical li button.toctree-expand, +.rst-content .btn .admonition-title, +.rst-content .code-block-caption .btn .headerlink, +.rst-content .code-block-caption .nav .headerlink, +.rst-content .eqno .btn .headerlink, +.rst-content .eqno .nav .headerlink, +.rst-content .nav .admonition-title, +.rst-content code.download .btn span:first-child, +.rst-content code.download .nav span:first-child, +.rst-content dl dt .btn .headerlink, +.rst-content dl dt .nav .headerlink, +.rst-content h1 .btn .headerlink, +.rst-content h1 .nav .headerlink, +.rst-content h2 .btn .headerlink, +.rst-content h2 .nav .headerlink, +.rst-content h3 .btn .headerlink, +.rst-content h3 .nav .headerlink, +.rst-content h4 .btn .headerlink, +.rst-content h4 .nav .headerlink, +.rst-content h5 .btn .headerlink, +.rst-content h5 .nav .headerlink, +.rst-content h6 .btn .headerlink, +.rst-content h6 .nav .headerlink, +.rst-content p .btn .headerlink, +.rst-content p .nav .headerlink, +.rst-content table>caption .btn .headerlink, +.rst-content table>caption .nav .headerlink, +.rst-content tt.download .btn span:first-child, +.rst-content tt.download .nav span:first-child, +.wy-menu-vertical li .btn button.toctree-expand, +.wy-menu-vertical li.current>a .btn button.toctree-expand, +.wy-menu-vertical li.current>a .nav button.toctree-expand, +.wy-menu-vertical li .nav button.toctree-expand, +.wy-menu-vertical li.on a .btn button.toctree-expand, +.wy-menu-vertical li.on a .nav button.toctree-expand { + display: inline +} + +.btn .fa-large.icon, +.btn .fa.fa-large, +.btn .rst-content .code-block-caption .fa-large.headerlink, +.btn .rst-content .eqno .fa-large.headerlink, +.btn .rst-content .fa-large.admonition-title, +.btn .rst-content code.download span.fa-large:first-child, +.btn .rst-content dl dt .fa-large.headerlink, +.btn .rst-content h1 .fa-large.headerlink, +.btn .rst-content h2 .fa-large.headerlink, +.btn .rst-content h3 .fa-large.headerlink, +.btn .rst-content h4 .fa-large.headerlink, +.btn .rst-content h5 .fa-large.headerlink, +.btn .rst-content h6 .fa-large.headerlink, +.btn .rst-content p .fa-large.headerlink, +.btn .rst-content table>caption .fa-large.headerlink, +.btn .rst-content tt.download span.fa-large:first-child, +.btn .wy-menu-vertical li button.fa-large.toctree-expand, +.nav .fa-large.icon, +.nav .fa.fa-large, +.nav .rst-content .code-block-caption .fa-large.headerlink, +.nav .rst-content .eqno .fa-large.headerlink, +.nav .rst-content .fa-large.admonition-title, +.nav .rst-content code.download span.fa-large:first-child, +.nav .rst-content dl dt .fa-large.headerlink, +.nav .rst-content h1 .fa-large.headerlink, +.nav .rst-content h2 .fa-large.headerlink, +.nav .rst-content h3 .fa-large.headerlink, +.nav .rst-content h4 .fa-large.headerlink, +.nav .rst-content h5 .fa-large.headerlink, +.nav .rst-content h6 .fa-large.headerlink, +.nav .rst-content p .fa-large.headerlink, +.nav .rst-content table>caption .fa-large.headerlink, +.nav .rst-content tt.download span.fa-large:first-child, +.nav .wy-menu-vertical li button.fa-large.toctree-expand, +.rst-content .btn .fa-large.admonition-title, +.rst-content .code-block-caption .btn .fa-large.headerlink, +.rst-content .code-block-caption .nav .fa-large.headerlink, +.rst-content .eqno .btn .fa-large.headerlink, +.rst-content .eqno .nav .fa-large.headerlink, +.rst-content .nav .fa-large.admonition-title, +.rst-content code.download .btn span.fa-large:first-child, +.rst-content code.download .nav span.fa-large:first-child, +.rst-content dl dt .btn .fa-large.headerlink, +.rst-content dl dt .nav .fa-large.headerlink, +.rst-content h1 .btn .fa-large.headerlink, +.rst-content h1 .nav .fa-large.headerlink, +.rst-content h2 .btn .fa-large.headerlink, +.rst-content h2 .nav .fa-large.headerlink, +.rst-content h3 .btn .fa-large.headerlink, +.rst-content h3 .nav .fa-large.headerlink, +.rst-content h4 .btn .fa-large.headerlink, +.rst-content h4 .nav .fa-large.headerlink, +.rst-content h5 .btn .fa-large.headerlink, +.rst-content h5 .nav .fa-large.headerlink, +.rst-content h6 .btn .fa-large.headerlink, +.rst-content h6 .nav .fa-large.headerlink, +.rst-content p .btn .fa-large.headerlink, +.rst-content p .nav .fa-large.headerlink, +.rst-content table>caption .btn .fa-large.headerlink, +.rst-content table>caption .nav .fa-large.headerlink, +.rst-content tt.download .btn span.fa-large:first-child, +.rst-content tt.download .nav span.fa-large:first-child, +.wy-menu-vertical li .btn button.fa-large.toctree-expand, +.wy-menu-vertical li .nav button.fa-large.toctree-expand { + line-height: .9em +} + +.btn .fa-spin.icon, +.btn .fa.fa-spin, +.btn .rst-content .code-block-caption .fa-spin.headerlink, +.btn .rst-content .eqno .fa-spin.headerlink, +.btn .rst-content .fa-spin.admonition-title, +.btn .rst-content code.download span.fa-spin:first-child, +.btn .rst-content dl dt .fa-spin.headerlink, +.btn .rst-content h1 .fa-spin.headerlink, +.btn .rst-content h2 .fa-spin.headerlink, +.btn .rst-content h3 .fa-spin.headerlink, +.btn .rst-content h4 .fa-spin.headerlink, +.btn .rst-content h5 .fa-spin.headerlink, +.btn .rst-content h6 .fa-spin.headerlink, +.btn .rst-content p .fa-spin.headerlink, +.btn .rst-content table>caption .fa-spin.headerlink, +.btn .rst-content tt.download span.fa-spin:first-child, +.btn .wy-menu-vertical li button.fa-spin.toctree-expand, +.nav .fa-spin.icon, +.nav .fa.fa-spin, +.nav .rst-content .code-block-caption .fa-spin.headerlink, +.nav .rst-content .eqno .fa-spin.headerlink, +.nav .rst-content .fa-spin.admonition-title, +.nav .rst-content code.download span.fa-spin:first-child, +.nav .rst-content dl dt .fa-spin.headerlink, +.nav .rst-content h1 .fa-spin.headerlink, +.nav .rst-content h2 .fa-spin.headerlink, +.nav .rst-content h3 .fa-spin.headerlink, +.nav .rst-content h4 .fa-spin.headerlink, +.nav .rst-content h5 .fa-spin.headerlink, +.nav .rst-content h6 .fa-spin.headerlink, +.nav .rst-content p .fa-spin.headerlink, +.nav .rst-content table>caption .fa-spin.headerlink, +.nav .rst-content tt.download span.fa-spin:first-child, +.nav .wy-menu-vertical li button.fa-spin.toctree-expand, +.rst-content .btn .fa-spin.admonition-title, +.rst-content .code-block-caption .btn .fa-spin.headerlink, +.rst-content .code-block-caption .nav .fa-spin.headerlink, +.rst-content .eqno .btn .fa-spin.headerlink, +.rst-content .eqno .nav .fa-spin.headerlink, +.rst-content .nav .fa-spin.admonition-title, +.rst-content code.download .btn span.fa-spin:first-child, +.rst-content code.download .nav span.fa-spin:first-child, +.rst-content dl dt .btn .fa-spin.headerlink, +.rst-content dl dt .nav .fa-spin.headerlink, +.rst-content h1 .btn .fa-spin.headerlink, +.rst-content h1 .nav .fa-spin.headerlink, +.rst-content h2 .btn .fa-spin.headerlink, +.rst-content h2 .nav .fa-spin.headerlink, +.rst-content h3 .btn .fa-spin.headerlink, +.rst-content h3 .nav .fa-spin.headerlink, +.rst-content h4 .btn .fa-spin.headerlink, +.rst-content h4 .nav .fa-spin.headerlink, +.rst-content h5 .btn .fa-spin.headerlink, +.rst-content h5 .nav .fa-spin.headerlink, +.rst-content h6 .btn .fa-spin.headerlink, +.rst-content h6 .nav .fa-spin.headerlink, +.rst-content p .btn .fa-spin.headerlink, +.rst-content p .nav .fa-spin.headerlink, +.rst-content table>caption .btn .fa-spin.headerlink, +.rst-content table>caption .nav .fa-spin.headerlink, +.rst-content tt.download .btn span.fa-spin:first-child, +.rst-content tt.download .nav span.fa-spin:first-child, +.wy-menu-vertical li .btn button.fa-spin.toctree-expand, +.wy-menu-vertical li .nav button.fa-spin.toctree-expand { + display: inline-block +} + +.btn.fa:before, +.btn.icon:before, +.rst-content .btn.admonition-title:before, +.rst-content .code-block-caption .btn.headerlink:before, +.rst-content .eqno .btn.headerlink:before, +.rst-content code.download span.btn:first-child:before, +.rst-content dl dt .btn.headerlink:before, +.rst-content h1 .btn.headerlink:before, +.rst-content h2 .btn.headerlink:before, +.rst-content h3 .btn.headerlink:before, +.rst-content h4 .btn.headerlink:before, +.rst-content h5 .btn.headerlink:before, +.rst-content h6 .btn.headerlink:before, +.rst-content p .btn.headerlink:before, +.rst-content table>caption .btn.headerlink:before, +.rst-content tt.download span.btn:first-child:before, +.wy-menu-vertical li button.btn.toctree-expand:before { + opacity: .5; + -webkit-transition: opacity .05s ease-in; + -moz-transition: opacity .05s ease-in; + transition: opacity .05s ease-in +} + +.btn.fa:hover:before, +.btn.icon:hover:before, +.rst-content .btn.admonition-title:hover:before, +.rst-content .code-block-caption .btn.headerlink:hover:before, +.rst-content .eqno .btn.headerlink:hover:before, +.rst-content code.download span.btn:first-child:hover:before, +.rst-content dl dt .btn.headerlink:hover:before, +.rst-content h1 .btn.headerlink:hover:before, +.rst-content h2 .btn.headerlink:hover:before, +.rst-content h3 .btn.headerlink:hover:before, +.rst-content h4 .btn.headerlink:hover:before, +.rst-content h5 .btn.headerlink:hover:before, +.rst-content h6 .btn.headerlink:hover:before, +.rst-content p .btn.headerlink:hover:before, +.rst-content table>caption .btn.headerlink:hover:before, +.rst-content tt.download span.btn:first-child:hover:before, +.wy-menu-vertical li button.btn.toctree-expand:hover:before { + opacity: 1 +} + +.btn-mini .fa:before, +.btn-mini .icon:before, +.btn-mini .rst-content .admonition-title:before, +.btn-mini .rst-content .code-block-caption .headerlink:before, +.btn-mini .rst-content .eqno .headerlink:before, +.btn-mini .rst-content code.download span:first-child:before, +.btn-mini .rst-content dl dt .headerlink:before, +.btn-mini .rst-content h1 .headerlink:before, +.btn-mini .rst-content h2 .headerlink:before, +.btn-mini .rst-content h3 .headerlink:before, +.btn-mini .rst-content h4 .headerlink:before, +.btn-mini .rst-content h5 .headerlink:before, +.btn-mini .rst-content h6 .headerlink:before, +.btn-mini .rst-content p .headerlink:before, +.btn-mini .rst-content table>caption .headerlink:before, +.btn-mini .rst-content tt.download span:first-child:before, +.btn-mini .wy-menu-vertical li button.toctree-expand:before, +.rst-content .btn-mini .admonition-title:before, +.rst-content .code-block-caption .btn-mini .headerlink:before, +.rst-content .eqno .btn-mini .headerlink:before, +.rst-content code.download .btn-mini span:first-child:before, +.rst-content dl dt .btn-mini .headerlink:before, +.rst-content h1 .btn-mini .headerlink:before, +.rst-content h2 .btn-mini .headerlink:before, +.rst-content h3 .btn-mini .headerlink:before, +.rst-content h4 .btn-mini .headerlink:before, +.rst-content h5 .btn-mini .headerlink:before, +.rst-content h6 .btn-mini .headerlink:before, +.rst-content p .btn-mini .headerlink:before, +.rst-content table>caption .btn-mini .headerlink:before, +.rst-content tt.download .btn-mini span:first-child:before, +.wy-menu-vertical li .btn-mini button.toctree-expand:before { + font-size: 14px; + vertical-align: -15% +} + +.rst-content .admonition, +.rst-content .admonition-todo, +.rst-content .attention, +.rst-content .caution, +.rst-content .danger, +.rst-content .error, +.rst-content .hint, +.rst-content .important, +.rst-content .note, +.rst-content .seealso, +.rst-content .tip, +.rst-content .warning, +.wy-alert { + padding: 12px; + line-height: 24px; + margin-bottom: 24px; + background: #e7f2fa +} + +.rst-content .admonition-title, +.wy-alert-title { + font-weight: 700; + display: block; + color: #fff; + background: #6ab0de; + padding: 6px 12px; + margin: -12px -12px 12px +} + +.rst-content .danger, +.rst-content .error, +.rst-content .wy-alert-danger.admonition, +.rst-content .wy-alert-danger.admonition-todo, +.rst-content .wy-alert-danger.attention, +.rst-content .wy-alert-danger.caution, +.rst-content .wy-alert-danger.hint, +.rst-content .wy-alert-danger.important, +.rst-content .wy-alert-danger.note, +.rst-content .wy-alert-danger.seealso, +.rst-content .wy-alert-danger.tip, +.rst-content .wy-alert-danger.warning, +.wy-alert.wy-alert-danger { + background: #fdf3f2 +} + +.rst-content .danger .admonition-title, +.rst-content .danger .wy-alert-title, +.rst-content .error .admonition-title, +.rst-content .error .wy-alert-title, +.rst-content .wy-alert-danger.admonition-todo .admonition-title, +.rst-content .wy-alert-danger.admonition-todo .wy-alert-title, +.rst-content .wy-alert-danger.admonition .admonition-title, +.rst-content .wy-alert-danger.admonition .wy-alert-title, +.rst-content .wy-alert-danger.attention .admonition-title, +.rst-content .wy-alert-danger.attention .wy-alert-title, +.rst-content .wy-alert-danger.caution .admonition-title, +.rst-content .wy-alert-danger.caution .wy-alert-title, +.rst-content .wy-alert-danger.hint .admonition-title, +.rst-content .wy-alert-danger.hint .wy-alert-title, +.rst-content .wy-alert-danger.important .admonition-title, +.rst-content .wy-alert-danger.important .wy-alert-title, +.rst-content .wy-alert-danger.note .admonition-title, +.rst-content .wy-alert-danger.note .wy-alert-title, +.rst-content .wy-alert-danger.seealso .admonition-title, +.rst-content .wy-alert-danger.seealso .wy-alert-title, +.rst-content .wy-alert-danger.tip .admonition-title, +.rst-content .wy-alert-danger.tip .wy-alert-title, +.rst-content .wy-alert-danger.warning .admonition-title, +.rst-content .wy-alert-danger.warning .wy-alert-title, +.rst-content .wy-alert.wy-alert-danger .admonition-title, +.wy-alert.wy-alert-danger .rst-content .admonition-title, +.wy-alert.wy-alert-danger .wy-alert-title { + background: #f29f97 +} + +.rst-content .admonition-todo, +.rst-content .attention, +.rst-content .caution, +.rst-content .warning, +.rst-content .wy-alert-warning.admonition, +.rst-content .wy-alert-warning.danger, +.rst-content .wy-alert-warning.error, +.rst-content .wy-alert-warning.hint, +.rst-content .wy-alert-warning.important, +.rst-content .wy-alert-warning.note, +.rst-content .wy-alert-warning.seealso, +.rst-content .wy-alert-warning.tip, +.wy-alert.wy-alert-warning { + background: #ffedcc +} + +.rst-content .admonition-todo .admonition-title, +.rst-content .admonition-todo .wy-alert-title, +.rst-content .attention .admonition-title, +.rst-content .attention .wy-alert-title, +.rst-content .caution .admonition-title, +.rst-content .caution .wy-alert-title, +.rst-content .warning .admonition-title, +.rst-content .warning .wy-alert-title, +.rst-content .wy-alert-warning.admonition .admonition-title, +.rst-content .wy-alert-warning.admonition .wy-alert-title, +.rst-content .wy-alert-warning.danger .admonition-title, +.rst-content .wy-alert-warning.danger .wy-alert-title, +.rst-content .wy-alert-warning.error .admonition-title, +.rst-content .wy-alert-warning.error .wy-alert-title, +.rst-content .wy-alert-warning.hint .admonition-title, +.rst-content .wy-alert-warning.hint .wy-alert-title, +.rst-content .wy-alert-warning.important .admonition-title, +.rst-content .wy-alert-warning.important .wy-alert-title, +.rst-content .wy-alert-warning.note .admonition-title, +.rst-content .wy-alert-warning.note .wy-alert-title, +.rst-content .wy-alert-warning.seealso .admonition-title, +.rst-content .wy-alert-warning.seealso .wy-alert-title, +.rst-content .wy-alert-warning.tip .admonition-title, +.rst-content .wy-alert-warning.tip .wy-alert-title, +.rst-content .wy-alert.wy-alert-warning .admonition-title, +.wy-alert.wy-alert-warning .rst-content .admonition-title, +.wy-alert.wy-alert-warning .wy-alert-title { + background: #f0b37e +} + +.rst-content .note, +.rst-content .seealso, +.rst-content .wy-alert-info.admonition, +.rst-content .wy-alert-info.admonition-todo, +.rst-content .wy-alert-info.attention, +.rst-content .wy-alert-info.caution, +.rst-content .wy-alert-info.danger, +.rst-content .wy-alert-info.error, +.rst-content .wy-alert-info.hint, +.rst-content .wy-alert-info.important, +.rst-content .wy-alert-info.tip, +.rst-content .wy-alert-info.warning, +.wy-alert.wy-alert-info { + background: #e7f2fa +} + +.rst-content .note .admonition-title, +.rst-content .note .wy-alert-title, +.rst-content .seealso .admonition-title, +.rst-content .seealso .wy-alert-title, +.rst-content .wy-alert-info.admonition-todo .admonition-title, +.rst-content .wy-alert-info.admonition-todo .wy-alert-title, +.rst-content .wy-alert-info.admonition .admonition-title, +.rst-content .wy-alert-info.admonition .wy-alert-title, +.rst-content .wy-alert-info.attention .admonition-title, +.rst-content .wy-alert-info.attention .wy-alert-title, +.rst-content .wy-alert-info.caution .admonition-title, +.rst-content .wy-alert-info.caution .wy-alert-title, +.rst-content .wy-alert-info.danger .admonition-title, +.rst-content .wy-alert-info.danger .wy-alert-title, +.rst-content .wy-alert-info.error .admonition-title, +.rst-content .wy-alert-info.error .wy-alert-title, +.rst-content .wy-alert-info.hint .admonition-title, +.rst-content .wy-alert-info.hint .wy-alert-title, +.rst-content .wy-alert-info.important .admonition-title, +.rst-content .wy-alert-info.important .wy-alert-title, +.rst-content .wy-alert-info.tip .admonition-title, +.rst-content .wy-alert-info.tip .wy-alert-title, +.rst-content .wy-alert-info.warning .admonition-title, +.rst-content .wy-alert-info.warning .wy-alert-title, +.rst-content .wy-alert.wy-alert-info .admonition-title, +.wy-alert.wy-alert-info .rst-content .admonition-title, +.wy-alert.wy-alert-info .wy-alert-title { + background: #6ab0de +} + +.rst-content .hint, +.rst-content .important, +.rst-content .tip, +.rst-content .wy-alert-success.admonition, +.rst-content .wy-alert-success.admonition-todo, +.rst-content .wy-alert-success.attention, +.rst-content .wy-alert-success.caution, +.rst-content .wy-alert-success.danger, +.rst-content .wy-alert-success.error, +.rst-content .wy-alert-success.note, +.rst-content .wy-alert-success.seealso, +.rst-content .wy-alert-success.warning, +.wy-alert.wy-alert-success { + background: #dbfaf4 +} + +.rst-content .hint .admonition-title, +.rst-content .hint .wy-alert-title, +.rst-content .important .admonition-title, +.rst-content .important .wy-alert-title, +.rst-content .tip .admonition-title, +.rst-content .tip .wy-alert-title, +.rst-content .wy-alert-success.admonition-todo .admonition-title, +.rst-content .wy-alert-success.admonition-todo .wy-alert-title, +.rst-content .wy-alert-success.admonition .admonition-title, +.rst-content .wy-alert-success.admonition .wy-alert-title, +.rst-content .wy-alert-success.attention .admonition-title, +.rst-content .wy-alert-success.attention .wy-alert-title, +.rst-content .wy-alert-success.caution .admonition-title, +.rst-content .wy-alert-success.caution .wy-alert-title, +.rst-content .wy-alert-success.danger .admonition-title, +.rst-content .wy-alert-success.danger .wy-alert-title, +.rst-content .wy-alert-success.error .admonition-title, +.rst-content .wy-alert-success.error .wy-alert-title, +.rst-content .wy-alert-success.note .admonition-title, +.rst-content .wy-alert-success.note .wy-alert-title, +.rst-content .wy-alert-success.seealso .admonition-title, +.rst-content .wy-alert-success.seealso .wy-alert-title, +.rst-content .wy-alert-success.warning .admonition-title, +.rst-content .wy-alert-success.warning .wy-alert-title, +.rst-content .wy-alert.wy-alert-success .admonition-title, +.wy-alert.wy-alert-success .rst-content .admonition-title, +.wy-alert.wy-alert-success .wy-alert-title { + background: #1abc9c +} + +.rst-content .wy-alert-neutral.admonition, +.rst-content .wy-alert-neutral.admonition-todo, +.rst-content .wy-alert-neutral.attention, +.rst-content .wy-alert-neutral.caution, +.rst-content .wy-alert-neutral.danger, +.rst-content .wy-alert-neutral.error, +.rst-content .wy-alert-neutral.hint, +.rst-content .wy-alert-neutral.important, +.rst-content .wy-alert-neutral.note, +.rst-content .wy-alert-neutral.seealso, +.rst-content .wy-alert-neutral.tip, +.rst-content .wy-alert-neutral.warning, +.wy-alert.wy-alert-neutral { + background: #f3f6f6 +} + +.rst-content .wy-alert-neutral.admonition-todo .admonition-title, +.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title, +.rst-content .wy-alert-neutral.admonition .admonition-title, +.rst-content .wy-alert-neutral.admonition .wy-alert-title, +.rst-content .wy-alert-neutral.attention .admonition-title, +.rst-content .wy-alert-neutral.attention .wy-alert-title, +.rst-content .wy-alert-neutral.caution .admonition-title, +.rst-content .wy-alert-neutral.caution .wy-alert-title, +.rst-content .wy-alert-neutral.danger .admonition-title, +.rst-content .wy-alert-neutral.danger .wy-alert-title, +.rst-content .wy-alert-neutral.error .admonition-title, +.rst-content .wy-alert-neutral.error .wy-alert-title, +.rst-content .wy-alert-neutral.hint .admonition-title, +.rst-content .wy-alert-neutral.hint .wy-alert-title, +.rst-content .wy-alert-neutral.important .admonition-title, +.rst-content .wy-alert-neutral.important .wy-alert-title, +.rst-content .wy-alert-neutral.note .admonition-title, +.rst-content .wy-alert-neutral.note .wy-alert-title, +.rst-content .wy-alert-neutral.seealso .admonition-title, +.rst-content .wy-alert-neutral.seealso .wy-alert-title, +.rst-content .wy-alert-neutral.tip .admonition-title, +.rst-content .wy-alert-neutral.tip .wy-alert-title, +.rst-content .wy-alert-neutral.warning .admonition-title, +.rst-content .wy-alert-neutral.warning .wy-alert-title, +.rst-content .wy-alert.wy-alert-neutral .admonition-title, +.wy-alert.wy-alert-neutral .rst-content .admonition-title, +.wy-alert.wy-alert-neutral .wy-alert-title { + color: #404040; + background: #e1e4e5 +} + +.rst-content .wy-alert-neutral.admonition-todo a, +.rst-content .wy-alert-neutral.admonition a, +.rst-content .wy-alert-neutral.attention a, +.rst-content .wy-alert-neutral.caution a, +.rst-content .wy-alert-neutral.danger a, +.rst-content .wy-alert-neutral.error a, +.rst-content .wy-alert-neutral.hint a, +.rst-content .wy-alert-neutral.important a, +.rst-content .wy-alert-neutral.note a, +.rst-content .wy-alert-neutral.seealso a, +.rst-content .wy-alert-neutral.tip a, +.rst-content .wy-alert-neutral.warning a, +.wy-alert.wy-alert-neutral a { + color: #2980b9 +} + +.rst-content .admonition-todo p:last-child, +.rst-content .admonition p:last-child, +.rst-content .attention p:last-child, +.rst-content .caution p:last-child, +.rst-content .danger p:last-child, +.rst-content .error p:last-child, +.rst-content .hint p:last-child, +.rst-content .important p:last-child, +.rst-content .note p:last-child, +.rst-content .seealso p:last-child, +.rst-content .tip p:last-child, +.rst-content .warning p:last-child, +.wy-alert p:last-child { + margin-bottom: 0 +} + +.wy-tray-container { + position: fixed; + bottom: 0; + left: 0; + z-index: 600 +} + +.wy-tray-container li { + display: block; + width: 300px; + background: transparent; + color: #fff; + text-align: center; + box-shadow: 0 5px 5px 0 rgba(0, 0, 0, .1); + padding: 0 24px; + min-width: 20%; + opacity: 0; + height: 0; + line-height: 56px; + overflow: hidden; + -webkit-transition: all .3s ease-in; + -moz-transition: all .3s ease-in; + transition: all .3s ease-in +} + +.wy-tray-container li.wy-tray-item-success { + background: #27ae60 +} + +.wy-tray-container li.wy-tray-item-info { + background: #2980b9 +} + +.wy-tray-container li.wy-tray-item-warning { + background: #e67e22 +} + +.wy-tray-container li.wy-tray-item-danger { + background: #e74c3c +} + +.wy-tray-container li.on { + opacity: 1; + height: 56px +} + +@media screen and (max-width:768px) { + .wy-tray-container { + bottom: auto; + top: 0; + width: 100% + } + + .wy-tray-container li { + width: 100% + } +} + +button { + font-size: 100%; + margin: 0; + vertical-align: baseline; + *vertical-align: middle; + cursor: pointer; + line-height: normal; + -webkit-appearance: button; + *overflow: visible +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0 +} + +button[disabled] { + cursor: default +} + +.btn { + display: inline-block; + border-radius: 2px; + line-height: normal; + white-space: nowrap; + text-align: center; + cursor: pointer; + font-size: 100%; + padding: 6px 12px 8px; + color: #fff; + border: 1px solid rgba(0, 0, 0, .1); + background-color: #27ae60; + text-decoration: none; + font-weight: 400; + font-family: Lato, proxima-nova, Helvetica Neue, Arial, sans-serif; + box-shadow: inset 0 1px 2px -1px hsla(0, 0%, 100%, .5), inset 0 -2px 0 0 rgba(0, 0, 0, .1); + outline-none: false; + vertical-align: middle; + *display: inline; + zoom: 1; + -webkit-user-drag: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-transition: all .1s linear; + -moz-transition: all .1s linear; + transition: all .1s linear +} + +.btn-hover { + background: #2e8ece; + color: #fff +} + +.btn:hover { + background: #2cc36b; + color: #fff +} + +.btn:focus { + background: #2cc36b; + outline: 0 +} + +.btn:active { + box-shadow: inset 0 -1px 0 0 rgba(0, 0, 0, .05), inset 0 2px 0 0 rgba(0, 0, 0, .1); + padding: 8px 12px 6px +} + +.btn:visited { + color: #fff +} + +.btn-disabled, +.btn-disabled:active, +.btn-disabled:focus, +.btn-disabled:hover, +.btn:disabled { + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + filter: alpha(opacity=40); + opacity: .4; + cursor: not-allowed; + box-shadow: none +} + +.btn::-moz-focus-inner { + padding: 0; + border: 0 +} + +.btn-small { + font-size: 80% +} + +.btn-info { + background-color: #2980b9 !important +} + +.btn-info:hover { + background-color: #2e8ece !important +} + +.btn-neutral { + background-color: #f3f6f6 !important; + color: #404040 !important +} + +.btn-neutral:hover { + background-color: #e5ebeb !important; + color: #404040 +} + +.btn-neutral:visited { + color: #404040 !important +} + +.btn-success { + background-color: #27ae60 !important +} + +.btn-success:hover { + background-color: #295 !important +} + +.btn-danger { + background-color: #e74c3c !important +} + +.btn-danger:hover { + background-color: #ea6153 !important +} + +.btn-warning { + background-color: #e67e22 !important +} + +.btn-warning:hover { + background-color: #e98b39 !important +} + +.btn-invert { + background-color: #222 +} + +.btn-invert:hover { + background-color: #2f2f2f !important +} + +.btn-link { + background-color: transparent !important; + color: #2980b9; + box-shadow: none; + border-color: transparent !important +} + +.btn-link:active, +.btn-link:hover { + background-color: transparent !important; + color: #409ad5 !important; + box-shadow: none +} + +.btn-link:visited { + color: #9b59b6 +} + +.wy-btn-group .btn, +.wy-control .btn { + vertical-align: middle +} + +.wy-btn-group { + margin-bottom: 24px; + *zoom: 1 +} + +.wy-btn-group:after, +.wy-btn-group:before { + display: table; + content: "" +} + +.wy-btn-group:after { + clear: both +} + +.wy-dropdown { + position: relative; + display: inline-block +} + +.wy-dropdown-active .wy-dropdown-menu { + display: block +} + +.wy-dropdown-menu { + position: absolute; + left: 0; + display: none; + float: left; + top: 100%; + min-width: 100%; + background: #fcfcfc; + z-index: 100; + border: 1px solid #cfd7dd; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .1); + padding: 12px +} + +.wy-dropdown-menu>dd>a { + display: block; + clear: both; + color: #404040; + white-space: nowrap; + font-size: 90%; + padding: 0 12px; + cursor: pointer +} + +.wy-dropdown-menu>dd>a:hover { + background: #2980b9; + color: #fff +} + +.wy-dropdown-menu>dd.divider { + border-top: 1px solid #cfd7dd; + margin: 6px 0 +} + +.wy-dropdown-menu>dd.search { + padding-bottom: 12px +} + +.wy-dropdown-menu>dd.search input[type=search] { + width: 100% +} + +.wy-dropdown-menu>dd.call-to-action { + background: #e3e3e3; + text-transform: uppercase; + font-weight: 500; + font-size: 80% +} + +.wy-dropdown-menu>dd.call-to-action:hover { + background: #e3e3e3 +} + +.wy-dropdown-menu>dd.call-to-action .btn { + color: #fff +} + +.wy-dropdown.wy-dropdown-up .wy-dropdown-menu { + bottom: 100%; + top: auto; + left: auto; + right: 0 +} + +.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu { + background: #fcfcfc; + margin-top: 2px +} + +.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a { + padding: 6px 12px +} + +.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover { + background: #2980b9; + color: #fff +} + +.wy-dropdown.wy-dropdown-left .wy-dropdown-menu { + right: 0; + left: auto; + text-align: right +} + +.wy-dropdown-arrow:before { + content: " "; + border-bottom: 5px solid #f5f5f5; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + position: absolute; + display: block; + top: -4px; + left: 50%; + margin-left: -3px +} + +.wy-dropdown-arrow.wy-dropdown-arrow-left:before { + left: 11px +} + +.wy-form-stacked select { + display: block +} + +.wy-form-aligned .wy-help-inline, +.wy-form-aligned input, +.wy-form-aligned label, +.wy-form-aligned select, +.wy-form-aligned textarea { + display: inline-block; + *display: inline; + *zoom: 1; + vertical-align: middle +} + +.wy-form-aligned .wy-control-group>label { + display: inline-block; + vertical-align: middle; + width: 10em; + margin: 6px 12px 0 0; + float: left +} + +.wy-form-aligned .wy-control { + float: left +} + +.wy-form-aligned .wy-control label { + display: block +} + +.wy-form-aligned .wy-control select { + margin-top: 6px +} + +fieldset { + margin: 0 +} + +fieldset, +legend { + border: 0; + padding: 0 +} + +legend { + width: 100%; + white-space: normal; + margin-bottom: 24px; + font-size: 150%; + *margin-left: -7px +} + +label, +legend { + display: block +} + +label { + margin: 0 0 .3125em; + color: #333; + font-size: 90% +} + +input, +select, +textarea { + font-size: 100%; + margin: 0; + vertical-align: baseline; + *vertical-align: middle +} + +.wy-control-group { + margin-bottom: 24px; + max-width: 1200px; + margin-left: auto; + margin-right: auto; + *zoom: 1 +} + +.wy-control-group:after, +.wy-control-group:before { + display: table; + content: "" +} + +.wy-control-group:after { + clear: both +} + +.wy-control-group.wy-control-group-required>label:after { + content: " *"; + color: #e74c3c +} + +.wy-control-group .wy-form-full, +.wy-control-group .wy-form-halves, +.wy-control-group .wy-form-thirds { + padding-bottom: 12px +} + +.wy-control-group .wy-form-full input[type=color], +.wy-control-group .wy-form-full input[type=date], +.wy-control-group .wy-form-full input[type=datetime-local], +.wy-control-group .wy-form-full input[type=datetime], +.wy-control-group .wy-form-full input[type=email], +.wy-control-group .wy-form-full input[type=month], +.wy-control-group .wy-form-full input[type=number], +.wy-control-group .wy-form-full input[type=password], +.wy-control-group .wy-form-full input[type=search], +.wy-control-group .wy-form-full input[type=tel], +.wy-control-group .wy-form-full input[type=text], +.wy-control-group .wy-form-full input[type=time], +.wy-control-group .wy-form-full input[type=url], +.wy-control-group .wy-form-full input[type=week], +.wy-control-group .wy-form-full select, +.wy-control-group .wy-form-halves input[type=color], +.wy-control-group .wy-form-halves input[type=date], +.wy-control-group .wy-form-halves input[type=datetime-local], +.wy-control-group .wy-form-halves input[type=datetime], +.wy-control-group .wy-form-halves input[type=email], +.wy-control-group .wy-form-halves input[type=month], +.wy-control-group .wy-form-halves input[type=number], +.wy-control-group .wy-form-halves input[type=password], +.wy-control-group .wy-form-halves input[type=search], +.wy-control-group .wy-form-halves input[type=tel], +.wy-control-group .wy-form-halves input[type=text], +.wy-control-group .wy-form-halves input[type=time], +.wy-control-group .wy-form-halves input[type=url], +.wy-control-group .wy-form-halves input[type=week], +.wy-control-group .wy-form-halves select, +.wy-control-group .wy-form-thirds input[type=color], +.wy-control-group .wy-form-thirds input[type=date], +.wy-control-group .wy-form-thirds input[type=datetime-local], +.wy-control-group .wy-form-thirds input[type=datetime], +.wy-control-group .wy-form-thirds input[type=email], +.wy-control-group .wy-form-thirds input[type=month], +.wy-control-group .wy-form-thirds input[type=number], +.wy-control-group .wy-form-thirds input[type=password], +.wy-control-group .wy-form-thirds input[type=search], +.wy-control-group .wy-form-thirds input[type=tel], +.wy-control-group .wy-form-thirds input[type=text], +.wy-control-group .wy-form-thirds input[type=time], +.wy-control-group .wy-form-thirds input[type=url], +.wy-control-group .wy-form-thirds input[type=week], +.wy-control-group .wy-form-thirds select { + width: 100% +} + +.wy-control-group .wy-form-full { + float: left; + display: block; + width: 100%; + margin-right: 0 +} + +.wy-control-group .wy-form-full:last-child { + margin-right: 0 +} + +.wy-control-group .wy-form-halves { + float: left; + display: block; + margin-right: 2.35765%; + width: 48.82117% +} + +.wy-control-group .wy-form-halves:last-child, +.wy-control-group .wy-form-halves:nth-of-type(2n) { + margin-right: 0 +} + +.wy-control-group .wy-form-halves:nth-of-type(odd) { + clear: left +} + +.wy-control-group .wy-form-thirds { + float: left; + display: block; + margin-right: 2.35765%; + width: 31.76157% +} + +.wy-control-group .wy-form-thirds:last-child, +.wy-control-group .wy-form-thirds:nth-of-type(3n) { + margin-right: 0 +} + +.wy-control-group .wy-form-thirds:nth-of-type(3n+1) { + clear: left +} + +.wy-control-group.wy-control-group-no-input .wy-control, +.wy-control-no-input { + margin: 6px 0 0; + font-size: 90% +} + +.wy-control-no-input { + display: inline-block +} + +.wy-control-group.fluid-input input[type=color], +.wy-control-group.fluid-input input[type=date], +.wy-control-group.fluid-input input[type=datetime-local], +.wy-control-group.fluid-input input[type=datetime], +.wy-control-group.fluid-input input[type=email], +.wy-control-group.fluid-input input[type=month], +.wy-control-group.fluid-input input[type=number], +.wy-control-group.fluid-input input[type=password], +.wy-control-group.fluid-input input[type=search], +.wy-control-group.fluid-input input[type=tel], +.wy-control-group.fluid-input input[type=text], +.wy-control-group.fluid-input input[type=time], +.wy-control-group.fluid-input input[type=url], +.wy-control-group.fluid-input input[type=week] { + width: 100% +} + +.wy-form-message-inline { + padding-left: .3em; + color: #666; + font-size: 90% +} + +.wy-form-message { + display: block; + color: #999; + font-size: 70%; + margin-top: .3125em; + font-style: italic +} + +.wy-form-message p { + font-size: inherit; + font-style: italic; + margin-bottom: 6px +} + +.wy-form-message p:last-child { + margin-bottom: 0 +} + +input { + line-height: normal +} + +input[type=button], +input[type=reset], +input[type=submit] { + -webkit-appearance: button; + cursor: pointer; + font-family: Lato, proxima-nova, Helvetica Neue, Arial, sans-serif; + *overflow: visible +} + +input[type=color], +input[type=date], +input[type=datetime-local], +input[type=datetime], +input[type=email], +input[type=month], +input[type=number], +input[type=password], +input[type=search], +input[type=tel], +input[type=text], +input[type=time], +input[type=url], +input[type=week] { + -webkit-appearance: none; + padding: 6px; + display: inline-block; + border: 1px solid #ccc; + font-size: 80%; + font-family: Lato, proxima-nova, Helvetica Neue, Arial, sans-serif; + box-shadow: inset 0 1px 3px #ddd; + border-radius: 0; + -webkit-transition: border .3s linear; + -moz-transition: border .3s linear; + transition: border .3s linear +} + +input[type=datetime-local] { + padding: .34375em .625em +} + +input[disabled] { + cursor: default +} + +input[type=checkbox], +input[type=radio] { + padding: 0; + margin-right: .3125em; + *height: 13px; + *width: 13px +} + +input[type=checkbox], +input[type=radio], +input[type=search] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +input[type=search]::-webkit-search-cancel-button, +input[type=search]::-webkit-search-decoration { + -webkit-appearance: none +} + +input[type=color]:focus, +input[type=date]:focus, +input[type=datetime-local]:focus, +input[type=datetime]:focus, +input[type=email]:focus, +input[type=month]:focus, +input[type=number]:focus, +input[type=password]:focus, +input[type=search]:focus, +input[type=tel]:focus, +input[type=text]:focus, +input[type=time]:focus, +input[type=url]:focus, +input[type=week]:focus { + outline: 0; + outline: thin dotted\9; + border-color: #333 +} + +input.no-focus:focus { + border-color: #ccc !important +} + +input[type=checkbox]:focus, +input[type=file]:focus, +input[type=radio]:focus { + outline: thin dotted #333; + outline: 1px auto #129fea +} + +input[type=color][disabled], +input[type=date][disabled], +input[type=datetime-local][disabled], +input[type=datetime][disabled], +input[type=email][disabled], +input[type=month][disabled], +input[type=number][disabled], +input[type=password][disabled], +input[type=search][disabled], +input[type=tel][disabled], +input[type=text][disabled], +input[type=time][disabled], +input[type=url][disabled], +input[type=week][disabled] { + cursor: not-allowed; + background-color: #fafafa +} + +input:focus:invalid, +select:focus:invalid, +textarea:focus:invalid { + color: #e74c3c; + border: 1px solid #e74c3c +} + +input:focus:invalid:focus, +select:focus:invalid:focus, +textarea:focus:invalid:focus { + border-color: #e74c3c +} + +input[type=checkbox]:focus:invalid:focus, +input[type=file]:focus:invalid:focus, +input[type=radio]:focus:invalid:focus { + outline-color: #e74c3c +} + +input.wy-input-large { + padding: 12px; + font-size: 100% +} + +textarea { + overflow: auto; + vertical-align: top; + width: 100%; + font-family: Lato, proxima-nova, Helvetica Neue, Arial, sans-serif +} + +select, +textarea { + padding: .5em .625em; + display: inline-block; + border: 1px solid #ccc; + font-size: 80%; + box-shadow: inset 0 1px 3px #ddd; + -webkit-transition: border .3s linear; + -moz-transition: border .3s linear; + transition: border .3s linear +} + +select { + border: 1px solid #ccc; + background-color: #fff +} + +select[multiple] { + height: auto +} + +select:focus, +textarea:focus { + outline: 0 +} + +input[readonly], +select[disabled], +select[readonly], +textarea[disabled], +textarea[readonly] { + cursor: not-allowed; + background-color: #fafafa +} + +input[type=checkbox][disabled], +input[type=radio][disabled] { + cursor: not-allowed +} + +.wy-checkbox, +.wy-radio { + margin: 6px 0; + color: #404040; + display: block +} + +.wy-checkbox input, +.wy-radio input { + vertical-align: baseline +} + +.wy-form-message-inline { + display: inline-block; + *display: inline; + *zoom: 1; + vertical-align: middle +} + +.wy-input-prefix, +.wy-input-suffix { + white-space: nowrap; + padding: 6px +} + +.wy-input-prefix .wy-input-context, +.wy-input-suffix .wy-input-context { + line-height: 27px; + padding: 0 8px; + display: inline-block; + font-size: 80%; + background-color: #f3f6f6; + border: 1px solid #ccc; + color: #999 +} + +.wy-input-suffix .wy-input-context { + border-left: 0 +} + +.wy-input-prefix .wy-input-context { + border-right: 0 +} + +.wy-switch { + position: relative; + display: block; + height: 24px; + margin-top: 12px; + cursor: pointer +} + +.wy-switch:before { + left: 0; + top: 0; + width: 36px; + height: 12px; + background: #ccc +} + +.wy-switch:after, +.wy-switch:before { + position: absolute; + content: ""; + display: block; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -moz-transition: all .2s ease-in-out; + transition: all .2s ease-in-out +} + +.wy-switch:after { + width: 18px; + height: 18px; + background: #999; + left: -3px; + top: -3px +} + +.wy-switch span { + position: absolute; + left: 48px; + display: block; + font-size: 12px; + color: #ccc; + line-height: 1 +} + +.wy-switch.active:before { + background: #1e8449 +} + +.wy-switch.active:after { + left: 24px; + background: #27ae60 +} + +.wy-switch.disabled { + cursor: not-allowed; + opacity: .8 +} + +.wy-control-group.wy-control-group-error .wy-form-message, +.wy-control-group.wy-control-group-error>label { + color: #e74c3c +} + +.wy-control-group.wy-control-group-error input[type=color], +.wy-control-group.wy-control-group-error input[type=date], +.wy-control-group.wy-control-group-error input[type=datetime-local], +.wy-control-group.wy-control-group-error input[type=datetime], +.wy-control-group.wy-control-group-error input[type=email], +.wy-control-group.wy-control-group-error input[type=month], +.wy-control-group.wy-control-group-error input[type=number], +.wy-control-group.wy-control-group-error input[type=password], +.wy-control-group.wy-control-group-error input[type=search], +.wy-control-group.wy-control-group-error input[type=tel], +.wy-control-group.wy-control-group-error input[type=text], +.wy-control-group.wy-control-group-error input[type=time], +.wy-control-group.wy-control-group-error input[type=url], +.wy-control-group.wy-control-group-error input[type=week], +.wy-control-group.wy-control-group-error textarea { + border: 1px solid #e74c3c +} + +.wy-inline-validate { + white-space: nowrap +} + +.wy-inline-validate .wy-input-context { + padding: .5em .625em; + display: inline-block; + font-size: 80% +} + +.wy-inline-validate.wy-inline-validate-success .wy-input-context { + color: #27ae60 +} + +.wy-inline-validate.wy-inline-validate-danger .wy-input-context { + color: #e74c3c +} + +.wy-inline-validate.wy-inline-validate-warning .wy-input-context { + color: #e67e22 +} + +.wy-inline-validate.wy-inline-validate-info .wy-input-context { + color: #2980b9 +} + +.rotate-90 { + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -ms-transform: rotate(90deg); + -o-transform: rotate(90deg); + transform: rotate(90deg) +} + +.rotate-180 { + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg) +} + +.rotate-270 { + -webkit-transform: rotate(270deg); + -moz-transform: rotate(270deg); + -ms-transform: rotate(270deg); + -o-transform: rotate(270deg); + transform: rotate(270deg) +} + +.mirror { + -webkit-transform: scaleX(-1); + -moz-transform: scaleX(-1); + -ms-transform: scaleX(-1); + -o-transform: scaleX(-1); + transform: scaleX(-1) +} + +.mirror.rotate-90 { + -webkit-transform: scaleX(-1) rotate(90deg); + -moz-transform: scaleX(-1) rotate(90deg); + -ms-transform: scaleX(-1) rotate(90deg); + -o-transform: scaleX(-1) rotate(90deg); + transform: scaleX(-1) rotate(90deg) +} + +.mirror.rotate-180 { + -webkit-transform: scaleX(-1) rotate(180deg); + -moz-transform: scaleX(-1) rotate(180deg); + -ms-transform: scaleX(-1) rotate(180deg); + -o-transform: scaleX(-1) rotate(180deg); + transform: scaleX(-1) rotate(180deg) +} + +.mirror.rotate-270 { + -webkit-transform: scaleX(-1) rotate(270deg); + -moz-transform: scaleX(-1) rotate(270deg); + -ms-transform: scaleX(-1) rotate(270deg); + -o-transform: scaleX(-1) rotate(270deg); + transform: scaleX(-1) rotate(270deg) +} + +@media only screen and (max-width:480px) { + .wy-form button[type=submit] { + margin: .7em 0 0 + } + + .wy-form input[type=color], + .wy-form input[type=date], + .wy-form input[type=datetime-local], + .wy-form input[type=datetime], + .wy-form input[type=email], + .wy-form input[type=month], + .wy-form input[type=number], + .wy-form input[type=password], + .wy-form input[type=search], + .wy-form input[type=tel], + .wy-form input[type=text], + .wy-form input[type=time], + .wy-form input[type=url], + .wy-form input[type=week], + .wy-form label { + margin-bottom: .3em; + display: block + } + + .wy-form input[type=color], + .wy-form input[type=date], + .wy-form input[type=datetime-local], + .wy-form input[type=datetime], + .wy-form input[type=email], + .wy-form input[type=month], + .wy-form input[type=number], + .wy-form input[type=password], + .wy-form input[type=search], + .wy-form input[type=tel], + .wy-form input[type=time], + .wy-form input[type=url], + .wy-form input[type=week] { + margin-bottom: 0 + } + + .wy-form-aligned .wy-control-group label { + margin-bottom: .3em; + text-align: left; + display: block; + width: 100% + } + + .wy-form-aligned .wy-control { + margin: 1.5em 0 0 + } + + .wy-form-message, + .wy-form-message-inline, + .wy-form .wy-help-inline { + display: block; + font-size: 80%; + padding: 6px 0 + } +} + +@media screen and (max-width:768px) { + .tablet-hide { + display: none + } +} + +@media screen and (max-width:480px) { + .mobile-hide { + display: none + } +} + +.float-left { + float: left +} + +.float-right { + float: right +} + +.full-width { + width: 100% +} + +.rst-content table.docutils, +.rst-content table.field-list, +.wy-table { + border-collapse: collapse; + border-spacing: 0; + empty-cells: show; + margin-bottom: 24px +} + +.rst-content table.docutils caption, +.rst-content table.field-list caption, +.wy-table caption { + color: #000; + font: italic 85%/1 arial, sans-serif; + padding: 1em 0; + text-align: center +} + +.rst-content table.docutils td, +.rst-content table.docutils th, +.rst-content table.field-list td, +.rst-content table.field-list th, +.wy-table td, +.wy-table th { + font-size: 90%; + margin: 0; + overflow: visible; + padding: 8px 16px +} + +.rst-content table.docutils td:first-child, +.rst-content table.docutils th:first-child, +.rst-content table.field-list td:first-child, +.rst-content table.field-list th:first-child, +.wy-table td:first-child, +.wy-table th:first-child { + border-left-width: 0 +} + +.rst-content table.docutils thead, +.rst-content table.field-list thead, +.wy-table thead { + color: #000; + text-align: left; + vertical-align: bottom; + white-space: nowrap +} + +.rst-content table.docutils thead th, +.rst-content table.field-list thead th, +.wy-table thead th { + font-weight: 700; + border-bottom: 2px solid #e1e4e5 +} + +.rst-content table.docutils td, +.rst-content table.field-list td, +.wy-table td { + background-color: transparent; + vertical-align: middle +} + +.rst-content table.docutils td p, +.rst-content table.field-list td p, +.wy-table td p { + line-height: 18px +} + +.rst-content table.docutils td p:last-child, +.rst-content table.field-list td p:last-child, +.wy-table td p:last-child { + margin-bottom: 0 +} + +.rst-content table.docutils .wy-table-cell-min, +.rst-content table.field-list .wy-table-cell-min, +.wy-table .wy-table-cell-min { + width: 1%; + padding-right: 0 +} + +.rst-content table.docutils .wy-table-cell-min input[type=checkbox], +.rst-content table.field-list .wy-table-cell-min input[type=checkbox], +.wy-table .wy-table-cell-min input[type=checkbox] { + margin: 0 +} + +.wy-table-secondary { + color: grey; + font-size: 90% +} + +.wy-table-tertiary { + color: grey; + font-size: 80% +} + +.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td, +.wy-table-backed, +.wy-table-odd td, +.wy-table-striped tr:nth-child(2n-1) td { + background-color: #f3f6f6 +} + +.rst-content table.docutils, +.wy-table-bordered-all { + border: 1px solid #e1e4e5 +} + +.rst-content table.docutils td, +.wy-table-bordered-all td { + border-bottom: 1px solid #e1e4e5; + border-left: 1px solid #e1e4e5 +} + +.rst-content table.docutils tbody>tr:last-child td, +.wy-table-bordered-all tbody>tr:last-child td { + border-bottom-width: 0 +} + +.wy-table-bordered { + border: 1px solid #e1e4e5 +} + +.wy-table-bordered-rows td { + border-bottom: 1px solid #e1e4e5 +} + +.wy-table-bordered-rows tbody>tr:last-child td { + border-bottom-width: 0 +} + +.wy-table-horizontal td, +.wy-table-horizontal th { + border-width: 0 0 1px; + border-bottom: 1px solid #e1e4e5 +} + +.wy-table-horizontal tbody>tr:last-child td { + border-bottom-width: 0 +} + +.wy-table-responsive { + margin-bottom: 24px; + max-width: 100%; + overflow: auto +} + +.wy-table-responsive table { + margin-bottom: 0 !important +} + +.wy-table-responsive table td { + white-space: nowrap +} + +.wy-table-responsive table th { + white-space: nowrap +} + +a { + color: #2980b9; + text-decoration: none; + cursor: pointer +} + +a:hover { + color: #3091d1 +} + +a:visited { + color: #9b59b6 +} + +html { + height: 100% +} + +body, +html { + overflow-x: hidden +} + +body { + font-family: Lato, proxima-nova, Helvetica Neue, Arial, sans-serif; + font-weight: 400; + color: #404040; + min-height: 100%; + background: #edf0f2 +} + +.wy-text-left { + text-align: left +} + +.wy-text-center { + text-align: center +} + +.wy-text-right { + text-align: right +} + +.wy-text-large { + font-size: 120% +} + +.wy-text-normal { + font-size: 100% +} + +.wy-text-small, +small { + font-size: 80% +} + +.wy-text-strike { + text-decoration: line-through +} + +.wy-text-warning { + color: #e67e22 !important +} + +a.wy-text-warning:hover { + color: #eb9950 !important +} + +.wy-text-info { + color: #2980b9 !important +} + +a.wy-text-info:hover { + color: #409ad5 !important +} + +.wy-text-success { + color: #27ae60 !important +} + +a.wy-text-success:hover { + color: #36d278 !important +} + +.wy-text-danger { + color: #e74c3c !important +} + +a.wy-text-danger:hover { + color: #ed7669 !important +} + +.wy-text-neutral { + color: #404040 !important +} + +a.wy-text-neutral:hover { + color: #595959 !important +} + +.rst-content .toctree-wrapper>p.caption, +h1, +h2, +h3, +h4, +h5, +h6, +legend { + margin-top: 0; + font-weight: 700; + font-family: Roboto Slab, ff-tisa-web-pro, Georgia, Arial, sans-serif +} + +p { + line-height: 24px; + font-size: 16px; + margin: 0 0 24px +} + +h1 { + font-size: 175% +} + +.rst-content .toctree-wrapper>p.caption, +h2 { + font-size: 150% +} + +h3 { + font-size: 125% +} + +h4 { + font-size: 115% +} + +h5 { + font-size: 110% +} + +h6 { + font-size: 100% +} + +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #e1e4e5; + margin: 24px 0; + padding: 0 +} + +.rst-content code, +.rst-content tt, +code { + white-space: nowrap; + max-width: 100%; + background: #fff; + border: 1px solid #e1e4e5; + font-size: 75%; + padding: 0 5px; + font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, Courier, monospace; + color: #e74c3c; + overflow-x: auto +} + +.rst-content tt.code-large, +code.code-large { + font-size: 90% +} + +.rst-content .section ul, +.rst-content .toctree-wrapper ul, +.rst-content section ul, +.wy-plain-list-disc, +article ul { + list-style: disc; + line-height: 24px; + margin-bottom: 24px +} + +.rst-content .section ul li, +.rst-content .toctree-wrapper ul li, +.rst-content section ul li, +.wy-plain-list-disc li, +article ul li { + list-style: disc; + margin-left: 24px +} + +.rst-content .section ul li p:last-child, +.rst-content .section ul li ul, +.rst-content .toctree-wrapper ul li p:last-child, +.rst-content .toctree-wrapper ul li ul, +.rst-content section ul li p:last-child, +.rst-content section ul li ul, +.wy-plain-list-disc li p:last-child, +.wy-plain-list-disc li ul, +article ul li p:last-child, +article ul li ul { + margin-bottom: 0 +} + +.rst-content .section ul li li, +.rst-content .toctree-wrapper ul li li, +.rst-content section ul li li, +.wy-plain-list-disc li li, +article ul li li { + list-style: circle +} + +.rst-content .section ul li li li, +.rst-content .toctree-wrapper ul li li li, +.rst-content section ul li li li, +.wy-plain-list-disc li li li, +article ul li li li { + list-style: square +} + +.rst-content .section ul li ol li, +.rst-content .toctree-wrapper ul li ol li, +.rst-content section ul li ol li, +.wy-plain-list-disc li ol li, +article ul li ol li { + list-style: decimal +} + +.rst-content .section ol, +.rst-content .section ol.arabic, +.rst-content .toctree-wrapper ol, +.rst-content .toctree-wrapper ol.arabic, +.rst-content section ol, +.rst-content section ol.arabic, +.wy-plain-list-decimal, +article ol { + list-style: decimal; + line-height: 24px; + margin-bottom: 24px +} + +.rst-content .section ol.arabic li, +.rst-content .section ol li, +.rst-content .toctree-wrapper ol.arabic li, +.rst-content .toctree-wrapper ol li, +.rst-content section ol.arabic li, +.rst-content section ol li, +.wy-plain-list-decimal li, +article ol li { + list-style: decimal; + margin-left: 24px +} + +.rst-content .section ol.arabic li ul, +.rst-content .section ol li p:last-child, +.rst-content .section ol li ul, +.rst-content .toctree-wrapper ol.arabic li ul, +.rst-content .toctree-wrapper ol li p:last-child, +.rst-content .toctree-wrapper ol li ul, +.rst-content section ol.arabic li ul, +.rst-content section ol li p:last-child, +.rst-content section ol li ul, +.wy-plain-list-decimal li p:last-child, +.wy-plain-list-decimal li ul, +article ol li p:last-child, +article ol li ul { + margin-bottom: 0 +} + +.rst-content .section ol.arabic li ul li, +.rst-content .section ol li ul li, +.rst-content .toctree-wrapper ol.arabic li ul li, +.rst-content .toctree-wrapper ol li ul li, +.rst-content section ol.arabic li ul li, +.rst-content section ol li ul li, +.wy-plain-list-decimal li ul li, +article ol li ul li { + list-style: disc +} + +.wy-breadcrumbs { + *zoom: 1 +} + +.wy-breadcrumbs:after, +.wy-breadcrumbs:before { + display: table; + content: "" +} + +.wy-breadcrumbs:after { + clear: both +} + +.wy-breadcrumbs>li { + display: inline-block; + padding-top: 5px +} + +.wy-breadcrumbs>li.wy-breadcrumbs-aside { + float: right; + padding-left: 5px; + font-size: 0.8em; +} + +.rst-content .wy-breadcrumbs>li code, +.rst-content .wy-breadcrumbs>li tt, +.wy-breadcrumbs>li .rst-content tt, +.wy-breadcrumbs>li code { + all: inherit; + color: inherit +} + +.breadcrumb-item:before { + content: "/"; + color: #bbb; + font-size: 13px; + padding: 0 6px 0 3px +} + +.wy-breadcrumbs-extra { + margin-bottom: 0; + color: #b3b3b3; + font-size: 80%; + display: inline-block +} + +@media screen and (max-width:480px) { + + .wy-breadcrumbs-extra, + .wy-breadcrumbs li.wy-breadcrumbs-aside { + display: none + } +} + +@media print { + .wy-breadcrumbs li.wy-breadcrumbs-aside { + display: none + } +} + +html { + font-size: 16px +} + +.wy-affix { + position: fixed; + top: 1.618em +} + +.wy-menu a:hover { + text-decoration: none +} + +.wy-menu-horiz { + *zoom: 1 +} + +.wy-menu-horiz:after, +.wy-menu-horiz:before { + display: table; + content: "" +} + +.wy-menu-horiz:after { + clear: both +} + +.wy-menu-horiz li, +.wy-menu-horiz ul { + display: inline-block +} + +.wy-menu-horiz li:hover { + background: hsla(0, 0%, 100%, .1) +} + +.wy-menu-horiz li.divide-left { + border-left: 1px solid #404040 +} + +.wy-menu-horiz li.divide-right { + border-right: 1px solid #404040 +} + +.wy-menu-horiz a { + height: 32px; + display: inline-block; + line-height: 32px; + padding: 0 16px +} + +.wy-menu-vertical { + width: 300px +} + +.wy-menu-vertical header, +.wy-menu-vertical p.caption { + color: #55a5d9; + height: 32px; + line-height: 32px; + padding: 0 1.618em; + margin: 12px 0 0; + display: block; + font-weight: 700; + text-transform: uppercase; + font-size: 85%; + white-space: nowrap +} + +.wy-menu-vertical ul { + margin-bottom: 0 +} + +.wy-menu-vertical li.divide-top { + border-top: 1px solid #404040 +} + +.wy-menu-vertical li.divide-bottom { + border-bottom: 1px solid #404040 +} + +.wy-menu-vertical li.current { + background: #e3e3e3 +} + +.wy-menu-vertical li.current a { + color: grey; + border-right: 1px solid #c9c9c9; + padding: .4045em 2.427em +} + +.wy-menu-vertical li.current a:hover { + background: #d6d6d6 +} + +.rst-content .wy-menu-vertical li tt, +.wy-menu-vertical li .rst-content tt, +.wy-menu-vertical li code { + border: none; + background: inherit; + color: inherit; + padding-left: 0; + padding-right: 0 +} + +.wy-menu-vertical li button.toctree-expand { + display: block; + float: left; + margin-left: -1.2em; + line-height: 18px; + color: #4d4d4d; + border: none; + background: none; + padding: 0 +} + +.wy-menu-vertical li.current>a, +.wy-menu-vertical li.on a { + color: #404040; + font-weight: 700; + position: relative; + background: #fcfcfc; + border: none; + padding: .4045em 1.618em +} + +.wy-menu-vertical li.current>a:hover, +.wy-menu-vertical li.on a:hover { + background: #fcfcfc +} + +.wy-menu-vertical li.current>a:hover button.toctree-expand, +.wy-menu-vertical li.on a:hover button.toctree-expand { + color: grey +} + +.wy-menu-vertical li.current>a button.toctree-expand, +.wy-menu-vertical li.on a button.toctree-expand { + display: block; + line-height: 18px; + color: #333 +} + +.wy-menu-vertical li.toctree-l1.current>a { + border-bottom: 1px solid #c9c9c9; + border-top: 1px solid #c9c9c9 +} + +.wy-menu-vertical .toctree-l1.current .toctree-l2>ul, +.wy-menu-vertical .toctree-l2.current .toctree-l3>ul, +.wy-menu-vertical .toctree-l3.current .toctree-l4>ul, +.wy-menu-vertical .toctree-l4.current .toctree-l5>ul, +.wy-menu-vertical .toctree-l5.current .toctree-l6>ul, +.wy-menu-vertical .toctree-l6.current .toctree-l7>ul, +.wy-menu-vertical .toctree-l7.current .toctree-l8>ul, +.wy-menu-vertical .toctree-l8.current .toctree-l9>ul, +.wy-menu-vertical .toctree-l9.current .toctree-l10>ul, +.wy-menu-vertical .toctree-l10.current .toctree-l11>ul { + display: none +} + +.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul, +.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul, +.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul, +.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul, +.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul, +.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul, +.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul, +.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul, +.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul, +.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul { + display: block +} + +.wy-menu-vertical li.toctree-l3, +.wy-menu-vertical li.toctree-l4 { + font-size: .9em +} + +.wy-menu-vertical li.toctree-l2 a, +.wy-menu-vertical li.toctree-l3 a, +.wy-menu-vertical li.toctree-l4 a, +.wy-menu-vertical li.toctree-l5 a, +.wy-menu-vertical li.toctree-l6 a, +.wy-menu-vertical li.toctree-l7 a, +.wy-menu-vertical li.toctree-l8 a, +.wy-menu-vertical li.toctree-l9 a, +.wy-menu-vertical li.toctree-l10 a { + color: #404040 +} + +.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand, +.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand, +.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand, +.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand, +.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand, +.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand, +.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand, +.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand, +.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand { + color: grey +} + +.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a, +.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a, +.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a, +.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a, +.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a, +.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a, +.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a, +.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a, +.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a { + display: block +} + +.wy-menu-vertical li.toctree-l2.current>a { + padding: .4045em 2.427em +} + +.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a { + padding: .4045em 1.618em .4045em 4.045em +} + +.wy-menu-vertical li.toctree-l3.current>a { + padding: .4045em 4.045em +} + +.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a { + padding: .4045em 1.618em .4045em 5.663em +} + +.wy-menu-vertical li.toctree-l4.current>a { + padding: .4045em 5.663em +} + +.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a { + padding: .4045em 1.618em .4045em 7.281em +} + +.wy-menu-vertical li.toctree-l5.current>a { + padding: .4045em 7.281em +} + +.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a { + padding: .4045em 1.618em .4045em 8.899em +} + +.wy-menu-vertical li.toctree-l6.current>a { + padding: .4045em 8.899em +} + +.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a { + padding: .4045em 1.618em .4045em 10.517em +} + +.wy-menu-vertical li.toctree-l7.current>a { + padding: .4045em 10.517em +} + +.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a { + padding: .4045em 1.618em .4045em 12.135em +} + +.wy-menu-vertical li.toctree-l8.current>a { + padding: .4045em 12.135em +} + +.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a { + padding: .4045em 1.618em .4045em 13.753em +} + +.wy-menu-vertical li.toctree-l9.current>a { + padding: .4045em 13.753em +} + +.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a { + padding: .4045em 1.618em .4045em 15.371em +} + +.wy-menu-vertical li.toctree-l10.current>a { + padding: .4045em 15.371em +} + +.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a { + padding: .4045em 1.618em .4045em 16.989em +} + +.wy-menu-vertical li.toctree-l2.current>a, +.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a { + background: #c9c9c9 +} + +.wy-menu-vertical li.toctree-l2 button.toctree-expand { + color: #a3a3a3 +} + +.wy-menu-vertical li.toctree-l3.current>a, +.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a { + background: #bdbdbd +} + +.wy-menu-vertical li.toctree-l3 button.toctree-expand { + color: #969696 +} + +.wy-menu-vertical li.current ul { + display: block +} + +.wy-menu-vertical li ul { + margin-bottom: 0; + display: none +} + +.wy-menu-vertical li ul li a { + margin-bottom: 0; + color: #d9d9d9; + font-weight: 400 +} + +.wy-menu-vertical a { + line-height: 18px; + padding: .4045em 1.618em; + display: block; + position: relative; + font-size: 90%; + color: #d9d9d9 +} + +.wy-menu-vertical a:hover { + background-color: #4e4a4a; + cursor: pointer +} + +.wy-menu-vertical a:hover button.toctree-expand { + color: #d9d9d9 +} + +.wy-menu-vertical a:active { + background-color: #2980b9; + cursor: pointer; + color: #fff +} + +.wy-menu-vertical a:active button.toctree-expand { + color: #fff +} + +.wy-side-nav-search { + display: block; + width: 300px; + padding: .809em; + margin-bottom: .809em; + z-index: 200; + background-color: #2980b9; + text-align: center; + color: #fcfcfc +} + +.wy-side-nav-search input[type=text] { + width: 100%; + border-radius: 50px; + padding: 6px 12px; + border-color: #2472a4 +} + +.wy-side-nav-search img { + display: block; + margin: auto auto .809em; + height: 45px; + width: 45px; + background-color: #2980b9; + padding: 5px; + border-radius: 100% +} + +.wy-side-nav-search .wy-dropdown>a, +.wy-side-nav-search>a { + color: #fcfcfc; + font-size: 100%; + font-weight: 700; + display: inline-block; + padding-top: 4px; + padding-right: 6px; + /*padding-bottom: 4px;*/ + padding-left: 6px; + /* margin-bottom:0.809em */ +} + +.wy-side-nav-search .wy-dropdown>a:hover, +.wy-side-nav-search .wy-dropdown>aactive, +.wy-side-nav-search .wy-dropdown>afocus, +.wy-side-nav-search>a:hover, +.wy-side-nav-search>aactive, +.wy-side-nav-search>afocus { + background: hsla(0, 0%, 100%, .1) +} + +.wy-side-nav-search .wy-dropdown>a img.logo, +.wy-side-nav-search>a img.logo { + display: block; + margin: 0 auto; + height: auto; + width: auto; + border-radius: 0; + max-width: 100%; + background: transparent +} + +.wy-side-nav-search .wy-dropdown>a.icon, +.wy-side-nav-search>a.icon { + display: block +} + +.wy-side-nav-search .wy-dropdown>a.icon img.logo, +.wy-side-nav-search>a.icon img.logo { + margin-top: .85em +} + +.wy-side-nav-search>div.switch-menus { + position: relative; + display: block; + margin-top: -.4045em; + margin-bottom: .809em; + font-weight: 400; + color: hsla(0, 0%, 100%, .3) +} + +.wy-side-nav-search>div.switch-menus>div.language-switch, +.wy-side-nav-search>div.switch-menus>div.version-switch { + display: inline-block; + padding: .2em +} + +.wy-side-nav-search>div.switch-menus>div.language-switch select, +.wy-side-nav-search>div.switch-menus>div.version-switch select { + display: inline-block; + margin-right: -2rem; + padding-right: 2rem; + max-width: 240px; + text-align-last: center; + background: none; + border: none; + border-radius: 0; + box-shadow: none; + font-family: Lato, proxima-nova, Helvetica Neue, Arial, sans-serif; + font-size: 1em; + font-weight: 400; + color: hsla(0, 0%, 100%, .3); + cursor: pointer; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none +} + +.wy-side-nav-search>div.switch-menus>div.language-switch select:active, +.wy-side-nav-search>div.switch-menus>div.language-switch select:focus, +.wy-side-nav-search>div.switch-menus>div.language-switch select:hover, +.wy-side-nav-search>div.switch-menus>div.version-switch select:active, +.wy-side-nav-search>div.switch-menus>div.version-switch select:focus, +.wy-side-nav-search>div.switch-menus>div.version-switch select:hover { + background: hsla(0, 0%, 100%, .1); + color: hsla(0, 0%, 100%, .5) +} + +.wy-side-nav-search>div.switch-menus>div.language-switch select option, +.wy-side-nav-search>div.switch-menus>div.version-switch select option { + color: #000 +} + +.wy-side-nav-search>div.switch-menus>div.language-switch:has(>select):after, +.wy-side-nav-search>div.switch-menus>div.version-switch:has(>select):after { + display: inline-block; + width: 1.5em; + height: 100%; + padding: .1em; + content: "\f0d7"; + font-size: 1em; + line-height: 1.2em; + font-family: FontAwesome; + text-align: center; + pointer-events: none; + box-sizing: border-box +} + +.wy-nav .wy-menu-vertical header { + color: #2980b9 +} + +.wy-nav .wy-menu-vertical a { + color: #b3b3b3 +} + +.wy-nav .wy-menu-vertical a:hover { + background-color: #2980b9; + color: #fff +} + +[data-menu-wrap] { + -webkit-transition: all .2s ease-in; + -moz-transition: all .2s ease-in; + transition: all .2s ease-in; + position: absolute; + opacity: 1; + width: 100%; + opacity: 0 +} + +[data-menu-wrap].move-center { + left: 0; + right: auto; + opacity: 1 +} + +[data-menu-wrap].move-left { + right: auto; + left: -100%; + opacity: 0 +} + +[data-menu-wrap].move-right { + right: -100%; + left: auto; + opacity: 0 +} + +.wy-body-for-nav { + /* background: #fcfcfc */ +} + +.wy-grid-for-nav { + /* position:absolute; */ + position: relative; + /* Make left column full length */ + width: 100%; + height: 100% +} + +.wy-nav-side { + position: absolute; + top: 0; + bottom: 0; + left: 0; + padding-bottom: 2em; + width: 300px; + overflow-x: hidden; + overflow-y: hidden; + min-height: 100%; + color: #9b9b9b; + background: #343131; + z-index: 200 +} + +.wy-side-scroll { + width: 320px; + position: relative; + overflow-x: hidden; + overflow-y: scroll; + height: 100% +} + +.wy-nav-top { + display: none; + background: #2980b9; + color: #fff; + padding: .4045em .809em; + position: relative; + line-height: 50px; + text-align: center; + font-size: 100%; + *zoom: 1 +} + +.wy-nav-top:after, +.wy-nav-top:before { + display: table; + content: "" +} + +.wy-nav-top:after { + clear: both +} + +.wy-nav-top a { + color: #fff; + font-weight: 700 +} + +.wy-nav-top img { + margin-right: 12px; + /*height:45px; */ + /*width:45px;*/ + width: 200px; + background-color: #2980b9; + padding: 5px; + /* border-radius: 100% */ +} + +.wy-nav-top i { + font-size: 30px; + float: left; + cursor: pointer; + padding-top: inherit +} + +.wy-nav-content-wrap { + margin-left: 300px; + background: #fcfcfc; + min-height: 100% +} + +.wy-nav-content { /* padding:1.618em 3.236em; */ - /* padding-top: 1.618em; */ - padding-right: 3.236em; - padding-bottom: 1.618em; - padding-left: 3.236em; - height:100%; - min-height: 100vh; /* ensure is always full height of browser window */ - max-width:800px; + /* padding-top: 1.618em; */ + padding-right: 3.236em; + padding-bottom: 1.618em; + padding-left: 3.236em; + height: 100%; + min-height: 100vh; + /* ensure is always full height of browser window */ + max-width: 800px; /* margin:auto; */ - margin-left:0px; + margin-left: 0px; background: #fcfcfc; - } - - .wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%} - - @media screen and (max-width: 768px){ - .wy-body-for-nav{background:#fcfcfc} - .wy-nav-top{display:block}.wy-nav-side{left:-300px} - .wy-nav-side.shift{width:85%;left:0} - .wy-nav-content-wrap{margin-left:0} - .wy-nav-content-wrap .wy-nav-content { - /* padding:1.618em */ - /* padding-top: 1.618em; */ - padding-right: 1.618em; - padding-bottom: 1.618em; - padding-left: 1.618em; - } - .wy-nav-content-wrap.shift{ - position:relative; /* position:fixed; */ - min-width:100%; - left:85%; - top:0;height:100%; - overflow:hidden - } - } - - @media screen and (min-width: 1400px) { - /* .wy-nav-content-wrap{background:rgba(0,0,0,0.05)} */ - .wy-nav-content{ - /* margin:0; */ - background:#fcfcfc} - } - - @media print{.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}nav.stickynav{position:absolute/* previously fixed hamishw */ ;top:0}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center} - - @media screen and (max-width: 768px){ - .rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:"\f0c1";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right} - - @media screen and (max-width: 480px){ - .rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040} +} + +.wy-body-mask { + position: fixed; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, .2); + display: none; + z-index: 499 +} + +.wy-body-mask.on { + display: block +} + +footer { + color: grey +} + +footer p { + margin-bottom: 12px +} + +.rst-content footer span.commit tt, +footer span.commit .rst-content tt, +footer span.commit code { + padding: 0; + font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, Courier, monospace; + font-size: 1em; + background: none; + border: none; + color: grey +} + +.rst-footer-buttons { + *zoom: 1 +} + +.rst-footer-buttons:after, +.rst-footer-buttons:before { + width: 100%; + display: table; + content: "" +} + +.rst-footer-buttons:after { + clear: both +} + +.rst-breadcrumbs-buttons { + margin-top: 12px; + *zoom: 1 +} + +.rst-breadcrumbs-buttons:after, +.rst-breadcrumbs-buttons:before { + display: table; + content: "" +} + +.rst-breadcrumbs-buttons:after { + clear: both +} + +#search-results .search li { + margin-bottom: 24px; + border-bottom: 1px solid #e1e4e5; + padding-bottom: 24px +} + +#search-results .search li:first-child { + border-top: 1px solid #e1e4e5; + padding-top: 24px +} + +#search-results .search li a { + font-size: 120%; + margin-bottom: 12px; + display: inline-block +} + +#search-results .context { + color: grey; + font-size: 90% +} + +.genindextable li>ul { + margin-left: 24px +} + +@media screen and (max-width:768px) { + .wy-body-for-nav { + background: #fcfcfc + } + + .wy-nav-top { + display: block + } + + .wy-nav-side { + left: -300px + } + + .wy-nav-side.shift { + width: 85%; + left: 0 + } + + .wy-menu.wy-menu-vertical, + .wy-side-nav-search, + .wy-side-scroll { + width: auto + } + + .wy-nav-content-wrap { + margin-left: 0 + } + + .wy-nav-content-wrap .wy-nav-content { + /* padding:1.618em */ + /* padding-top: 1.618em; */ + padding-right: 1.618em; + padding-bottom: 1.618em; + padding-left: 1.618em; + } + + .wy-nav-content-wrap.shift { + position: relative; + /* position:fixed; */ + min-width: 100%; + left: 85%; + top: 0; + height: 100%; + overflow: hidden + } +} + +@media screen and (min-width:1100px) { + /* .wy-nav-content-wrap { + background: rgba(0, 0, 0, .05) + } */ + + .wy-nav-content { + /* margin: 0; */ + background: #fcfcfc + } +} + +@media print { + + .rst-versions, + .wy-nav-side, + footer { + display: none + } + + .wy-nav-content-wrap { + margin-left: 0 + } +} + +.rst-versions { + position: fixed; + bottom: 0; + left: 0; + width: 300px; + color: #fcfcfc; + background: #1f1d1d; + font-family: Lato, proxima-nova, Helvetica Neue, Arial, sans-serif; + z-index: 400 +} + +.rst-versions a { + color: #2980b9; + text-decoration: none +} + +.rst-versions .rst-badge-small { + display: none +} + +.rst-versions .rst-current-version { + padding: 12px; + background-color: #272525; + display: block; + text-align: right; + font-size: 90%; + cursor: pointer; + color: #27ae60; + *zoom: 1 +} + +.rst-versions .rst-current-version:after, +.rst-versions .rst-current-version:before { + display: table; + content: "" +} + +.rst-versions .rst-current-version:after { + clear: both +} + +.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink, +.rst-content .eqno .rst-versions .rst-current-version .headerlink, +.rst-content .rst-versions .rst-current-version .admonition-title, +.rst-content code.download .rst-versions .rst-current-version span:first-child, +.rst-content dl dt .rst-versions .rst-current-version .headerlink, +.rst-content h1 .rst-versions .rst-current-version .headerlink, +.rst-content h2 .rst-versions .rst-current-version .headerlink, +.rst-content h3 .rst-versions .rst-current-version .headerlink, +.rst-content h4 .rst-versions .rst-current-version .headerlink, +.rst-content h5 .rst-versions .rst-current-version .headerlink, +.rst-content h6 .rst-versions .rst-current-version .headerlink, +.rst-content p .rst-versions .rst-current-version .headerlink, +.rst-content table>caption .rst-versions .rst-current-version .headerlink, +.rst-content tt.download .rst-versions .rst-current-version span:first-child, +.rst-versions .rst-current-version .fa, +.rst-versions .rst-current-version .icon, +.rst-versions .rst-current-version .rst-content .admonition-title, +.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink, +.rst-versions .rst-current-version .rst-content .eqno .headerlink, +.rst-versions .rst-current-version .rst-content code.download span:first-child, +.rst-versions .rst-current-version .rst-content dl dt .headerlink, +.rst-versions .rst-current-version .rst-content h1 .headerlink, +.rst-versions .rst-current-version .rst-content h2 .headerlink, +.rst-versions .rst-current-version .rst-content h3 .headerlink, +.rst-versions .rst-current-version .rst-content h4 .headerlink, +.rst-versions .rst-current-version .rst-content h5 .headerlink, +.rst-versions .rst-current-version .rst-content h6 .headerlink, +.rst-versions .rst-current-version .rst-content p .headerlink, +.rst-versions .rst-current-version .rst-content table>caption .headerlink, +.rst-versions .rst-current-version .rst-content tt.download span:first-child, +.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand, +.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand { + color: #fcfcfc +} + +.rst-versions .rst-current-version .fa-book, +.rst-versions .rst-current-version .icon-book { + float: left +} + +.rst-versions .rst-current-version.rst-out-of-date { + background-color: #e74c3c; + color: #fff +} + +.rst-versions .rst-current-version.rst-active-old-version { + background-color: #f1c40f; + color: #000 +} + +.rst-versions.shift-up { + height: auto; + max-height: 100%; + overflow-y: scroll +} + +.rst-versions.shift-up .rst-other-versions { + display: block +} + +.rst-versions .rst-other-versions { + font-size: 90%; + padding: 12px; + color: grey; + display: none +} + +.rst-versions .rst-other-versions hr { + display: block; + height: 1px; + border: 0; + margin: 20px 0; + padding: 0; + border-top: 1px solid #413d3d +} + +.rst-versions .rst-other-versions dd { + display: inline-block; + margin: 0 +} + +.rst-versions .rst-other-versions dd a { + display: inline-block; + padding: 6px; + color: #fcfcfc +} + +.rst-versions .rst-other-versions .rtd-current-item { + font-weight: 700 +} + +.rst-versions.rst-badge { + width: auto; + bottom: 20px; + right: 20px; + left: auto; + border: none; + max-width: 300px; + max-height: 90% +} + +.rst-versions.rst-badge .fa-book, +.rst-versions.rst-badge .icon-book { + float: none; + line-height: 30px +} + +.rst-versions.rst-badge.shift-up .rst-current-version { + text-align: right +} + +.rst-versions.rst-badge.shift-up .rst-current-version .fa-book, +.rst-versions.rst-badge.shift-up .rst-current-version .icon-book { + float: left +} + +.rst-versions.rst-badge>.rst-current-version { + width: auto; + height: 30px; + line-height: 30px; + padding: 0 6px; + display: block; + text-align: center +} + +@media screen and (max-width:768px) { + .rst-versions { + width: 85%; + display: none + } + + .rst-versions.shift { + display: block + } +} + +#flyout-search-form { + padding: 6px +} + +.rst-content .toctree-wrapper>p.caption, +.rst-content h1, +.rst-content h2, +.rst-content h3, +.rst-content h4, +.rst-content h5, +.rst-content h6 { + margin-bottom: 24px +} + +.rst-content img { + height: auto +} + +.rst-content div.figure, +.rst-content figure { + margin-bottom: 24px +} + +.rst-content div.figure .caption-text, +.rst-content figure .caption-text { + font-style: italic +} + +.rst-content div.figure p:last-child.caption, +.rst-content figure p:last-child.caption { + margin-bottom: 0 +} + +.rst-content div.figure.align-center, +.rst-content figure.align-center { + text-align: center +} + +.rst-content .section>a>img, +.rst-content .section>img, +.rst-content section>a>img, +.rst-content section>img { + margin-bottom: 24px +} + +.rst-content abbr[title] { + text-decoration: none +} + +.rst-content.style-external-links a.reference.external:after { + font-family: FontAwesome; + content: "\f08e"; + color: #b3b3b3; + vertical-align: super; + font-size: 60%; + margin: 0 .2em +} + +.rst-content blockquote { + margin-left: 24px; + line-height: 24px; + margin-bottom: 24px +} + +.rst-content pre.literal-block { + white-space: pre; + margin: 0; + padding: 12px; + font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, Courier, monospace; + display: block; + overflow: auto +} + +.rst-content div[class^=highlight], +.rst-content pre.literal-block { + border: 1px solid #e1e4e5; + overflow-x: auto; + margin: 1px 0 24px +} + +.rst-content div[class^=highlight] div[class^=highlight], +.rst-content pre.literal-block div[class^=highlight] { + padding: 0; + border: none; + margin: 0 +} + +.rst-content div[class^=highlight] td.code { + width: 100% +} + +.rst-content .linenodiv pre { + border-right: 1px solid #e6e9ea; + margin: 0; + padding: 12px; + font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, Courier, monospace; + user-select: none; + pointer-events: none +} + +.rst-content div[class^=highlight] pre { + white-space: pre; + margin: 0; + padding: 12px; + display: block; + overflow: auto +} + +.rst-content div[class^=highlight] pre .hll { + display: block; + margin: 0 -12px; + padding: 0 12px +} + +.rst-content .linenodiv pre, +.rst-content div[class^=highlight] pre, +.rst-content pre.literal-block { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, Courier, monospace; + font-size: 12px; + line-height: 1.4 +} + +.rst-content div.highlight .gp, +.rst-content div.highlight span.linenos { + user-select: none; + pointer-events: none +} + +.rst-content div.highlight span.linenos { + display: inline-block; + padding-left: 0; + padding-right: 12px; + margin-right: 12px; + border-right: 1px solid #e6e9ea +} + +.rst-content .code-block-caption { + font-style: italic; + font-size: 85%; + line-height: 1; + padding: 1em 0; + text-align: center +} + +@media print { + + .rst-content .codeblock, + .rst-content div[class^=highlight], + .rst-content div[class^=highlight] pre { + white-space: pre-wrap + } +} + +.rst-content .admonition, +.rst-content .admonition-todo, +.rst-content .attention, +.rst-content .caution, +.rst-content .danger, +.rst-content .error, +.rst-content .hint, +.rst-content .important, +.rst-content .note, +.rst-content .seealso, +.rst-content .tip, +.rst-content .warning { + clear: both +} + +.rst-content .admonition-todo .last, +.rst-content .admonition-todo>:last-child, +.rst-content .admonition .last, +.rst-content .admonition>:last-child, +.rst-content .attention .last, +.rst-content .attention>:last-child, +.rst-content .caution .last, +.rst-content .caution>:last-child, +.rst-content .danger .last, +.rst-content .danger>:last-child, +.rst-content .error .last, +.rst-content .error>:last-child, +.rst-content .hint .last, +.rst-content .hint>:last-child, +.rst-content .important .last, +.rst-content .important>:last-child, +.rst-content .note .last, +.rst-content .note>:last-child, +.rst-content .seealso .last, +.rst-content .seealso>:last-child, +.rst-content .tip .last, +.rst-content .tip>:last-child, +.rst-content .warning .last, +.rst-content .warning>:last-child { + margin-bottom: 0 +} + +.rst-content .admonition-title:before { + margin-right: 4px +} + +.rst-content .admonition table { + border-color: rgba(0, 0, 0, .1) +} + +.rst-content .admonition table td, +.rst-content .admonition table th { + background: transparent !important; + border-color: rgba(0, 0, 0, .1) !important +} + +.rst-content .section ol.loweralpha, +.rst-content .section ol.loweralpha>li, +.rst-content .toctree-wrapper ol.loweralpha, +.rst-content .toctree-wrapper ol.loweralpha>li, +.rst-content section ol.loweralpha, +.rst-content section ol.loweralpha>li { + list-style: lower-alpha +} + +.rst-content .section ol.upperalpha, +.rst-content .section ol.upperalpha>li, +.rst-content .toctree-wrapper ol.upperalpha, +.rst-content .toctree-wrapper ol.upperalpha>li, +.rst-content section ol.upperalpha, +.rst-content section ol.upperalpha>li { + list-style: upper-alpha +} + +.rst-content .section ol li>*, +.rst-content .section ul li>*, +.rst-content .toctree-wrapper ol li>*, +.rst-content .toctree-wrapper ul li>*, +.rst-content section ol li>*, +.rst-content section ul li>* { + margin-top: 12px; + margin-bottom: 12px +} + +.rst-content .section ol li>:first-child, +.rst-content .section ul li>:first-child, +.rst-content .toctree-wrapper ol li>:first-child, +.rst-content .toctree-wrapper ul li>:first-child, +.rst-content section ol li>:first-child, +.rst-content section ul li>:first-child { + margin-top: 0 +} + +.rst-content .section ol li>p, +.rst-content .section ol li>p:last-child, +.rst-content .section ul li>p, +.rst-content .section ul li>p:last-child, +.rst-content .toctree-wrapper ol li>p, +.rst-content .toctree-wrapper ol li>p:last-child, +.rst-content .toctree-wrapper ul li>p, +.rst-content .toctree-wrapper ul li>p:last-child, +.rst-content section ol li>p, +.rst-content section ol li>p:last-child, +.rst-content section ul li>p, +.rst-content section ul li>p:last-child { + margin-bottom: 12px +} + +.rst-content .section ol li>p:only-child, +.rst-content .section ol li>p:only-child:last-child, +.rst-content .section ul li>p:only-child, +.rst-content .section ul li>p:only-child:last-child, +.rst-content .toctree-wrapper ol li>p:only-child, +.rst-content .toctree-wrapper ol li>p:only-child:last-child, +.rst-content .toctree-wrapper ul li>p:only-child, +.rst-content .toctree-wrapper ul li>p:only-child:last-child, +.rst-content section ol li>p:only-child, +.rst-content section ol li>p:only-child:last-child, +.rst-content section ul li>p:only-child, +.rst-content section ul li>p:only-child:last-child { + margin-bottom: 0 +} + +.rst-content .section ol li>ol, +.rst-content .section ol li>ul, +.rst-content .section ul li>ol, +.rst-content .section ul li>ul, +.rst-content .toctree-wrapper ol li>ol, +.rst-content .toctree-wrapper ol li>ul, +.rst-content .toctree-wrapper ul li>ol, +.rst-content .toctree-wrapper ul li>ul, +.rst-content section ol li>ol, +.rst-content section ol li>ul, +.rst-content section ul li>ol, +.rst-content section ul li>ul { + margin-bottom: 12px +} + +.rst-content .section ol.simple li>*, +.rst-content .section ol.simple li ol, +.rst-content .section ol.simple li ul, +.rst-content .section ul.simple li>*, +.rst-content .section ul.simple li ol, +.rst-content .section ul.simple li ul, +.rst-content .toctree-wrapper ol.simple li>*, +.rst-content .toctree-wrapper ol.simple li ol, +.rst-content .toctree-wrapper ol.simple li ul, +.rst-content .toctree-wrapper ul.simple li>*, +.rst-content .toctree-wrapper ul.simple li ol, +.rst-content .toctree-wrapper ul.simple li ul, +.rst-content section ol.simple li>*, +.rst-content section ol.simple li ol, +.rst-content section ol.simple li ul, +.rst-content section ul.simple li>*, +.rst-content section ul.simple li ol, +.rst-content section ul.simple li ul { + margin-top: 0; + margin-bottom: 0 +} + +.rst-content .line-block { + margin-left: 0; + margin-bottom: 24px; + line-height: 24px +} + +.rst-content .line-block .line-block { + margin-left: 24px; + margin-bottom: 0 +} + +.rst-content .topic-title { + font-weight: 700; + margin-bottom: 12px +} + +.rst-content .toc-backref { + color: #404040 +} + +.rst-content .align-right { + float: right; + margin: 0 0 24px 24px +} + +.rst-content .align-left { + float: left; + margin: 0 24px 24px 0 +} + +.rst-content .align-center { + margin: auto +} + +.rst-content .align-center:not(table) { + display: block +} + +.rst-content .code-block-caption .headerlink, +.rst-content .eqno .headerlink, +.rst-content .toctree-wrapper>p.caption .headerlink, +.rst-content dl dt .headerlink, +.rst-content h1 .headerlink, +.rst-content h2 .headerlink, +.rst-content h3 .headerlink, +.rst-content h4 .headerlink, +.rst-content h5 .headerlink, +.rst-content h6 .headerlink, +.rst-content p.caption .headerlink, +.rst-content p .headerlink, +.rst-content table>caption .headerlink { + opacity: 0; + font-size: 14px; + font-family: FontAwesome; + margin-left: .5em +} + +.rst-content .code-block-caption .headerlink:focus, +.rst-content .code-block-caption:hover .headerlink, +.rst-content .eqno .headerlink:focus, +.rst-content .eqno:hover .headerlink, +.rst-content .toctree-wrapper>p.caption .headerlink:focus, +.rst-content .toctree-wrapper>p.caption:hover .headerlink, +.rst-content dl dt .headerlink:focus, +.rst-content dl dt:hover .headerlink, +.rst-content h1 .headerlink:focus, +.rst-content h1:hover .headerlink, +.rst-content h2 .headerlink:focus, +.rst-content h2:hover .headerlink, +.rst-content h3 .headerlink:focus, +.rst-content h3:hover .headerlink, +.rst-content h4 .headerlink:focus, +.rst-content h4:hover .headerlink, +.rst-content h5 .headerlink:focus, +.rst-content h5:hover .headerlink, +.rst-content h6 .headerlink:focus, +.rst-content h6:hover .headerlink, +.rst-content p.caption .headerlink:focus, +.rst-content p.caption:hover .headerlink, +.rst-content p .headerlink:focus, +.rst-content p:hover .headerlink, +.rst-content table>caption .headerlink:focus, +.rst-content table>caption:hover .headerlink { + opacity: 1 +} + +.rst-content p a { + overflow-wrap: anywhere +} + +.rst-content .wy-table td p, +.rst-content .wy-table td ul, +.rst-content .wy-table th p, +.rst-content .wy-table th ul, +.rst-content table.docutils td p, +.rst-content table.docutils td ul, +.rst-content table.docutils th p, +.rst-content table.docutils th ul, +.rst-content table.field-list td p, +.rst-content table.field-list td ul, +.rst-content table.field-list th p, +.rst-content table.field-list th ul { + font-size: inherit +} + +.rst-content .btn:focus { + outline: 2px solid +} + +.rst-content table>caption .headerlink:after { + font-size: 12px +} + +.rst-content .centered { + text-align: center +} + +.rst-content .sidebar { + float: right; + width: 40%; + display: block; + margin: 0 0 24px 24px; + padding: 24px; + background: #f3f6f6; + border: 1px solid #e1e4e5 +} + +.rst-content .sidebar dl, +.rst-content .sidebar p, +.rst-content .sidebar ul { + font-size: 90% +} + +.rst-content .sidebar .last, +.rst-content .sidebar>:last-child { + margin-bottom: 0 +} + +.rst-content .sidebar .sidebar-title { + display: block; + font-family: Roboto Slab, ff-tisa-web-pro, Georgia, Arial, sans-serif; + font-weight: 700; + background: #e1e4e5; + padding: 6px 12px; + margin: -24px -24px 24px; + font-size: 100% +} + +.rst-content .highlighted { + background: #f1c40f; + box-shadow: 0 0 0 2px #f1c40f; + display: inline; + font-weight: 700 +} + +.rst-content .citation-reference, +.rst-content .footnote-reference { + vertical-align: baseline; + position: relative; + top: -.4em; + line-height: 0; + font-size: 90% +} + +.rst-content .citation-reference>span.fn-bracket, +.rst-content .footnote-reference>span.fn-bracket { + display: none +} + +.rst-content .hlist { + width: 100% +} + +.rst-content dl dt span.classifier:before { + content: " : " +} + +.rst-content dl dt span.classifier-delimiter { + display: none !important +} + +html.writer-html4 .rst-content table.docutils.citation, +html.writer-html4 .rst-content table.docutils.footnote { + background: none; + border: none +} + +html.writer-html4 .rst-content table.docutils.citation td, +html.writer-html4 .rst-content table.docutils.citation tr, +html.writer-html4 .rst-content table.docutils.footnote td, +html.writer-html4 .rst-content table.docutils.footnote tr { + border: none; + background-color: transparent !important; + white-space: normal +} + +html.writer-html4 .rst-content table.docutils.citation td.label, +html.writer-html4 .rst-content table.docutils.footnote td.label { + padding-left: 0; + padding-right: 0; + vertical-align: top +} + +html.writer-html5 .rst-content dl.citation, +html.writer-html5 .rst-content dl.field-list, +html.writer-html5 .rst-content dl.footnote { + display: grid; + grid-template-columns: auto minmax(80%, 95%) +} + +html.writer-html5 .rst-content dl.citation>dt, +html.writer-html5 .rst-content dl.field-list>dt, +html.writer-html5 .rst-content dl.footnote>dt { + display: inline-grid; + grid-template-columns: max-content auto +} + +html.writer-html5 .rst-content aside.citation, +html.writer-html5 .rst-content aside.footnote, +html.writer-html5 .rst-content div.citation { + display: grid; + grid-template-columns: auto auto minmax(.65rem, auto) minmax(40%, 95%) +} + +html.writer-html5 .rst-content aside.citation>span.label, +html.writer-html5 .rst-content aside.footnote>span.label, +html.writer-html5 .rst-content div.citation>span.label { + grid-column-start: 1; + grid-column-end: 2 +} + +html.writer-html5 .rst-content aside.citation>span.backrefs, +html.writer-html5 .rst-content aside.footnote>span.backrefs, +html.writer-html5 .rst-content div.citation>span.backrefs { + grid-column-start: 2; + grid-column-end: 3; + grid-row-start: 1; + grid-row-end: 3 +} + +html.writer-html5 .rst-content aside.citation>p, +html.writer-html5 .rst-content aside.footnote>p, +html.writer-html5 .rst-content div.citation>p { + grid-column-start: 4; + grid-column-end: 5 +} + +html.writer-html5 .rst-content dl.citation, +html.writer-html5 .rst-content dl.field-list, +html.writer-html5 .rst-content dl.footnote { + margin-bottom: 24px +} + +html.writer-html5 .rst-content dl.citation>dt, +html.writer-html5 .rst-content dl.field-list>dt, +html.writer-html5 .rst-content dl.footnote>dt { + padding-left: 1rem +} + +html.writer-html5 .rst-content dl.citation>dd, +html.writer-html5 .rst-content dl.citation>dt, +html.writer-html5 .rst-content dl.field-list>dd, +html.writer-html5 .rst-content dl.field-list>dt, +html.writer-html5 .rst-content dl.footnote>dd, +html.writer-html5 .rst-content dl.footnote>dt { + margin-bottom: 0 +} + +html.writer-html5 .rst-content dl.citation, +html.writer-html5 .rst-content dl.footnote { + font-size: .9rem +} + +html.writer-html5 .rst-content dl.citation>dt, +html.writer-html5 .rst-content dl.footnote>dt { + margin: 0 .5rem .5rem 0; + line-height: 1.2rem; + word-break: break-all; + font-weight: 400 +} + +html.writer-html5 .rst-content dl.citation>dt>span.brackets:before, +html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before { + content: "[" +} + +html.writer-html5 .rst-content dl.citation>dt>span.brackets:after, +html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after { + content: "]" +} + +html.writer-html5 .rst-content dl.citation>dt>span.fn-backref, +html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref { + text-align: left; + font-style: italic; + margin-left: .65rem; + word-break: break-word; + word-spacing: -.1rem; + max-width: 5rem +} + +html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a, +html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a { + word-break: keep-all +} + +html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before, +html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before { + content: " " +} + +html.writer-html5 .rst-content dl.citation>dd, +html.writer-html5 .rst-content dl.footnote>dd { + margin: 0 0 .5rem; + line-height: 1.2rem +} + +html.writer-html5 .rst-content dl.citation>dd p, +html.writer-html5 .rst-content dl.footnote>dd p { + font-size: .9rem +} + +html.writer-html5 .rst-content aside.citation, +html.writer-html5 .rst-content aside.footnote, +html.writer-html5 .rst-content div.citation { + padding-left: 1rem; + padding-right: 1rem; + font-size: .9rem; + line-height: 1.2rem +} + +html.writer-html5 .rst-content aside.citation p, +html.writer-html5 .rst-content aside.footnote p, +html.writer-html5 .rst-content div.citation p { + font-size: .9rem; + line-height: 1.2rem; + margin-bottom: 12px +} + +html.writer-html5 .rst-content aside.citation span.backrefs, +html.writer-html5 .rst-content aside.footnote span.backrefs, +html.writer-html5 .rst-content div.citation span.backrefs { + text-align: left; + font-style: italic; + margin-left: .65rem; + word-break: break-word; + word-spacing: -.1rem; + max-width: 5rem +} + +html.writer-html5 .rst-content aside.citation span.backrefs>a, +html.writer-html5 .rst-content aside.footnote span.backrefs>a, +html.writer-html5 .rst-content div.citation span.backrefs>a { + word-break: keep-all +} + +html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before, +html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before, +html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before { + content: " " +} + +html.writer-html5 .rst-content aside.citation span.label, +html.writer-html5 .rst-content aside.footnote span.label, +html.writer-html5 .rst-content div.citation span.label { + line-height: 1.2rem +} + +html.writer-html5 .rst-content aside.citation-list, +html.writer-html5 .rst-content aside.footnote-list, +html.writer-html5 .rst-content div.citation-list { + margin-bottom: 24px +} + +html.writer-html5 .rst-content dl.option-list kbd { + font-size: .9rem +} + +.rst-content table.docutils.footnote, +html.writer-html4 .rst-content table.docutils.citation, +html.writer-html5 .rst-content aside.footnote, +html.writer-html5 .rst-content aside.footnote-list aside.footnote, +html.writer-html5 .rst-content div.citation-list>div.citation, +html.writer-html5 .rst-content dl.citation, +html.writer-html5 .rst-content dl.footnote { + color: grey +} + +.rst-content table.docutils.footnote code, +.rst-content table.docutils.footnote tt, +html.writer-html4 .rst-content table.docutils.citation code, +html.writer-html4 .rst-content table.docutils.citation tt, +html.writer-html5 .rst-content aside.footnote-list aside.footnote code, +html.writer-html5 .rst-content aside.footnote-list aside.footnote tt, +html.writer-html5 .rst-content aside.footnote code, +html.writer-html5 .rst-content aside.footnote tt, +html.writer-html5 .rst-content div.citation-list>div.citation code, +html.writer-html5 .rst-content div.citation-list>div.citation tt, +html.writer-html5 .rst-content dl.citation code, +html.writer-html5 .rst-content dl.citation tt, +html.writer-html5 .rst-content dl.footnote code, +html.writer-html5 .rst-content dl.footnote tt { + color: #555 +} + +.rst-content .wy-table-responsive.citation, +.rst-content .wy-table-responsive.footnote { + margin-bottom: 0 +} + +.rst-content .wy-table-responsive.citation+:not(.citation), +.rst-content .wy-table-responsive.footnote+:not(.footnote) { + margin-top: 24px +} + +.rst-content .wy-table-responsive.citation:last-child, +.rst-content .wy-table-responsive.footnote:last-child { + margin-bottom: 24px +} + +.rst-content table.docutils th { + border-color: #e1e4e5 +} + +html.writer-html5 .rst-content table.docutils th { + border: 1px solid #e1e4e5 +} + +html.writer-html5 .rst-content table.docutils td>p, +html.writer-html5 .rst-content table.docutils th>p { + line-height: 1rem; + margin-bottom: 0; + font-size: .9rem +} + +.rst-content table.docutils td .last, +.rst-content table.docutils td .last>:last-child { + margin-bottom: 0 +} + +.rst-content table.field-list, +.rst-content table.field-list td { + border: none +} + +.rst-content table.field-list td p { + line-height: inherit +} + +.rst-content table.field-list td>strong { + display: inline-block +} + +.rst-content table.field-list .field-name { + padding-right: 10px; + text-align: left; + white-space: nowrap +} + +.rst-content table.field-list .field-body { + text-align: left +} + +.rst-content code, +.rst-content tt { + color: #000; + font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, Courier, monospace; + padding: 2px 5px +} + +.rst-content code big, +.rst-content code em, +.rst-content tt big, +.rst-content tt em { + font-size: 100% !important; + line-height: normal +} + +.rst-content code.literal, +.rst-content tt.literal { + color: #e74c3c; + white-space: normal +} + +.rst-content code.xref, +.rst-content tt.xref, +a .rst-content code, +a .rst-content tt { + font-weight: 700; + color: #404040; + overflow-wrap: normal +} + +.rst-content kbd, +.rst-content pre, +.rst-content samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, Courier, monospace +} + +.rst-content a code, +.rst-content a tt { + color: #2980b9 +} + +.rst-content dl { + margin-bottom: 24px +} + +.rst-content dl dt { + font-weight: 700; + margin-bottom: 12px +} + +.rst-content dl ol, +.rst-content dl p, +.rst-content dl table, +.rst-content dl ul { + margin-bottom: 12px +} + +.rst-content dl dd { + margin: 0 0 12px 24px; + line-height: 24px +} + +.rst-content dl dd>ol:last-child, +.rst-content dl dd>p:last-child, +.rst-content dl dd>table:last-child, +.rst-content dl dd>ul:last-child { + margin-bottom: 0 +} + +html.writer-html4 .rst-content dl:not(.docutils), +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) { + margin-bottom: 24px +} + +html.writer-html4 .rst-content dl:not(.docutils)>dt, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt { + display: table; + margin: 6px 0; + font-size: 90%; + line-height: normal; + background: #e7f2fa; + color: #2980b9; + border-top: 3px solid #6ab0de; + padding: 6px; + position: relative +} + +html.writer-html4 .rst-content dl:not(.docutils)>dt:before, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before { + color: #6ab0de +} + +html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink { + color: #404040; + font-size: 100% !important +} + +html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt { + margin-bottom: 6px; + border: none; + border-left: 3px solid #ccc; + background: #f0f0f0; + color: #555 +} + +html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink { + color: #404040; + font-size: 100% !important +} + +html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child { + margin-top: 0 +} + +html.writer-html4 .rst-content dl:not(.docutils) code.descclassname, +html.writer-html4 .rst-content dl:not(.docutils) code.descname, +html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname, +html.writer-html4 .rst-content dl:not(.docutils) tt.descname, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname { + background-color: transparent; + border: none; + padding: 0; + font-size: 100% !important +} + +html.writer-html4 .rst-content dl:not(.docutils) code.descname, +html.writer-html4 .rst-content dl:not(.docutils) tt.descname, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname { + font-weight: 700 +} + +html.writer-html4 .rst-content dl:not(.docutils) .optional, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional { + display: inline-block; + padding: 0 4px; + color: #000; + font-weight: 700 +} + +html.writer-html4 .rst-content dl:not(.docutils) .property, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property { + display: inline-block; + padding-right: 8px; + max-width: 100% +} + +html.writer-html4 .rst-content dl:not(.docutils) .k, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k { + font-style: italic +} + +html.writer-html4 .rst-content dl:not(.docutils) .descclassname, +html.writer-html4 .rst-content dl:not(.docutils) .descname, +html.writer-html4 .rst-content dl:not(.docutils) .sig-name, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname, +html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, Courier, monospace; + color: #000 +} + +.rst-content .viewcode-back, +.rst-content .viewcode-link { + display: inline-block; + color: #27ae60; + font-size: 80%; + padding-left: 24px +} + +.rst-content .viewcode-back { + display: block; + float: right +} + +.rst-content p.rubric { + margin-bottom: 12px; + font-weight: 700 +} + +.rst-content code.download, +.rst-content tt.download { + background: inherit; + padding: inherit; + font-weight: 400; + font-family: inherit; + font-size: inherit; + color: inherit; + border: inherit; + white-space: inherit +} + +.rst-content code.download span:first-child, +.rst-content tt.download span:first-child { + -webkit-font-smoothing: subpixel-antialiased +} + +.rst-content code.download span:first-child:before, +.rst-content tt.download span:first-child:before { + margin-right: 4px +} + +.rst-content .guilabel, +.rst-content .menuselection { + font-size: 80%; + font-weight: 700; + border-radius: 4px; + padding: 2.4px 6px; + margin: auto 2px +} + +.rst-content .guilabel, +.rst-content .menuselection { + border: 1px solid #7fbbe3; + background: #e7f2fa +} + +.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd, +.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd { + color: inherit; + font-size: 80%; + background-color: #fff; + border: 1px solid #a6a6a6; + border-radius: 4px; + box-shadow: 0 2px grey; + padding: 2.4px 6px; + margin: auto 0 +} + +.rst-content .versionmodified { + font-style: italic +} + +@media screen and (max-width:480px) { + .rst-content .sidebar { + width: 100% + } +} + +span[id*=MathJax-Span] { + color: #404040 +} + +.math { + text-align: center +} + +@font-face { + font-family: Lato; + src: url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"), url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff"); + font-weight: 400; + font-style: normal; + font-display: block +} + +@font-face { + font-family: Lato; + src: url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"), url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff"); + font-weight: 700; + font-style: normal; + font-display: block +} + +@font-face { + font-family: Lato; + src: url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"), url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff"); + font-weight: 700; + font-style: italic; + font-display: block +} + +@font-face { + font-family: Lato; + src: url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"), url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff"); + font-weight: 400; + font-style: italic; + font-display: block +} + +@font-face { + font-family: Roboto Slab; + font-style: normal; + font-weight: 400; + src: url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"), url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff"); + font-display: block +} + +@font-face { + font-family: Roboto Slab; + font-style: normal; + font-weight: 700; + src: url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"), url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff"); + font-display: block +} + /*! * HamishW - some CSS for nav bar */ - + .main-nav-bar { - display:block; - max-width: 1100px; - border-bottom: solid; - border-bottom-width: thin; - padding-bottom: 10px; - margin-bottom:20px; - padding-top: 10px; + display: block; + max-width: 1100px; + border-bottom: solid; + border-bottom-width: thin; + padding-bottom: 10px; + margin-bottom: 20px; + padding-top: 10px; } #menu-options { display: table; - /* background-color:#F8F8F8; */ + /* background-color:#F8F8F8; */ /*height: 87px;*/ - + width: 100%; } #menu-options li { display: table-cell; - /* padding-left: 5px; + /* padding-left: 5px; padding-right: 5px; */ - padding-top: 10px; - padding-bottom: 10px; - width: 5.0%; /*(100 / numItems)% */ + padding-top: 10px; + padding-bottom: 10px; + width: 5.0%; + /*(100 / numItems)% */ text-align: center; - font-weight:bold; + font-weight: bold; /*background: #ddd;*/ white-space: nowrap; -}​ - - +} -.navlink-long { - display:inline-block; - vertical-align: top; - padding:5px; +​ .navlink-long { + display: inline-block; + vertical-align: top; + padding: 5px; } .navlink-short { - display:none; - vertical-align: top; - padding:5px; + display: none; + vertical-align: top; + padding: 5px; } .footer-nav-bar { - display:block; - background-color:#F8F8F8; - max-width: 1100px; - /*border-bottom: solid;*/ - padding-bottom: 10px; - margin-top:15px; - border-top:solid; - /* border-top-width:thin; */ + display: block; + background-color: #F8F8F8; + max-width: 1100px; + /*border-bottom: solid;*/ + padding-bottom: 10px; + margin-top: 15px; + border-top: solid; + /* border-top-width:thin; */ } .footer-options { -/* display:block; + /* display:block; width:inherit; font-size:0.8em; font-weight:normal; */ -display:block; -text-align:justify; -font-size:0.8em; -width:inherit; + display: block; + text-align: justify; + font-size: 0.8em; + width: inherit; } .footer-navlink-long { - display: inline-block; - vertical-align: top; - padding:5px; + display: inline-block; + vertical-align: top; + padding: 5px; } .footer-navlink-short { - display:none; - padding:5px; + display: none; + padding: 5px; } .footer-options:after { content: ""; - width: 100%; - line-height:1px; - line-spacing:1px; - display: inline-block; - } - + width: 100%; + line-height: 1px; + line-spacing: 1px; + display: inline-block; +} + .copyright-box { - border-top:solid; - border-top-width:thin; - margin-top:10px; - background-color:#F8F8F8; - padding-bottom:5px; + border-top: solid; + border-top-width: thin; + margin-top: 10px; + background-color: #F8F8F8; + padding-bottom: 5px; } .copyright-box p { - font-size:0.8em; + font-size: 0.8em; } - - -/* HamishW - Attempt to wrap table columns/remove the "responsive" behaviour on some tables. */ -table.wrap-table-content td, table.wrap-table-content th { - white-space: normal; + + +/* HamishW - Attempt to wrap table columns/remove the "responsive" behaviour on some tables. */ +table.wrap-table-content td, +table.wrap-table-content th { + white-space: normal; } - -/* HamishW - add clear markup for external links */ + +/* HamishW - add clear markup for external links */ a.external:after { content: ""; display: inline-block; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAV0lEQVR4Xq2QwQ2AAAwC3cmd2Kk7sRP64CEJ9qOX8OPatMc/QKppnEPhTmJh23CLiwAqIw21CybKQ28qQi37WGFYBJcwfJQpP8LlEHKyZMF0IdmF13zlAjZ/6H4wb+mUAAAAAElFTkSuQmCC); + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAV0lEQVR4Xq2QwQ2AAAwC3cmd2Kk7sRP64CEJ9qOX8OPatMc/QKppnEPhTmJh23CLiwAqIw21CybKQ28qQi37WGFYBJcwfJQpP8LlEHKyZMF0IdmF13zlAjZ/6H4wb+mUAAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: right top; background-origin: border-box; @@ -396,18 +8004,18 @@ a.external:after { height: 16px; } - + /* HamishW - some CSS for the breadcrumb (make elements inline blocks) */ .breadcrumb-box { - margin-top:10px; - font-size:0.8em; - } + margin-top: 10px; + font-size: 0.8em; +} -.breadcrumb-box-item{ - display: inline-block; - } +.breadcrumb-box-item { + display: inline-block; +} /* indent third level item */ @@ -417,10 +8025,8 @@ a.external:after { .wy-menu-vertical li.toctree-l2.current>a, .wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>a, -.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current>a -.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current li.toctree-l5.current>a - { -background: #c9c9c9; +.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current>a .wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current li.toctree-l5.current>a { + background: #c9c9c9; } @@ -432,82 +8038,91 @@ background: purple; */ .wy-menu-vertical li.toctree-l2 li.toctree-l3>a { -display:none; + display: none; } .wy-menu-vertical li.toctree-l2.current li.toctree-l3>a { -display:block; -font-size:0.8em; -/*padding-top: 0.4045em;*/ -padding-right: 2.427em; -padding-bottom: 0.4045em; -padding-left: 4.25em; -width:100%; + display: block; + font-size: 0.8em; + /*padding-top: 0.4045em;*/ + padding-right: 2.427em; + padding-bottom: 0.4045em; + padding-left: 4.25em; + width: 100%; } .wy-menu-vertical li.toctree-l2 li.toctree-l3 li.toctree-l4>a { -display:none; + display: none; } .wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4>a { -/* + /* background: #F0EEEE; background: purple; */ -display:block; -font-size:0.8em; -/*padding-top: 0.4045em;*/ -padding-right: 2.427em; -padding-bottom: 0.4045em; -padding-left: 5.0em; -width:100%; + display: block; + font-size: 0.8em; + /*padding-top: 0.4045em;*/ + padding-right: 2.427em; + padding-bottom: 0.4045em; + padding-left: 5.0em; + width: 100%; } - { -background: #c9c9c9; + { + background: #c9c9c9; } .wy-menu-vertical a[href^="#"] { -background:#F0EEEE; + background: #F0EEEE; } .grid-to-center-rtd-theme { - margin-left:0; - margin-right:auto; - max-width: 1100px; - } + margin-left: 0; + margin-right: auto; + max-width: 1100px; +} -@media only screen and (min-width : 1100px){ +@media only screen and (min-width : 1100px) { + + .grid-to-center-rtd-theme { + margin-left: auto; + /* max-width: 1100px; */ + } -.grid-to-center-rtd-theme { - margin-left:auto; - /* max-width: 1100px; */ - } - } - -@media only screen -and (max-width : 480px) { -/* Styles */ - .navlink-long, .footer-navlink-long { - display:none; - } - .navlink-short, .footer-navlink-short { - display:inline-block; - vertical-align: top;} - +@media only screen and (max-width : 480px) { + + /* Styles */ + .navlink-long, + .footer-navlink-long { + display: none; + } + + .navlink-short, + .footer-navlink-short { + display: inline-block; + vertical-align: top; + } + } @media screen and (min-width: 480px) and (max-width: 768px) { - .navlink-long, .footer-navlink-long { - display:inline-block; - } - .navlink-short, .footer-navlink-short { - display:none; - } + + .navlink-long, + .footer-navlink-long { + display: inline-block; + } + + .navlink-short, + .footer-navlink-short { + display: none; + } } -.cellborder { border: solid 1px black; } \ No newline at end of file +.cellborder { + border: solid 1px black; +} \ No newline at end of file diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot b/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot deleted file mode 100644 index 7c79c6a6bc9a1..0000000000000 Binary files a/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg b/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg deleted file mode 100644 index 45fdf33830123..0000000000000 --- a/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,414 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf b/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf deleted file mode 100644 index e89738de5eaf8..0000000000000 Binary files a/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff b/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff deleted file mode 100644 index 8c1748aab7a79..0000000000000 Binary files a/site/source/_themes/emscripten_sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/site/source/_themes/emscripten_sphinx_rtd_theme/static/js/theme.js b/site/source/_themes/emscripten_sphinx_rtd_theme/static/js/theme.js index 60520cc3adb2c..a6c836093fb67 100644 --- a/site/source/_themes/emscripten_sphinx_rtd_theme/static/js/theme.js +++ b/site/source/_themes/emscripten_sphinx_rtd_theme/static/js/theme.js @@ -1,47 +1 @@ -$( document ).ready(function() { - // Shift nav in mobile when clicking the menu. - $(document).on('click', "[data-toggle='wy-nav-top']", function() { - $("[data-toggle='wy-nav-shift']").toggleClass("shift"); - $("[data-toggle='rst-versions']").toggleClass("shift"); - }); - // Close menu when you click a link. - $(document).on('click', ".wy-menu-vertical .current ul li a", function() { - $("[data-toggle='wy-nav-shift']").removeClass("shift"); - $("[data-toggle='rst-versions']").toggleClass("shift"); - }); - $(document).on('click', "[data-toggle='rst-current-version']", function() { - $("[data-toggle='rst-versions']").toggleClass("shift-up"); - }); - // Make tables responsive - $("table.docutils:not(.field-list)").wrap("
"); -}); - -window.SphinxRtdTheme = (function (jquery) { - var stickyNav = (function () { - var navBar, - win, - stickyNavCssClass = 'stickynav', - applyStickNav = function () { - if (navBar.height() <= win.height()) { - navBar.addClass(stickyNavCssClass); - } else { - navBar.removeClass(stickyNavCssClass); - } - }, - enable = function () { - applyStickNav(); - win.on('resize', applyStickNav); - }, - init = function () { - navBar = jquery('nav.wy-nav-side:first'); - win = jquery(window); - }; - jquery(init); - return { - enable : enable - }; - }()); - return { - StickyNav : stickyNav - }; -}($)); +!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");/*t[0].scrollIntoView()*/}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t - - Read the Docs - v: {{ current_version }} - - -
-
-
Versions
- {% for slug, url in versions %} -
{{ slug }}
- {% endfor %} -
-
-
Downloads
- {% for type, url in downloads %} -
{{ type }}
- {% endfor %} -
-
-
On Read the Docs
-
- Project Home -
-
- Builds -
-
-
- Free document hosting provided by Read the Docs. - -
- -{% endif %} - diff --git a/site/source/conf.py b/site/source/conf.py index 90532815f185a..a4d3d3dd2fe67 100644 --- a/site/source/conf.py +++ b/site/source/conf.py @@ -56,6 +56,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. +sys.path.insert(0, os.path.abspath('_themes')) extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', @@ -64,6 +65,8 @@ 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', + 'sphinxcontrib.jquery', + 'emscripten_sphinx_rtd_theme', # 'breathe', #added by HamishW ] @@ -75,7 +78,7 @@ templates_path = ['_templates'] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = {'.rst': 'restructuredtext'} # The encoding of source files. #source_encoding = 'utf-8-sig' @@ -94,7 +97,7 @@ # |version| and |release|, also used in various other places throughout the # built documents. # -version_path = Path(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'emscripten-version.txt').resolve() +version_path = Path(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'emscripten-version.txt') emscripten_version = version_path.read_text().strip().strip('"') # The short X.Y version. @@ -161,10 +164,9 @@ # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = { -# "rightsidebar": "true", -# "relbarbgcolor": "black" -#} +html_theme_options = { + 'logo_only': True +} # Add any paths that contain custom themes here, relative to this directory. @@ -390,10 +392,6 @@ # If false, no index is generated. #epub_use_index = True - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} - #highlight_language = 'default' primary_domain = 'cpp' diff --git a/site/source/docs/api_reference/Filesystem-API.rst b/site/source/docs/api_reference/Filesystem-API.rst index 60761c6cc9713..99c5e23ffb78a 100644 --- a/site/source/docs/api_reference/Filesystem-API.rst +++ b/site/source/docs/api_reference/Filesystem-API.rst @@ -23,7 +23,7 @@ A high level overview of the way File Systems work in Emscripten-ported code is New File System: WasmFS ======================= -.. note:: Current Status: Work in Progress +.. note:: Current Status: Stable, but not yet feature-complete with the old FS. WasmFS is a high-performance, fully-multithreaded, WebAssembly-based file system layer for Emscripten that will replace the existing JavaScript version. @@ -297,6 +297,20 @@ File system API :param int mode: :ref:`File permissions ` for the new node. The default setting (`in octal numeric notation `_) is 0777. +.. js:function:: FS.mkdirTree(path, mode) + + Creates a new directory node and all parent directories in the file system. For example: + + .. code-block:: javascript + + FS.mkdirTree('/data/subdir1/subdir2'); + + .. note:: The underlying implementation does not support user or group permissions. The caller is always treated as the owner of the folder, and only permissions relevant to the owner apply. + + :param string path: The path name for the new directory node. + :param int mode: :ref:`File permissions ` for the new node. The default setting (`in octal numeric notation `_) is 0777. + + .. js:function:: FS.mkdev(path, mode, dev) Creates a new device node in the file system referencing the registered device driver (:js:func:`FS.registerDevice`) for ``dev``. For example: diff --git a/site/source/docs/api_reference/advanced-apis.rst b/site/source/docs/api_reference/advanced-apis.rst index 23a917defc608..1b53e46720ff5 100644 --- a/site/source/docs/api_reference/advanced-apis.rst +++ b/site/source/docs/api_reference/advanced-apis.rst @@ -91,7 +91,6 @@ example, writing a new local file system) or legacy file system compatibility. .. js:function:: FS.lookup(parent, name) .. js:function:: FS.mknod(path, mode, dev) .. js:function:: FS.create(path, mode) -.. js:function:: FS.allocate(stream, offset, length) .. js:function:: FS.mmap(stream, buffer, offset, length, position, prot, flags) .. js:function:: FS.ioctl(stream, cmd, arg) .. js:function:: FS.staticInit() diff --git a/site/source/docs/api_reference/emscripten.h.rst b/site/source/docs/api_reference/emscripten.h.rst index a29edbc51785a..f1cbb1ffed2cf 100644 --- a/site/source/docs/api_reference/emscripten.h.rst +++ b/site/source/docs/api_reference/emscripten.h.rst @@ -349,7 +349,7 @@ Guide material for the following APIs can be found in :ref:`emscripten-runtime-e Functions --------- -.. c:function:: void emscripten_set_main_loop(em_callback_func func, int fps, int simulate_infinite_loop) +.. c:function:: void emscripten_set_main_loop(em_callback_func func, int fps, bool simulate_infinite_loop) Set a C function as the main event loop for the calling thread. @@ -373,10 +373,10 @@ Functions :param em_callback_func func: C function to set as main event loop for the calling thread. :param int fps: Number of frames per second that the JavaScript will call the function. Setting ``int <=0`` (recommended) uses the browser’s ``requestAnimationFrame`` mechanism to call the function. - :param int simulate_infinite_loop: If true, this function will throw an exception in order to stop execution of the caller. + :param bool simulate_infinite_loop: If true, this function will throw an exception in order to stop execution of the caller. -.. c:function:: void emscripten_set_main_loop_arg(em_arg_callback_func func, void *arg, int fps, int simulate_infinite_loop) +.. c:function:: void emscripten_set_main_loop_arg(em_arg_callback_func func, void *arg, int fps, bool simulate_infinite_loop) Set a C function as the main event loop for the calling thread, passing it user-defined data. @@ -385,7 +385,7 @@ Functions :param em_arg_callback_func func: C function to set as main event loop. The function signature must have a ``void*`` parameter for passing the ``arg`` value. :param void* arg: User-defined data passed to the main loop function, untouched by the API itself. :param int fps: Number of frames per second at which the JavaScript will call the function. Setting ``int <=0`` (recommended) uses the browser’s ``requestAnimationFrame`` mechanism to call the function. - :param int simulate_infinite_loop: If true, this function will throw an exception in order to stop execution of the caller. + :param bool simulate_infinite_loop: If true, this function will throw an exception in order to stop execution of the caller. .. c:function:: void emscripten_push_main_loop_blocker(em_arg_callback_func func, void *arg) @@ -399,7 +399,7 @@ Functions .. note:: - - Main loop blockers block the main loop from running, and can be counted to show progress. In contrast, ``emscripten_async_calls`` are not counted, do not block the main loop, and can fire at specific time in the future. + - Main loop blockers block the main loop from running, and can be counted to show progress. In contrast, :c:func:`emscripten_async_call` is not counted, does not block the main loop, and can fire at a specific time in the future. :param em_arg_callback_func func: The main loop blocker function. The function signature must have a ``void*`` parameter for passing the ``arg`` value. :param void* arg: User-defined arguments to pass to the blocker function. @@ -1434,22 +1434,6 @@ Functions local state all the way up the stack. As a result, it will add overhead to your program. -.. c:function:: void emscripten_lazy_load_code() - - This creates two Wasm files at compile time: the first Wasm which is - downloaded and run normally, and a second that is lazy-loaded. When an - ``emscripten_lazy_load_code()`` call is reached, we load the second Wasm - and resume execution using it. - - The idea here is that the initial download can be quite small, if you - place enough ``emscripten_lazy_load_code()`` calls in your codebase, as - the optimizer can remove code from the first Wasm if it sees it can't - be reached. The second downloaded Wasm can contain your full codebase, - including rarely-used functions, in which case the lazy-loading may - not happen at all. - - .. note:: This requires building with ``-sASYNCIFY_LAZY_LOAD_CODE``. - ABI functions ============= diff --git a/site/source/docs/api_reference/fetch.rst b/site/source/docs/api_reference/fetch.rst index e361b549ba242..3c0cf98ee66f4 100644 --- a/site/source/docs/api_reference/fetch.rst +++ b/site/source/docs/api_reference/fetch.rst @@ -348,6 +348,16 @@ be dealt with separately. emscripten_fetch(&attr, "myfile.dat"); } +Redirections +============ + +The XHR requests issued by the Fetch API are subject to the usual browser +redirection behavior (transparently followed except when infinitely looping). +When this happens, the `emscripten_fetch_t` structure will contain the final URL +after the redirection in the `responseUrl` field. Many HTTP status codes in the 3xx +range are used for redirection, and the Fetch API will follow these, in particular +the 301 (Moved Permanently), 302 (Found), 303 (See Other), 307 (Temporary Redirect), +and 308 (Permanent Redirect) status codes. TODO To Document ================ diff --git a/site/source/docs/api_reference/html5.h.rst b/site/source/docs/api_reference/html5.h.rst index 6e087a20a230c..23155eefd4307 100644 --- a/site/source/docs/api_reference/html5.h.rst +++ b/site/source/docs/api_reference/html5.h.rst @@ -53,7 +53,7 @@ The typical format of registration functions is as follows (some methods may omi EMSCRIPTEN_RESULT emscripten_set_some_callback( const char *target, // ID of the target HTML element. void *userData, // User-defined data to be passed to the callback. - EM_BOOL useCapture, // Whether or not to use capture. + bool useCapture, // Whether or not to use capture. em_someevent_callback_func callback // Callback function. ); @@ -96,7 +96,7 @@ Callback functions When the event occurs the callback is invoked with the relevant event "type" (for example :c:data:`EMSCRIPTEN_EVENT_CLICK`), a ``struct`` containing the details of the event that occurred, and the ``userData`` that was originally passed to the registration function. The general format of the callback function is: :: - typedef EM_BOOL (*em_someevent_callback_func) // Callback function. Return true if event is "consumed". + typedef bool (*em_someevent_callback_func) // Callback function. Return true if event is "consumed". ( int eventType, // The type of event. const EmscriptenSomeEvent *someEvent, // Information about the event. @@ -106,7 +106,7 @@ When the event occurs the callback is invoked with the relevant event "type" (fo .. _callback-handler-return-em_bool-html5-api: -Callback handlers that return an :c:data:`EM_BOOL` may specify ``true`` to signal that the handler *consumed* the event (this suppresses the default action for that event by calling its ``.preventDefault();`` member). Returning ``false`` indicates that the event was not consumed — the default browser event action is carried out and the event is allowed to pass on/bubble up as normal. +Callback handlers that return a ``bool`` may specify ``true`` to signal that the handler *consumed* the event (this suppresses the default action for that event by calling its ``.preventDefault();`` member). Returning ``false`` indicates that the event was not consumed — the default browser event action is carried out and the event is allowed to pass on/bubble up as normal. Calling a registration function with a ``null`` pointer for the callback causes a de-registration of that callback from the given ``target`` element. All event handlers are also automatically unregistered when the C ``exit()`` function is invoked during the ``atexit`` handler pass. Either use the function :c:func:`emscripten_set_main_loop` or set ``Module.noExitRuntime = true;`` to make sure that leaving ``main()`` will not immediately cause an ``exit()`` and clean up the event handlers. @@ -140,26 +140,11 @@ General types ============= -.. c:macro:: EM_BOOL - - This is the Emscripten type for a ``bool``. - Possible values: - - .. c:macro:: EM_TRUE - - This is the Emscripten value for ``true``. - - .. c:macro:: EM_FALSE - - This is the Emscripten value for ``false``. - - .. c:macro:: EM_UTF8 This is the Emscripten type for a UTF8 string (maps to a ``char``). This is used for node names, element ids, etc. - Function result values ====================== @@ -259,18 +244,18 @@ Struct Maximum size 32 ``char`` (i.e. ``EM_UTF8 code[32]``). - .. c:member:: unsigned long location + .. c:member:: unsigned int location Indicates the location of the key on the keyboard. One of the :c:data:`DOM_KEY_LOCATION ` values. - .. c:member:: EM_BOOL ctrlKey - EM_BOOL shiftKey - EM_BOOL altKey - EM_BOOL metaKey + .. c:member:: bool ctrlKey + bool shiftKey + bool altKey + bool metaKey Specifies which modifiers were active during the key event. - .. c:member:: EM_BOOL repeat + .. c:member:: bool repeat Specifies if this keyboard event represents a repeated press. @@ -288,20 +273,20 @@ Struct .. warning:: This attribute has been dropped from DOM Level 3 events. - .. c:member:: unsigned long charCode + .. c:member:: unsigned int charCode The Unicode reference number of the key; this attribute is used only by the keypress event. For keys whose ``char`` attribute contains multiple characters, this is the Unicode value of the first character in that attribute. .. warning:: This attribute is deprecated, you should use the field ``key`` instead, if available. - .. c:member:: unsigned long keyCode + .. c:member:: unsigned int keyCode A system and implementation dependent numerical code identifying the unmodified value of the pressed key. .. warning:: This attribute is deprecated, you should use the field ``key`` instead, if available. - .. c:member:: unsigned long which + .. c:member:: unsigned int which A system and implementation dependent numeric code identifying the unmodified value of the pressed key; this is usually the same as ``keyCode``. @@ -317,29 +302,29 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_key_callback_func)(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); + typedef bool (*em_key_callback_func)(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); :param int eventType: The type of :c:data:`key event `. :param keyEvent: Information about the key event that occurred. :type keyEvent: const EmscriptenKeyboardEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_keypress_callback(const char *target, void *userData, EM_BOOL useCapture, em_key_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_keydown_callback(const char *target, void *userData, EM_BOOL useCapture, em_key_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_keyup_callback(const char *target, void *userData, EM_BOOL useCapture, em_key_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_keypress_callback(const char *target, void *userData, bool useCapture, em_key_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_keydown_callback(const char *target, void *userData, bool useCapture, em_key_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_keyup_callback(const char *target, void *userData, bool useCapture, em_key_callback_func callback) Registers a callback function for receiving browser-generated keyboard input events. :param target: |target-parameter-doc| :type target: const char* :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_key_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -379,21 +364,21 @@ Struct Absolute wallclock time when the data was recorded (milliseconds). - .. c:member:: long screenX - long screenY + .. c:member:: int screenX + int screenY The coordinates relative to the browser screen coordinate system. - .. c:member:: long clientX - long clientY + .. c:member:: int clientX + int clientY The coordinates relative to the viewport associated with the event. - .. c:member:: EM_BOOL ctrlKey - EM_BOOL shiftKey - EM_BOOL altKey - EM_BOOL metaKey + .. c:member:: bool ctrlKey + bool shiftKey + bool altKey + bool metaKey Specifies which modifiers were active during the mouse event. @@ -411,24 +396,24 @@ Struct A bitmask that indicates which combinations of mouse buttons were being held down at the time of the event. - .. c:member:: long movementX - long movementY; + .. c:member:: int movementX + int movementY; If pointer lock is active, these two extra fields give relative mouse movement since the last event. - .. c:member:: long targetX - long targetY + .. c:member:: int targetX + int targetY These fields give the mouse coordinates mapped relative to the coordinate space of the target DOM element receiving the input events (Emscripten-specific extension; coordinates are rounded down to the nearest integer). - .. c:member:: long canvasX - long canvasY + .. c:member:: int canvasX + int canvasY These fields give the mouse coordinates mapped to the Emscripten canvas client area (Emscripten-specific extension; coordinates are rounded down the nearest integer). - .. c:member:: long padding + .. c:member:: int padding Internal, and can be ignored. @@ -444,34 +429,34 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_mouse_callback_func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); + typedef bool (*em_mouse_callback_func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); :param int eventType: The type of :c:data:`mouse event `. :param mouseEvent: Information about the mouse event that occurred. :type mouseEvent: const EmscriptenMouseEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_click_callback(const char *target, void *userData, EM_BOOL useCapture, em_mouse_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_mousedown_callback(const char *target, void *userData, EM_BOOL useCapture, em_mouse_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_mouseup_callback(const char *target, void *userData, EM_BOOL useCapture, em_mouse_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_dblclick_callback(const char *target, void *userData, EM_BOOL useCapture, em_mouse_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_mousemove_callback(const char *target, void *userData, EM_BOOL useCapture, em_mouse_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_mouseenter_callback(const char *target, void *userData, EM_BOOL useCapture, em_mouse_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_mouseleave_callback(const char *target, void *userData, EM_BOOL useCapture, em_mouse_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_click_callback(const char *target, void *userData, bool useCapture, em_mouse_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_mousedown_callback(const char *target, void *userData, bool useCapture, em_mouse_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_mouseup_callback(const char *target, void *userData, bool useCapture, em_mouse_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_dblclick_callback(const char *target, void *userData, bool useCapture, em_mouse_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_mousemove_callback(const char *target, void *userData, bool useCapture, em_mouse_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_mouseenter_callback(const char *target, void *userData, bool useCapture, em_mouse_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_mouseleave_callback(const char *target, void *userData, bool useCapture, em_mouse_callback_func callback) Registers a callback function for receiving browser-generated `mouse input events `_. :param target: |target-parameter-doc| :type target: const char* :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_mouse_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -532,7 +517,7 @@ Struct in scroll values of 0. The positive Y scroll direction is when scrolling the page downwards (page CSS pixel +Y direction), which corresponds to scrolling the mouse wheel downwards (away from the screen) on Windows, Linux, and also on macOS when the 'natural scroll' option is disabled. - .. c:member:: unsigned long deltaMode + .. c:member:: unsigned int deltaMode One of the :c:data:`DOM_DELTA_` values that indicates the units of measurement for the delta values. @@ -546,28 +531,28 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_wheel_callback_func)(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData); + typedef bool (*em_wheel_callback_func)(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData); :param int eventType: The type of wheel event (:c:data:`EMSCRIPTEN_EVENT_WHEEL`). :param wheelEvent: Information about the wheel event that occurred. :type wheelEvent: const EmscriptenWheelEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_wheel_callback(const char *target, void *userData, EM_BOOL useCapture, em_wheel_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_wheel_callback(const char *target, void *userData, bool useCapture, em_wheel_callback_func callback) Registers a callback function for receiving browser-generated `mousewheel events `_. :param target: |target-parameter-doc| :type target: const char* :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_wheel_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -594,7 +579,7 @@ Struct The event structure passed in DOM element `UIEvent `_ events: `resize `_ and `scroll `_. - .. c:member:: long detail + .. c:member:: int detail For resize and scroll events this is always zero. @@ -628,21 +613,21 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_ui_callback_func)(int eventType, const EmscriptenUiEvent *uiEvent, void *userData); + typedef bool (*em_ui_callback_func)(int eventType, const EmscriptenUiEvent *uiEvent, void *userData); :param int eventType: The type of UI event (:c:data:`EMSCRIPTEN_EVENT_RESIZE`). :param uiEvent: Information about the UI event that occurred. :type uiEvent: const EmscriptenUiEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_resize_callback(const char *target, void *userData, EM_BOOL useCapture, em_ui_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_scroll_callback(const char *target, void *userData, EM_BOOL useCapture, em_ui_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_resize_callback(const char *target, void *userData, bool useCapture, em_ui_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_scroll_callback(const char *target, void *userData, bool useCapture, em_ui_callback_func callback) Registers a callback function for receiving DOM element `resize `_ and `scroll `_ events. @@ -654,7 +639,7 @@ Functions :param target: |target-parameter-doc| :type target: const char* :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_ui_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -706,31 +691,31 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_focus_callback_func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData); + typedef bool (*em_focus_callback_func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData); :param int eventType: The type of focus event (:c:data:`EMSCRIPTEN_EVENT_BLUR`). :param focusEvent: Information about the focus event that occurred. :type focusEvent: const EmscriptenFocusEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_blur_callback(const char *target, void *userData, EM_BOOL useCapture, em_focus_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_focus_callback(const char *target, void *userData, EM_BOOL useCapture, em_focus_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_focusin_callback(const char *target, void *userData, EM_BOOL useCapture, em_focus_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_focusout_callback(const char *target, void *userData, EM_BOOL useCapture, em_focus_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_blur_callback(const char *target, void *userData, bool useCapture, em_focus_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_focus_callback(const char *target, void *userData, bool useCapture, em_focus_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_focusin_callback(const char *target, void *userData, bool useCapture, em_focus_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_focusout_callback(const char *target, void *userData, bool useCapture, em_focus_callback_func callback) Registers a callback function for receiving DOM element `blur `_, `focus `_, `focusin `_ and `focusout `_ events. :param target: |target-parameter-doc| :type target: const char* :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_focus_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -772,7 +757,7 @@ Struct :alt: Image of device showing X, Y, Z axes - .. c:member:: EM_BOOL absolute + .. c:member:: bool absolute If ``false``, the orientation is only relative to some other base orientation, not to the fixed coordinate frame. @@ -786,26 +771,26 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_deviceorientation_callback_func)(int eventType, const EmscriptenDeviceOrientationEvent *deviceOrientationEvent, void *userData); + typedef bool (*em_deviceorientation_callback_func)(int eventType, const EmscriptenDeviceOrientationEvent *deviceOrientationEvent, void *userData); :param int eventType: The type of orientation event (:c:data:`EMSCRIPTEN_EVENT_DEVICEORIENTATION`). :param deviceOrientationEvent: Information about the orientation event that occurred. :type deviceOrientationEvent: const EmscriptenDeviceOrientationEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_deviceorientation_callback(void *userData, EM_BOOL useCapture, em_deviceorientation_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_deviceorientation_callback(void *userData, bool useCapture, em_deviceorientation_callback_func callback) Registers a callback function for receiving the `deviceorientation `_ event. :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_deviceorientation_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -815,7 +800,7 @@ Functions Returns the most recently received ``deviceorientation`` event state. - Note that for this function call to succeed, :c:func:`emscripten_set_deviceorientation_callback` must have first been called with one of the mouse event types and a non-zero callback function pointer to enable the ``deviceorientation`` state capture. + Note that for this function call to succeed, :c:func:`emscripten_set_deviceorientation_callback` must have first been called with a non-zero callback function pointer to enable the ``deviceorientation`` state capture. :param orientationState: The most recently received ``deviceorientation`` event state. :type orientationState: EmscriptenDeviceOrientationEvent* @@ -877,14 +862,14 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_devicemotion_callback_func)(int eventType, const EmscriptenDeviceMotionEvent *deviceMotionEvent, void *userData); + typedef bool (*em_devicemotion_callback_func)(int eventType, const EmscriptenDeviceMotionEvent *deviceMotionEvent, void *userData); :param int eventType: The type of devicemotion event (:c:data:`EMSCRIPTEN_EVENT_DEVICEMOTION`). :param deviceMotionEvent: Information about the devicemotion event that occurred. :type deviceMotionEvent: const EmscriptenDeviceMotionEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool @@ -892,12 +877,12 @@ Callback functions Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_devicemotion_callback(void *userData, EM_BOOL useCapture, em_devicemotion_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_devicemotion_callback(void *userData, bool useCapture, em_devicemotion_callback_func callback) Registers a callback function for receiving the `devicemotion `_ event. :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_devicemotion_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -907,7 +892,7 @@ Functions Returns the most recently received `devicemotion `_ event state. - Note that for this function call to succeed, :c:func:`emscripten_set_devicemotion_callback` must have first been called with one of the mouse event types and a non-zero callback function pointer to enable the ``devicemotion`` state capture. + Note that for this function call to succeed, :c:func:`emscripten_set_devicemotion_callback` must have first been called with a non-zero callback function pointer to enable the ``devicemotion`` state capture. :param motionState: The most recently received ``devicemotion`` event state. :type motionState: EmscriptenDeviceMotionEvent* @@ -976,25 +961,25 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_orientationchange_callback_func)(int eventType, const EmscriptenOrientationChangeEvent *orientationChangeEvent, void *userData); + typedef bool (*em_orientationchange_callback_func)(int eventType, const EmscriptenOrientationChangeEvent *orientationChangeEvent, void *userData); :param int eventType: The type of orientationchange event (:c:data:`EMSCRIPTEN_EVENT_ORIENTATIONCHANGE`). :param orientationChangeEvent: Information about the orientationchange event that occurred. :type orientationChangeEvent: const EmscriptenOrientationChangeEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_orientationchange_callback(void *userData, EM_BOOL useCapture, em_orientationchange_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_orientationchange_callback(void *userData, bool useCapture, em_orientationchange_callback_func callback) Registers a callback function for receiving the `orientationchange `_ event. :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_orientationchange_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -1105,12 +1090,12 @@ Struct The event structure passed in the `fullscreenchange `_ event. - .. c:member:: EM_BOOL isFullscreen + .. c:member:: bool isFullscreen Specifies whether an element on the browser page is currently fullscreen. - .. c:member:: EM_BOOL fullscreenEnabled + .. c:member:: bool fullscreenEnabled Specifies if the current page has the ability to display elements fullscreen. @@ -1176,28 +1161,28 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_fullscreenchange_callback_func)(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData); + typedef bool (*em_fullscreenchange_callback_func)(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData); :param int eventType: The type of fullscreen event (:c:data:`EMSCRIPTEN_EVENT_FULLSCREENCHANGE`). :param fullscreenChangeEvent: Information about the fullscreen event that occurred. :type fullscreenChangeEvent: const EmscriptenFullscreenChangeEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_fullscreenchange_callback(const char *target, void *userData, EM_BOOL useCapture, em_fullscreenchange_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_fullscreenchange_callback(const char *target, void *userData, bool useCapture, em_fullscreenchange_callback_func callback) Registers a callback function for receiving the `fullscreenchange `_ event. :param target: |target-parameter-doc| :type target: const char* :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_fullscreenchange_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -1213,7 +1198,7 @@ Functions :rtype: |EMSCRIPTEN_RESULT| -.. c:function:: EMSCRIPTEN_RESULT emscripten_request_fullscreen(const char *target, EM_BOOL deferUntilInEventHandler) +.. c:function:: EMSCRIPTEN_RESULT emscripten_request_fullscreen(const char *target, bool deferUntilInEventHandler) Requests the given target element to transition to full screen mode. @@ -1223,12 +1208,12 @@ Functions :param target: |target-parameter-doc| :type target: const char* - :param EM_BOOL deferUntilInEventHandler: If ``true`` requests made outside of a user-generated event handler are automatically deferred until the user next presses a keyboard or mouse button. If ``false`` the request will fail if called outside of a user-generated event handler. + :param bool deferUntilInEventHandler: If ``true`` requests made outside of a user-generated event handler are automatically deferred until the user next presses a keyboard or mouse button. If ``false`` the request will fail if called outside of a user-generated event handler. :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: **EMSCRIPTEN_RESULT** -.. c:function:: EMSCRIPTEN_RESULT emscripten_request_fullscreen_strategy(const char *target, EM_BOOL deferUntilInEventHandler, const EmscriptenFullscreenStrategy *fullscreenStrategy) +.. c:function:: EMSCRIPTEN_RESULT emscripten_request_fullscreen_strategy(const char *target, bool deferUntilInEventHandler, const EmscriptenFullscreenStrategy *fullscreenStrategy) Requests the given target element to transition to full screen mode, using a custom presentation mode for the element. This function is otherwise the same as :c:func:`emscripten_request_fullscreen`, but this function adds options to control how resizing and aspect ratio, and ensures that the behavior is consistent across browsers. @@ -1278,7 +1263,7 @@ Struct The event structure passed in the `pointerlockchange `_ event. - .. c:member:: EM_BOOL isActive + .. c:member:: bool isActive Specifies whether an element on the browser page currently has pointer lock enabled. @@ -1304,14 +1289,14 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_pointerlockchange_callback_func)(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData); + typedef bool (*em_pointerlockchange_callback_func)(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData); :param int eventType: The type of pointerlockchange event (:c:data:`EMSCRIPTEN_EVENT_POINTERLOCKCHANGE`). :param pointerlockChangeEvent: Information about the pointerlockchange event that occurred. :type pointerlockChangeEvent: const EmscriptenPointerlockChangeEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool .. c:type:: em_pointerlockerror_callback_func @@ -1319,20 +1304,20 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_pointerlockerror_callback_func)(int eventType, const void *reserved, void *userData); + typedef bool (*em_pointerlockerror_callback_func)(int eventType, const void *reserved, void *userData); :param int eventType: The type of pointerlockerror event (:c:data:`EMSCRIPTEN_EVENT_POINTERLOCKERROR`). :param const void* reserved: Reserved for future use; pass in 0. :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_pointerlockchange_callback(const char *target, void *userData, EM_BOOL useCapture, em_pointerlockchange_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_pointerlockchange_callback(const char *target, void *userData, bool useCapture, em_pointerlockchange_callback_func callback) Registers a callback function for receiving the `pointerlockchange `_ event. @@ -1341,21 +1326,21 @@ Functions :param target: |target-parameter-doc| :type target: const char* :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_pointerlockchange_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_pointerlockerror_callback(const char *target, void *userData, EM_BOOL useCapture, em_pointerlockerror_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_pointerlockerror_callback(const char *target, void *userData, bool useCapture, em_pointerlockerror_callback_func callback) Registers a callback function for receiving the `pointerlockerror `_ event. :param target: |target-parameter-doc| :type target: const char* :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_pointerlockerror_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -1371,7 +1356,7 @@ Functions :rtype: |EMSCRIPTEN_RESULT| -.. c:function:: EMSCRIPTEN_RESULT emscripten_request_pointerlock(const char *target, EM_BOOL deferUntilInEventHandler) +.. c:function:: EMSCRIPTEN_RESULT emscripten_request_pointerlock(const char *target, bool deferUntilInEventHandler) Requests the given target element to grab pointerlock. @@ -1380,7 +1365,7 @@ Functions :param target: |target-parameter-doc| :type target: const char* - :param EM_BOOL deferUntilInEventHandler: If ``true`` requests made outside of a user-generated event handler are automatically deferred until the user next presses a keyboard or mouse button. If ``false`` the request will fail if called outside of a user-generated event handler. + :param bool deferUntilInEventHandler: If ``true`` requests made outside of a user-generated event handler are automatically deferred until the user next presses a keyboard or mouse button. If ``false`` the request will fail if called outside of a user-generated event handler. :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -1428,7 +1413,7 @@ Struct The event structure passed in the `visibilitychange `__ event. - .. c:member:: EM_BOOL hidden + .. c:member:: bool hidden If true, the current browser page is now hidden. @@ -1447,25 +1432,25 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_visibilitychange_callback_func)(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData); + typedef bool (*em_visibilitychange_callback_func)(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData); :param int eventType: The type of ``visibilitychange`` event (:c:data:`EMSCRIPTEN_VISIBILITY_HIDDEN`). :param visibilityChangeEvent: Information about the ``visibilitychange`` event that occurred. :type visibilityChangeEvent: const EmscriptenVisibilityChangeEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_visibilitychange_callback(void *userData, EM_BOOL useCapture, em_visibilitychange_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_visibilitychange_callback(void *userData, bool useCapture, em_visibilitychange_callback_func callback) Registers a callback function for receiving the `visibilitychange `_ event. :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_visibilitychange_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -1502,40 +1487,40 @@ Struct Specifies the status of a single `touch point `_ on the page. - .. c:member:: long identifier + .. c:member:: int identifier An identification number for each touch point. - .. c:member:: long screenX - long screenY + .. c:member:: int screenX + int screenY The touch coordinate relative to the whole screen origin, in pixels. - .. c:member:: long clientX - long clientY + .. c:member:: int clientX + int clientY The touch coordinate relative to the viewport, in pixels. - .. c:member:: long pageX - long pageY + .. c:member:: int pageX + int pageY The touch coordinate relative to the viewport, in pixels, and including any scroll offset. - .. c:member:: EM_BOOL isChanged + .. c:member:: bool isChanged Specifies whether the touch point changed during this event. - .. c:member:: EM_BOOL onTarget + .. c:member:: bool onTarget Specifies whether this touch point is still above the original target on which it was initially pressed. - .. c:member:: long targetX - long targetY + .. c:member:: int targetX + int targetY These fields give the touch coordinates mapped relative to the coordinate space of the target DOM element receiving the input events (Emscripten-specific extension). - .. c:member:: long canvasX - long canvasY + .. c:member:: int canvasX + int canvasY The touch coordinates mapped to the Emscripten canvas client area, in pixels (Emscripten-specific extension). @@ -1554,10 +1539,10 @@ Struct The number of valid elements in the touches array. - .. c:member:: EM_BOOL ctrlKey - EM_BOOL shiftKey - EM_BOOL altKey - EM_BOOL metaKey + .. c:member:: bool ctrlKey + bool shiftKey + bool altKey + bool metaKey Specifies which modifiers were active during the touch event. @@ -1577,31 +1562,31 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_touch_callback_func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); + typedef bool (*em_touch_callback_func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); :param int eventType: The type of touch event (:c:data:`EMSCRIPTEN_EVENT_TOUCHSTART`). :param touchEvent: Information about the touch event that occurred. :type touchEvent: const EmscriptenTouchEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_touchstart_callback(const char *target, void *userData, EM_BOOL useCapture, em_touch_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_touchend_callback(const char *target, void *userData, EM_BOOL useCapture, em_touch_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_touchmove_callback(const char *target, void *userData, EM_BOOL useCapture, em_touch_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_touchcancel_callback(const char *target, void *userData, EM_BOOL useCapture, em_touch_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_touchstart_callback(const char *target, void *userData, bool useCapture, em_touch_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_touchend_callback(const char *target, void *userData, bool useCapture, em_touch_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_touchmove_callback(const char *target, void *userData, bool useCapture, em_touch_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_touchcancel_callback(const char *target, void *userData, bool useCapture, em_touch_callback_func callback) Registers a callback function for receiving `touch events `__ : `touchstart `_, `touchend `_, `touchmove `_ and `touchcancel `_. :param target: |target-parameter-doc| :type target: const char* :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_touch_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -1650,15 +1635,15 @@ Struct The analog state of the gamepad buttons, in the range [0, 1]. - .. c:member:: EM_BOOL digitalButton[64] + .. c:member:: bool digitalButton[64] The digital state of the gamepad buttons, either 0 or 1. - .. c:member:: EM_BOOL connected + .. c:member:: bool connected Specifies whether this gamepad is connected to the browser page. - .. c:member:: long index + .. c:member:: int index An ordinal associated with this gamepad, zero-based. @@ -1685,27 +1670,27 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_gamepad_callback_func)(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) + typedef bool (*em_gamepad_callback_func)(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) :param int eventType: The type of gamepad event (:c:data:`EMSCRIPTEN_EVENT_GAMEPADCONNECTED`). :param gamepadEvent: Information about the gamepad event that occurred. :type gamepadEvent: const EmscriptenGamepadEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_gamepadconnected_callback(void *userData, EM_BOOL useCapture, em_gamepad_callback_func callback) - EMSCRIPTEN_RESULT emscripten_set_gamepaddisconnected_callback(void *userData, EM_BOOL useCapture, em_gamepad_callback_func callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_gamepadconnected_callback(void *userData, bool useCapture, em_gamepad_callback_func callback) + EMSCRIPTEN_RESULT emscripten_set_gamepaddisconnected_callback(void *userData, bool useCapture, em_gamepad_callback_func callback) Registers a callback function for receiving the gamepad_ events: `gamepadconnected `_ and `gamepaddisconnected `_. :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_gamepad_callback_func callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| @@ -1793,7 +1778,7 @@ Struct Current battery level, on a scale of 0 to 1.0. - .. c:member:: EM_BOOL charging; + .. c:member:: bool charging; ``true`` if the battery is charging, ``false`` otherwise. @@ -1807,14 +1792,14 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_battery_callback_func)(int eventType, const EmscriptenBatteryEvent *batteryEvent, void *userData); + typedef bool (*em_battery_callback_func)(int eventType, const EmscriptenBatteryEvent *batteryEvent, void *userData); :param int eventType: The type of ``batterymanager`` event (:c:data:`EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE`). :param batteryEvent: Information about the ``batterymanager`` event that occurred. :type batteryEvent: const EmscriptenBatteryEvent* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool @@ -1940,29 +1925,29 @@ Struct Specifies `WebGL context creation parameters `_. - .. c:member:: EM_BOOL alpha + .. c:member:: bool alpha If ``true``, request an alpha channel for the context. If you create an alpha channel, you can blend the canvas rendering with the underlying web page contents. Default value: ``true``. - .. c:member:: EM_BOOL depth + .. c:member:: bool depth If ``true``, request a depth buffer of at least 16 bits. If ``false``, no depth buffer will be initialized. Default value: ``true``. - .. c:member:: EM_BOOL stencil + .. c:member:: bool stencil If ``true``, request a stencil buffer of at least 8 bits. If ``false``, no stencil buffer will be initialized. Default value: ``false``. - .. c:member:: EM_BOOL antialias + .. c:member:: bool antialias If ``true``, antialiasing will be initialized with a browser-specified algorithm and quality level. If ``false``, antialiasing is disabled. Default value: ``true``. - .. c:member:: EM_BOOL premultipliedAlpha + .. c:member:: bool premultipliedAlpha If ``true``, the alpha channel of the rendering context will be treated as representing premultiplied alpha values. If ``false``, the alpha channel represents non-premultiplied alpha. Default value: ``true``. - .. c:member:: EM_BOOL preserveDrawingBuffer + .. c:member:: bool preserveDrawingBuffer If ``true``, the contents of the drawing buffer are preserved between consecutive ``requestAnimationFrame()`` calls. If ``false``, color, depth and stencil are cleared at the beginning of each ``requestAnimationFrame()``. Generally setting this to ``false`` gives better performance. Default value: ``false``. @@ -1971,7 +1956,7 @@ Struct Specifies a hint to the WebGL canvas implementation to how it should choose the use of available GPU resources. One of EM_WEBGL_POWER_PREFERENCE_DEFAULT, EM_WEBGL_POWER_PREFERENCE_LOW_POWER, EM_WEBGL_POWER_PREFERENCE_HIGH_PERFORMANCE. - .. c:member:: EM_BOOL failIfMajorPerformanceCaveat + .. c:member:: bool failIfMajorPerformanceCaveat If ``true``, requests context creation to abort if the browser is only able to create a context that does not give good hardware-accelerated performance. Default value: ``false``. @@ -1986,26 +1971,26 @@ Struct Default value: ``majorVersion=1``, ``minorVersion=0`` - .. c:member:: EM_BOOL enableExtensionsByDefault + .. c:member:: bool enableExtensionsByDefault If ``true``, all GLES2-compatible non-performance-impacting WebGL extensions will automatically be enabled for you after the context has been created. If ``false``, no extensions are enabled by default, and you need to manually call :c:func:`emscripten_webgl_enable_extension` to enable each extension that you want to use. Default value: ``true``. - .. c:member:: EM_BOOL explicitSwapControl + .. c:member:: bool explicitSwapControl By default, when ``explicitSwapControl`` is in its default state ``false``, rendered WebGL content is implicitly presented (displayed to the user) on the canvas when the event handler that renders with WebGL returns back to the browser event loop. If ``explicitSwapControl`` is set to ``true``, rendered content will not be displayed on screen automatically when event handler function finishes, but the control of swapping is given to the user to manage, via the ``emscripten_webgl_commit_frame()`` function. In order to be able to set ``explicitSwapControl==true``, support for it must explicitly be enabled either 1) via adding the ``-sOFFSCREEN_FRAMEBUFFER`` Emscripten linker flag, and enabling ``renderViaOffscreenBackBuffer==1``, or 2) via adding the linker flag ``-sOFFSCREENCANVAS_SUPPORT``, and running in a browser that supports OffscreenCanvas. - .. c:member:: EM_BOOL renderViaOffscreenBackBuffer + .. c:member:: bool renderViaOffscreenBackBuffer If ``true``, an extra intermediate backbuffer (offscreen render target) is allocated to the created WebGL context, and rendering occurs to this backbuffer instead of directly onto the WebGL "default backbuffer". This is required to be enabled if 1) ``explicitSwapControl==true`` and the browser does not support OffscreenCanvas, 2) when performing WebGL rendering in a worker thread and the browser does not support OffscreenCanvas, and 3) when performing WebGL context accesses from multiple threads simultaneously (independent of whether OffscreenCanvas is supported or not). Because supporting offscreen framebuffer adds some amount of extra code to the compiled output, support for it must explicitly be enabled via the ``-sOFFSCREEN_FRAMEBUFFER`` Emscripten linker flag. When building simultaneously with both ``-sOFFSCREEN_FRAMEBUFFER`` and ``-sOFFSCREENCANVAS_SUPPORT`` linker flags enabled, offscreen backbuffer can be used as a polyfill-like compatibility fallback to enable rendering WebGL from a pthread when the browser does not support the OffscreenCanvas API. - .. c:member:: EM_BOOL proxyContextToMainThread + .. c:member:: bool proxyContextToMainThread This member specifies the threading model that will be used for the created WebGL context, when the WebGL context is created in a pthread. Three values are possible: ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW``, ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK`` or ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS``. If ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW`` is specified, the WebGLRenderingContext object will be created inside the pthread that is calling the ``emscripten_webgl_create_context()`` function as an OffscreenCanvas-based rendering context. This is only possible if 1) current browser supports OffscreenCanvas specification, 2) code was compiled with ``-sOFFSCREENCANVAS_SUPPORT`` linker flag enabled, 3) the Canvas object that the context is being created on was transferred over to the calling pthread with function ``emscripten_pthread_attr_settransferredcanvases()`` when the pthread was originally created, and 4) no OffscreenCanvas-based context already exists from the given Canvas at the same time. @@ -2027,14 +2012,14 @@ Callback functions .. code-block:: cpp - typedef EM_BOOL (*em_webgl_context_callback)(int eventType, const void *reserved, void *userData); + typedef bool (*em_webgl_context_callback)(int eventType, const void *reserved, void *userData); :param int eventType: The type of :c:data:`WebGL context event `. :param reserved: Reserved for future use; pass in 0. :type reserved: const void* :param void* userData: The ``userData`` originally passed to the registration function. :returns: |callback-handler-return-value-doc| - :rtype: |EM_BOOL| + :rtype: bool @@ -2042,28 +2027,28 @@ Functions --------- -.. c:function:: EMSCRIPTEN_RESULT emscripten_set_webglcontextlost_callback(const char *target, void *userData, EM_BOOL useCapture, em_webgl_context_callback callback) - EMSCRIPTEN_RESULT emscripten_set_webglcontextrestored_callback(const char *target, void *userData, EM_BOOL useCapture, em_webgl_context_callback callback) +.. c:function:: EMSCRIPTEN_RESULT emscripten_set_webglcontextlost_callback(const char *target, void *userData, bool useCapture, em_webgl_context_callback callback) + EMSCRIPTEN_RESULT emscripten_set_webglcontextrestored_callback(const char *target, void *userData, bool useCapture, em_webgl_context_callback callback) Registers a callback function for the canvas `WebGL context`_ events: ``webglcontextlost`` and ``webglcontextrestored``. :param target: |target-parameter-doc| :type target: const char* :param void* userData: |userData-parameter-doc| - :param EM_BOOL useCapture: |useCapture-parameter-doc| + :param bool useCapture: |useCapture-parameter-doc| :param em_webgl_context_callback callback: |callback-function-parameter-doc| :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| -.. c:function:: EM_BOOL emscripten_is_webgl_context_lost(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context) +.. c:function:: bool emscripten_is_webgl_context_lost(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context) Queries the given WebGL context if it is in a lost context state. :param target: Specifies a handle to the context to test. :type target: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE :returns: ``true`` if the WebGL context is in a lost state (or the context does not exist) - :rtype: |EM_BOOL| + :rtype: bool .. c:function:: void emscripten_webgl_init_context_attributes(EmscriptenWebGLContextAttributes *attributes) @@ -2084,7 +2069,11 @@ Functions .. note:: - A successful call to this function will not immediately make that rendering context active. Call :c:func:`emscripten_webgl_make_context_current` after creating a context to activate it. - - This function will try to initialize the context version that was *exactly* requested. It will not e.g. initialize a newer backwards-compatible version or similar. + - A word of caution about :c:type:`EmscriptenWebGLContextAttributes.majorVersion`: + + - When no (WEBGL) linker flags are set, then this attribute is ignored and the context returned is WebGL 1.0 + - When the linker flag ``-sMIN_WEBGL_VERSION=2`` is set, then this attribute is ignored and the context returned is WebGL 2.0 + - When the linker flag ``-sMAX_WEBGL_VERSION=2`` is set, then this attribute is used and the context returned matches the value of this attribute :param target: The DOM canvas element in which to initialize the WebGL context. :type target: const char* @@ -2148,15 +2137,15 @@ Functions :rtype: |EMSCRIPTEN_RESULT| -.. c:function:: EM_BOOL emscripten_webgl_enable_extension(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context, const char *extension) +.. c:function:: bool emscripten_webgl_enable_extension(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context, const char *extension) Enables the given extension on the given context. :param EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context: The WebGL context on which the extension is to be enabled. :param extension: A string identifying the `WebGL extension `_. For example "OES_texture_float". :type extension: const char* - :returns: EM_TRUE if the given extension is supported by the context, and EM_FALSE if the extension was not available. - :rtype: |EM_BOOL| + :returns: true if the given extension is supported by the context, and false if the extension was not available. + :rtype: bool .. c:function:: EMSCRIPTEN_RESULT emscripten_set_canvas_element_size(const char *target, int width, int height) @@ -2216,7 +2205,6 @@ Functions .. COMMENT (not rendered): The replace function return values with links (not created automatically) .. |EMSCRIPTEN_RESULT| replace:: :c:type:`EMSCRIPTEN_RESULT` -.. |EM_BOOL| replace:: :c:type:`EM_BOOL` .. |EMSCRIPTEN_WEBGL_CONTEXT_HANDLE| replace:: :c:type:`EMSCRIPTEN_WEBGL_CONTEXT_HANDLE` @@ -2259,10 +2247,10 @@ Functions :param setTimeoutId: An ID returned by function :c:func:`emscripten_set_timeout()`. -.. c:function:: void emscripten_set_timeout_loop(EM_BOOL (*cb)(double time, void *userData), double intervalMsecs, void *userData) +.. c:function:: void emscripten_set_timeout_loop(bool (*cb)(double time, void *userData), double intervalMsecs, void *userData) Initializes a ``setTimeout()`` loop on the given function on the calling thread. The specified callback - function 'cb' needs to keep returning ``EM_TRUE`` as long as the animation loop should continue to run. + function 'cb' needs to keep returning ``true`` as long as the animation loop should continue to run. When the function returns false, the ``setTimeout()`` loop will stop. Note: The loop will start immediately with a 0 msecs delay - the passed in intervalMsecs time specifies the interval that the consecutive callback calls should fire at. @@ -2272,7 +2260,7 @@ Functions :param userData: Specifies a pointer sized field of custom data that will be passed in to the callback function. -.. c:function:: long emscripten_request_animation_frame(EM_BOOL (*cb)(double time, void *userData), void *userData) +.. c:function:: long emscripten_request_animation_frame(bool (*cb)(double time, void *userData), void *userData) Performs a single ``requestAnimationFrame()`` callback call on the given function on the calling thread. @@ -2293,10 +2281,10 @@ Functions :param requestAnimationFrameId: An ID returned by function :c:func:`emscripten_request_animation_frame()`. -.. c:function:: void emscripten_request_animation_frame_loop(EM_BOOL (*cb)(double time, void *userData), void *userData) +.. c:function:: void emscripten_request_animation_frame_loop(bool (*cb)(double time, void *userData), void *userData) Initializes a ``requestAnimationFrame()`` loop on the given function on the calling thread. The specified - callback function 'cb' needs to keep returning ``EM_TRUE`` as long as the animation loop should continue + callback function 'cb' needs to keep returning ``true`` as long as the animation loop should continue to run. When the function returns false, the animation frame loop will stop. :param cb: The callback function to call. This function will receive the current high precision timer value @@ -2324,10 +2312,10 @@ Functions :param setImmediateId: An ID returned by function :c:func:`emscripten_set_immediate()`. -.. c:function:: void emscripten_set_immediate_loop(EM_BOOL (*cb)(void *userData), void *userData) +.. c:function:: void emscripten_set_immediate_loop(bool (*cb)(void *userData), void *userData) Initializes a ``setImmediate()`` loop on the given function on the calling thread. The specified callback - function 'cb' needs to keep returning ``EM_TRUE`` as long as the loop should continue to run. + function 'cb' needs to keep returning ``true`` as long as the loop should continue to run. When the function returns false, the ``setImmediate()`` loop will stop. TODO: Currently the polyfill of ``setImmediate()`` only works in the main browser thread, but not in pthreads. diff --git a/site/source/docs/api_reference/preamble.js.rst b/site/source/docs/api_reference/preamble.js.rst index f81065f8863d0..c2bd9829f95f5 100644 --- a/site/source/docs/api_reference/preamble.js.rst +++ b/site/source/docs/api_reference/preamble.js.rst @@ -159,12 +159,13 @@ Accessing memory Conversion functions — strings, pointers and arrays =================================================== -.. js:function:: UTF8ToString(ptr[, maxBytesToRead]) +.. js:function:: UTF8ToString(ptr[, maxBytesToRead], [ignoreNul]) Given a pointer ``ptr`` to a null-terminated UTF8-encoded string in the Emscripten HEAP, returns a copy of that string as a JavaScript ``String`` object. :param ptr: A pointer to a null-terminated UTF8-encoded string in the Emscripten HEAP. - :param maxBytesToRead: An optional length that specifies the maximum number of bytes to read. You can omit this parameter to scan the string until the first \0 byte. If maxBytesToRead is passed, and the string at ``[ptr, ptr+maxBytesToReadr)`` contains a null byte in the middle, then the string will cut short at that byte index (i.e. maxBytesToRead will not produce a string of exact length ``[ptr, ptr+maxBytesToRead)``) N.B. mixing frequent uses of ``UTF8ToString()`` with and without maxBytesToRead may throw JS JIT optimizations off, so it is worth to consider consistently using one style or the other. + :param 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. N.B. mixing frequent uses of this function with and without maxBytesToRead may throw JS JIT optimizations off, so it is worth to consider consistently using one style or the other. + :param ignoreNul: If ``true``, the function will not stop on a NUL character, but will read the entire string up to ``maxBytesToRead``. :returns: A JavaScript ``String`` object @@ -180,11 +181,13 @@ Conversion functions — strings, pointers and arrays :param maxBytesToWrite: A limit on the number of bytes that this function can at most write out. If the string is longer than this, the output is truncated. The outputted string will always be null terminated, even if truncation occurred, as long as ``maxBytesToWrite > 0``. -.. js:function:: UTF16ToString(ptr) +.. js:function:: UTF16ToString(ptr[, maxBytesToRead], [ignoreNul]) Given a pointer ``ptr`` to a null-terminated UTF16LE-encoded string in the Emscripten HEAP, returns a copy of that string as a JavaScript ``String`` object. :param ptr: A pointer to a null-terminated UTF16LE-encoded string in the Emscripten HEAP. + :param 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. N.B. mixing frequent uses of this function with and without maxBytesToRead may throw JS JIT optimizations off, so it is worth to consider consistently using one style or the other. + :param ignoreNul: If ``true``, the function will not stop on a NUL character, but will read the entire string up to ``maxBytesToRead``. :returns: A JavaScript ``String`` object @@ -202,11 +205,13 @@ Conversion functions — strings, pointers and arrays -.. js:function:: UTF32ToString(ptr) +.. js:function:: UTF32ToString(ptr[, maxBytesToRead], [ignoreNul]) Given a pointer ``ptr`` to a null-terminated UTF32LE-encoded string in the Emscripten HEAP, returns a copy of that string as a JavaScript ``String`` object. :param ptr: A pointer to a null-terminated UTF32LE-encoded string in the Emscripten HEAP. + :param 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. N.B. mixing frequent uses of this function with and without maxBytesToRead may throw JS JIT optimizations off, so it is worth to consider consistently using one style or the other. + :param ignoreNul: If ``true``, the function will not stop on a NUL character, but will read the entire string up to ``maxBytesToRead``. :returns: A JavaScript ``String`` object. @@ -389,8 +394,6 @@ The :ref:`emscripten-memory-model` uses a typed array buffer (``ArrayBuffer``) t function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) function SAFE_FT_MASK(value, mask) function CHECK_OVERFLOW(value, bits, ignore, sig) - Module["preloadedImages"] - Module["preloadedAudios"] .. PRIVATE NOTES (not rendered) : diff --git a/site/source/docs/api_reference/wasm_audio_worklets.rst b/site/source/docs/api_reference/wasm_audio_worklets.rst index e2587bf58b372..95a20f0cae53c 100644 --- a/site/source/docs/api_reference/wasm_audio_worklets.rst +++ b/site/source/docs/api_reference/wasm_audio_worklets.rst @@ -45,7 +45,9 @@ processing graph as AudioWorkletNodes. Once a class type is instantiated on the Web Audio graph and the graph is running, a C/C++ function pointer callback will be invoked for each 128 -samples of the processed audio stream that flows through the node. +samples of the processed audio stream that flows through the node. Newer Web +Audio API specs allow this to be changed, so for future compatibility use the +``AudioSampleFrame``'s ``samplesPerChannel`` to get the value. This callback will be executed on a dedicated separate audio processing thread with real-time processing priority. Each Web Audio context will @@ -95,7 +97,7 @@ own noise generator AudioWorkletProcessor node type: .. code-block:: cpp - void AudioThreadInitialized(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void *userData) + void AudioThreadInitialized(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData) { if (!success) return; // Check browser console in a debug build for detailed errors WebAudioWorkletProcessorCreateOptions opts = { @@ -110,7 +112,7 @@ which resumes the audio context when the user clicks on the DOM Canvas element t .. code-block:: cpp - void AudioWorkletProcessorCreated(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void *userData) + void AudioWorkletProcessorCreated(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData) { if (!success) return; // Check browser console in a debug build for detailed errors @@ -126,8 +128,7 @@ which resumes the audio context when the user clicks on the DOM Canvas element t "noise-generator", &options, &GenerateNoise, 0); // Connect it to audio context destination - EM_ASM({emscriptenGetAudioObject($0).connect(emscriptenGetAudioObject($1).destination)}, - wasmAudioWorklet, audioContext); + emscripten_audio_node_connect(wasmAudioWorklet, audioContext, 0, 0); // Resume context on mouse click emscripten_set_click_callback("canvas", (void*)audioContext, 0, OnCanvasClick); @@ -137,13 +138,13 @@ which resumes the audio context when the user clicks on the DOM Canvas element t .. code-block:: cpp - EM_BOOL OnCanvasClick(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) + bool OnCanvasClick(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) { EMSCRIPTEN_WEBAUDIO_T audioContext = (EMSCRIPTEN_WEBAUDIO_T)userData; if (emscripten_audio_context_state(audioContext) != AUDIO_CONTEXT_STATE_RUNNING) { emscripten_resume_audio_context_sync(audioContext); } - return EM_FALSE; + return false; } 5. Finally we can implement the audio callback that is to generate the noise: @@ -152,19 +153,19 @@ which resumes the audio context when the user clicks on the DOM Canvas element t #include - EM_BOOL GenerateNoise(int numInputs, const AudioSampleFrame *inputs, + bool GenerateNoise(int numInputs, const AudioSampleFrame *inputs, int numOutputs, AudioSampleFrame *outputs, int numParams, const AudioParamFrame *params, void *userData) { for(int i = 0; i < numOutputs; ++i) - for(int j = 0; j < 128*outputs[i].numberOfChannels; ++j) + for(int j = 0; j < outputs[i].samplesPerChannel*outputs[i].numberOfChannels; ++j) outputs[i].data[j] = emscripten_random() * 0.2 - 0.1; // Warning: scale down audio volume by factor of 0.2, raw noise can be really loud otherwise - return EM_TRUE; // Keep the graph output going + return true; // Keep the graph output going } -And that's it! Compile the code with the linker flags ``-sAUDIO_WORKLET=1 -sWASM_WORKERS=1`` to enable targeting AudioWorklets. +And that's it! Compile the code with the linker flags ``-sAUDIO_WORKLET -sWASM_WORKERS`` to enable targeting AudioWorklets. Synchronizing audio thread with the main thread =============================================== diff --git a/site/source/docs/api_reference/wasm_workers.rst b/site/source/docs/api_reference/wasm_workers.rst index c99c363dcf80d..bea9afbeba3a0 100644 --- a/site/source/docs/api_reference/wasm_workers.rst +++ b/site/source/docs/api_reference/wasm_workers.rst @@ -153,7 +153,7 @@ a Wasm Worker, consider which type of hierarchy you would like, and if necessary hierarchy manually by posting the Worker creation over to the main thread yourself. Note that support for nested Workers varies across browsers. As of 02/2022, nested Workers are `not -supported in Safari `_. See `here +supported in Safari `_. See `here `_ for a polyfill. Pthreads can use the Wasm Worker synchronization API, but not vice versa @@ -266,7 +266,7 @@ table. Thread stack Specify in pthread_attr_t structure. Manage thread stack area explicitly with
emscripten_create_wasm_worker_*_tls()
functions, or -
automatically allocate stack with
emscripten_malloc_wasm_worker()
API. +
automatically allocate stack+TLS area with
emscripten_malloc_wasm_worker()
API. Thread Local Storage (TLS) Supported transparently. @@ -342,6 +342,53 @@ table. +Wasm Workers stack size considerations +====================================== + +When instantiating a Wasm Worker, one has to create a memory array for the LLVM +data stack for the created Worker. This data stack will generally consist only +of local variables that have been "spilled" by LLVM into memory, e.g. to contain +large arrays, structs, or other variables that are referenced by a memory +address. This stack will not contain control flow information. + +Since WebAssembly does not support virtual memory, the size of the LLVM data +stack that is defined both for Wasm Workers but also the main thread will not be +possible to grow at runtime. So if the Worker (or the main thread) runs out of +stack space, the program behavior will be undefined. Use the Emscripten linker +flag -sSTACK_OVERFLOW_CHECK=2 to emit runtime stack overflow checks into the +program code to detect these situations during development. + +Note that to avoid the need to perform two separate allocations, the TLS memory +for the Wasm Worker will be located at the bottom end (low memory address) of +the Wasm Worker stack space. + +Wasm Workers vs the earlier Emscripten Worker API +================================================= + +Emscripten provides a second Worker API as part of the emscripten.h header. This Worker API predates the advent of SharedArrayBuffer, and is quite distinct from Wasm Workers API, just the naming of these two APIs is similar due to historical reasons. + +Both APIs allow one to spawn Web Workers from the main thread, though the semantics are different. + +With the Worker API, the user will be able to spawn a Web Worker from a custom URL. This URL can point to a completely separate JS file that was not compiled with Emscripten, to load up Workers from arbitrary URLs. With Wasm Workers, a custom URL is not specified: Wasm Workers will always spawn a Web Worker that computes in the same WebAssembly+JavaScript context as the main program. + +The Worker API does not integrate with SharedArrayBuffer, so interaction with the loaded Worker will always be asynchronous. Wasm Workers howerer is built on top of SharedArrayBuffer, and each Wasm Worker shares and computes in the same WebAssembly Memory address space of the main thread. + +Both the Worker API and Wasm Workers API provide the user with ability to postMessage() function calls to the Worker. In Worker API, this message posting is restricted to need to originate/initiate from the main thread towards the Worker (using the API ``emscripten_call_worker()`` and ``emscripten_worker_respond()`` in ````). With Wasm Workers however one can also postMessage() function calls to their parent (owning) thread. + +If posting function calls with the Emscripten Worker API, it is required that the target Worker URL points to an Emscripten compiled program (so it has the ``Module`` structure to locate function names). Only functions that have been exported to the ``Module`` object are callable. With Wasm Workers, any C/C++ function may be posted, and does not need to be exported. + +Use the Emscripten Worker API when: + - you want to easily spawn a Worker from a JS file that was not built using Emscripten + - you want to spawn as Worker a single separate compiled program than the main thread program represents, and the main thread and Worker programs do not share common code + - you do not want to require the use of SharedArrayBuffer, or setting up COOP+COEP headers + - you only need to communicate with the Worker asynchronously using postMessage() function calls + +Use the Wasm Workers API when: + - you want to create one or more new threads that synchronously compute in the same Wasm Module context + - you want to spawn multiple Workers from the same codebase and save memory by sharing the WebAssembly Module (object code) and Memory (address space) across the Workers + - you want to synchronously coordinate communication between threads by using atomic primitives and locks + - your web server has been configured with the needed COOP+COEP headers to enable SharedArrayBuffer capabilities on the site + Limitations =========== @@ -355,4 +402,4 @@ The following build options are not supported at the moment with Wasm Workers: Example Code ============ -See the directory ``test/wasm_workers/`` for code examples on different Wasm Workers API functionality. +See the directory ``test/wasm_worker/`` for code examples on different Wasm Workers API functionality. diff --git a/site/source/docs/building_from_source/index.rst b/site/source/docs/building_from_source/index.rst index d42e17bd5492d..af98f2a9cdfee 100644 --- a/site/source/docs/building_from_source/index.rst +++ b/site/source/docs/building_from_source/index.rst @@ -13,6 +13,11 @@ before it can be used (e.g. ``npm install``). The ``bootstrap`` script in the top level of the repository takes care of running these steps and ``emcc`` will error out if it detects that ``bootstrap`` needs to be run. +Emscripten comes with its own versions some C/C++ system libaries which ``emcc`` +builds automatically as and when needed (in the emsdk builds, these are precompiled). +You can also build them manually with the ``embuilder`` tool - see ``embuilder --help`` +for more information. + In addition to the main emscripten repository you will also need to checkout and build LLVM and Binaryen (as detailed below). After compiling these, you will need to edit your ``.emscripten`` file to point to their corresponding diff --git a/site/source/docs/building_from_source/toolchain_what_is_needed.rst b/site/source/docs/building_from_source/toolchain_what_is_needed.rst index a6246732e80e6..d81407cb99487 100644 --- a/site/source/docs/building_from_source/toolchain_what_is_needed.rst +++ b/site/source/docs/building_from_source/toolchain_what_is_needed.rst @@ -22,8 +22,8 @@ Emscripten tools and dependencies In general a complete Emscripten environment requires the following tools. First test to see if they are already installed using the :ref:`instructions below `. - - :term:`Node.js` (0.8 or above; 0.10.17 or above to run websocket-using servers in node) - - :term:`Python` (3.6 or above) + - :term:`Node.js` (18.3.0 or above) + - :term:`Python` (3.8 or above) - :term:`Java` (1.6.0_31 or later). Java is optional. It can be used to run the java version of term:`Closure Compiler`. - :term:`Git` client. Git is required if building tools from source. - :term:`LLVM` (LLVM, including clang and wasm-ld) diff --git a/site/source/docs/compiling/Contrib-Ports.rst b/site/source/docs/compiling/Contrib-Ports.rst index e387428c5c774..8934e2e748616 100644 --- a/site/source/docs/compiling/Contrib-Ports.rst +++ b/site/source/docs/compiling/Contrib-Ports.rst @@ -17,17 +17,53 @@ available in emscripten. In order to use a contrib port you use the contrib.glfw3 ============= -This project is an emscripten port of GLFW written in C++ for the +This project is an Emscripten port of GLFW written in C++ for the web/webassembly platform. .. note:: - emscripten includes support for both GLFW 2 and 3 written in Javascript. + Emscripten includes support for both GLFW 2 and 3 written in Javascript. These can be activated with the :ref:`settings ` ``-sUSE_GLFW=2`` and ``-sUSE_GLFW=3``. This non-official contribution, written in C++, provides a more extensive and up-to-date implementation of the GLFW 3 API than the built-in port. It is enabled with the option ``--use-port=contrib.glfw3``. -`Project information `_ +`Project information `__ -License: Apache 2.0 license \ No newline at end of file +License: Apache 2.0 license + +.. _contrib.lua: + +contrib.lua +=========== + +Lua is a powerful, efficient, lightweight, embeddable scripting language. + +Example usage: + +.. code-block:: text + + // main.c + #include + #include + #include + #include + + int main() { + lua_State *L = luaL_newstate(); + luaL_openlibs(L); + if (luaL_dostring(L, "print('hello world')") != LUA_OK) { + printf("Error running Lua code %s\n", lua_tostring(L, -1)); + lua_pop(L, 1); + } + lua_close(L); + return 0; + } + + // compile with + emcc --use-port=contrib.lua main.c -o /tmp/index.html + + +`Project information `__ + +License: MIT License \ No newline at end of file diff --git a/site/source/docs/compiling/Dynamic-Linking.rst b/site/source/docs/compiling/Dynamic-Linking.rst index 7ef9893e35bb3..9760eea94e525 100644 --- a/site/source/docs/compiling/Dynamic-Linking.rst +++ b/site/source/docs/compiling/Dynamic-Linking.rst @@ -231,7 +231,7 @@ added and these changes need to be mirrored on every thread in the process. Changes to the table are protected by a mutex, and before any thread returns from ``dlopen`` or ``dlsym`` it will wait until all other threads are sync. In order to make this synchronization as seamless as possible, we hook into the -low level primitives of `emscripten_futex_wait` and `emscirpten_yield`. +low level primitives of `emscripten_futex_wait` and `emscripten_yield`. For most use cases all this happens under hood and no special action is needed. However, there there is one class of application that currently may require diff --git a/site/source/docs/compiling/Modularized-Output.rst b/site/source/docs/compiling/Modularized-Output.rst new file mode 100644 index 0000000000000..a4a2bc7b5ad8d --- /dev/null +++ b/site/source/docs/compiling/Modularized-Output.rst @@ -0,0 +1,176 @@ +.. _Modularized-Output: + +================== +Modularized Output +================== + +By default, emscripten outputs regular JS scripts which can be loaded on the Web +via a ``script`` tag, or on the command line using Node or other JS runtime. + +In this default configuration the generated :ref:`module` variable (and indeed +all of the emscripten internal symbols) are added to the global scope. This +means that only one emscripten-built program can be loaded in a given scope +(since multiple modules would clobber each other's state). + +The :ref:`modularize` setting (along with other related settings) can be used to +isolate the generated module within its own private scope and/or allow multiple +instances of the same module to be created. + +This section covers the various modularization options available and how to use +them. + + +Using -sMODULARIZE +================== + +The :ref:`modularize` setting has two primary functions. Firstly, it +puts all of the generated code inside its own scope so that the global namespace +is not polluted when the JS is loaded (for example, via ``script`` tag). +Secondly, it allows multiple instances of the module to be created. + +The generated factory function is async and it returns a promise of an instance +of the program. It can be called any number of times to create multiple +separate and isolated instances of the same program. + +By default, the factory function is called ``Module``, but it can be given a +unique name via the ``-sEXPORT_NAME`` setting. + +For example, a program is built using ``-sMODULARIZE -sEXPORT_NAME=Foo`` can +be instantiated using: + +:: + + var myFoo = await Foo({ /* optional params */ }); + myFoo._nativeMethod(); + + +or: + +:: + + Foo({ /* optional params */ }).then((myFoo) => { + myFoo._nativeMethod(); + } + +If node is included in the ``-sENVIRONMENT`` setting then the generated module +also acts as a ``CommonJS`` module under node. + +Because there is no global ``Module`` object in this case any parameters to the +module creation are passed via the optional parameter to the factory function. +For example, if you wanted to use a custom ``print`` method you could write: + +:: + + var myFoo = await Foo({ print: myPrint }); + + +Generating ES6 Modules +====================== + +The :ref:`EXPORT_ES6` setting can be used to instead output JavaScript in the +form of an ES module. (The ES module format did not exist at time when the +`MODULARIZE` setting was first created otherwise this would likely be the +default). + +Using an output filename that ends in `.mjs` will automatically enable this +setting. + +In this mode the module has a single default export which is the factory +function for creating new instances of the module. For example: + +:: + + import Foo from './emcc-output.js'; + var myFoo = await Foo({ print: myPrint }); + +The name ``Foo`` here can be whatever you choose and will always be assigned to +the default export of the module. + + +Using -sMODULARIZE=instance (experimental) +========================================== + +Emscripten has experimental support for performing only the encapsulation part of +modularization, and not the ability to create multiple instances. In this +mode a module is created that exports a single instance of the program rather +than a factory function. + +This setting implicitly enables :ref:`export_es6` and will not work when +:ref:`export_es6` is explictly disabled. + +In this mode the default export of the module is an initializer function which +allows input parameters to be passed to the instance. Other elements normally +found on the module object are instead exported directly. For example: + +:: + + // Import the default init function and a named native method + import init, { _nativeMethod } from './emcc-output.js'; + await init({ print: myPrint }); + _nativeMethod(); + +Limitations +----------- + +Some major features still do not work in this mode. Many of these we hope to +fix in future releses. Current limitations include: + +* Internal usage (e.g. usage within EM_JS / JS libary code) of the ``Module`` + global does not work. This is because symbols are exported directly using + ES6 module syntax rathar than using the ``Module`` global. + +* The ``wasmExports`` internal global does not exist. + +* `ccall`/`cwrap` are not supported (depends on the ``Module`` global). + +* :ref:`minimal_runtime` is not supported. + +* The output of file_packager is not compatible so :ref:`emcc-preload-file` and + :ref:`emcc-embed-file` do not work. + + +Source Phase Imports (experimental) +=================================== + +`Source phase imports`_ is a JavaScript proposal that adds support for importing +Wasm modules via ES import statements. This allows emscripten to elide some of +the auto-generated code for finding and fetching the Wasm binary. + +See :ref:`source_phase_imports`. + +This setting implicitly enables :ref:`export_es6` and will not work when +:ref:`export_es6` is explictly disabled. + + +ES Module Integration (experimental) +==================================== + +`Wasm ESM integration`_ is a WebAssembly proposal that allows Wasm instances to +be imported directly as ES modules. This allows emscripten to elide a lot of +boilerplate code for linking up Wasm and JavaScript. + +See :ref:`wasm_esm_integration`. + +Limitations +----------- + +This setting implicitly enables :ref:`export_es6` and sets :ref:`MODULARIZE` to +``instance``. Because of this all the same limitations mentioned above for +``-sMODULARIZE=intance`` apply. + +Some additional limitations are: + +* :ref:`wasm_workers` is not yet supported. + +* :ref:`abort_on_wasm_exceptions` is not supported (requires wrapping wasm + exports). + +* :ref:`asyncify` is not supported (depends on ``wasmExports`` global) + +* Setting :ref:`wasm` to ``0`` is not supported. + +* Setting :ref:`wasm_async_compilation` to ``0`` is not supported. + + +.. _Source phase imports: https://github.com/tc39/proposal-source-phase-imports +.. _Wasm ESM integration: https://github.com/WebAssembly/esm-integration diff --git a/site/source/docs/compiling/WebAssembly.rst b/site/source/docs/compiling/WebAssembly.rst index 3bb28b2f2f853..8e6b3953be8a3 100644 --- a/site/source/docs/compiling/WebAssembly.rst +++ b/site/source/docs/compiling/WebAssembly.rst @@ -92,14 +92,57 @@ When using ``emcc`` to build to WebAssembly, you will see a ``.wasm`` file conta You may also see additional files generated, like a ``.data`` file if you are preloading files into the virtual filesystem. All that is exactly the same as when building to asm.js. One difference you may notice is the lack of a ``.mem file``, which for asm.js contains the static memory initialization data, which in WebAssembly we can pack more efficiently into the WebAssembly binary itself. -WebAssembly support in browsers -=============================== -WebAssembly is supported by all major browsers going back to Firefox 52, Chrome -57, Safari 11 and Opera 44. +WebAssembly feature extensions +============================== + +Since its original launch, WebAssembly has been expanded with various feature +extensions, which have been implmented in browsers. A list of features +(including already-shipped and in-progress) and details about browser versions +that support them can be found on +`webassembly.org `_. + +Several of these features can be used by Emscripten (or are by default) and can +be enabled or disabled individually (using either Clang or emscripten flags) +or by selecting which version of browsers Emscripten should target. + +Examples: + +* Exception handling (see :ref:`exceptions` for details). +* SIMD (see :ref:`Porting SIMD code` for details). +* Nontrapping float-to-int conversion (enabled by default, use + ``-mno-nontrapping-fptoint`` to disable). + Clang will generate nontrapping (saturating) float-to-int conversion instructions for + C typecasts. This should have no effect on programs that do not have + undefined behavior but if the casted floating-point value is outside the range + of the target integer type, the result will be a number of the max or min value + instead of a trap. This also results in a small code size improvement because + of details of the LLVM IR semantics. +* Bulk memory operations (enabled by default, use + ``-mno-bulk-memory-opt -mno-bulk-memory`` to disable). ``memory.copy`` + and ``memory.fill`` instructions are used in the implementation of C + ``memcpy`` and ``memset``, and Clang may generate them elsewhere. +* JS BigInt integration (enabled by default, use the + ``-sWASM_BIGINT=0`` :ref:`setting ` + to disable). This has the effect that Wasm i64 values are passed and returned + between Wasm and JS as BigInt values rather than being split by Binaryen into + pairs of Numbers. +* Sign-extension operators (enabled by default, use ``-mno-sign-ext`` to disable). + +For the features that are enabled by default (or will be when sufficient +browser support exists), it's also possible to control them by specifying +which browser versions you want to target. You can use the +``-sMIN_FIREFOX_VERSION`` :ref:`setting ` +(and also ``MIN_CHROME_VERSION``, ``MIN_SAFARI_VERSION`` and +``MIN_NODE_VERSION``). Setting a value lower than the default version will +disable features not supported by the specified version. Some features (e.g. +Exception handling and threads) are not enabled by default because they have +tradeoffs (e.g. binary size costs or restrictions on how the resulting wasm +can be used such as COEP headers). These are not controlled by the browser +version flags and must be enabled explicitly. +See the :ref:`settings ` page for details of the default +browser versions Emscripten targets. -For further info on WebAssembly features supported in various browsers, see the -`WebAssembly Roadmap `_ ``.wasm`` files and compilation =============================== diff --git a/site/source/docs/compiling/index.rst b/site/source/docs/compiling/index.rst index 5ab6dc48fce45..e8b03becc832d 100644 --- a/site/source/docs/compiling/index.rst +++ b/site/source/docs/compiling/index.rst @@ -9,6 +9,7 @@ This section contains topics about building projects and running the output. - :ref:`Building-Projects` shows how to use :ref:`emccdoc` as a drop-in replacement for *gcc* in your existing project. - :ref:`WebAssembly` explains how Emscripten can be used to build WebAssembly files - :ref:`Running-html-files-with-emrun` explains how to use *emrun* to run generated HTML pages in a locally launched web server. +- :ref:`Modularized-Output` covers the various options for generating modularized JS code. - :ref:`Deploying-Pages` covers topics related to hosting Emscripten compiled web pages on a CDN. - :ref:`GitLab` explains how to build and test projects on GitLab. - :ref:`Contrib-Ports` contains information about contrib ports. @@ -19,6 +20,7 @@ This section contains topics about building projects and running the output. Building-Projects WebAssembly + Modularized-Output Dynamic-Linking Running-html-files-with-emrun Deploying-Pages diff --git a/site/source/docs/contributing/developers_guide.rst b/site/source/docs/contributing/developers_guide.rst index 9753ecb762ad4..7fe05e8c6744f 100644 --- a/site/source/docs/contributing/developers_guide.rst +++ b/site/source/docs/contributing/developers_guide.rst @@ -79,10 +79,10 @@ The :ref:`Emscripten Compiler Frontend (emcc) ` is a python script that - **emcc** calls :term:`Clang` to compile C++ and ``wasm-ld`` to link it. It builds and integrates with the Emscripten system libraries, both the compiled ones and the ones implemented in JS. -- **emcc** then calls `emscripten.py `_ +- **emcc** then calls `emscripten.py `_ which performs the final transformation to Wasm (including invoking **wasm-emscripten-finalize** from Binaryen) and calls the JS compiler - (see ``src/compiler.js`` and related files) which emits the JS. + (see ``tools/compiler.mjs`` and related files) which emits the JS. - If optimizing Wasm, **emcc** will then call **wasm-opt**, run meta-dce, and other useful things. It will also run JS optimizations on the JS that is emitted alongside the Wasm. diff --git a/site/source/docs/getting_started/FAQ.rst b/site/source/docs/getting_started/FAQ.rst index be6b48b437a1f..1d2653a2bc98f 100644 --- a/site/source/docs/getting_started/FAQ.rst +++ b/site/source/docs/getting_started/FAQ.rst @@ -18,7 +18,7 @@ Why do I get errors building basic code and the tests? All the tests in the :ref:`Emscripten test suite ` are known to build and pass on our test infrastructure, so if you see failures -locally it is likely that there is some problem with your environment. (Rarely, +locally, it is likely that there is some problem with your environment. (Rarely, there may be temporary breakage, but never on a tagged release version.) First call ``emcc --check``, which runs basic sanity checks and prints out @@ -105,13 +105,13 @@ compilation time). Why is my compiled code big? ============================ -Make sure you build with ``-O3`` or ``-Os`` so code is fully optimized and +Make sure you build with ``-O3`` or ``-Os``, so the code is fully optimized and minified. You should use the closure compiler, gzip compression on your webserver, etc., see the :ref:`section on code size in Optimizing code `. -Why does compiling code that works on another machine gives me errors? +Why does compiling code that works on another machine give me errors? ====================================================================== Make sure you are using the Emscripten bundled system headers. Using :ref:`emcc @@ -207,11 +207,11 @@ JavaScript that does not complete and return control to the browser. Graphical C++ apps typically have an infinite main loop in which event handling, processing and rendering is done, followed by a delay to keep the frame-rate right (``SDL_DELAY`` in :term:`SDL` apps). As the main loop does not complete -(is infinite) it cannot return control to the browser, and the app will hang. +(is infinite), it cannot return control to the browser, and the app will hang. Apps that use an infinite main loop should be re-coded to put the actions for a single iteration of the loop into a single "finite" function. In the native -build this function can be run in an infinite loop as before. In the Emscripten +build, this function can be run in an infinite loop as before. In the Emscripten build it is set as the :ref:`main loop function ` and will be called by the browser at a specified frequency. @@ -360,23 +360,16 @@ The crucial thing is that ``Module`` exists, and has the property ``onRuntimeInitialized``, before the script containing emscripten output (``my_project.js`` in this example) is loaded. -Another option is to use the ``MODULARIZE`` option, using ``-sMODULARIZE``. -That puts all of the generated JavaScript into a factory function, which you can -call to create an instance of your module. The factory function returns a -Promise that resolves with the module instance. The promise is resolved once -it's safe to call the compiled code, i.e. after the compiled code has been -downloaded and instantiated. For example, if you build with ``-sMODULARIZE -s -'EXPORT_NAME="createMyModule"'``, then you can do this: +When using the ``MODULARIZE`` is it sufficient to await the returned promise +from the factory function. For example: :: - createMyModule(/* optional default settings */).then(function(Module) { + createMyModule(/* optional default settings */).then((myModule) => { // this is reached when everything is ready, and you can call methods on Module }); -Note that in ``MODULARIZE`` mode we do not look for a global Module object for -default values. Default values must be passed as a parameter to the factory -function. (see details in settings.js) +See :ref:`Modularized-Output` for more this. .. _faq-NO_EXIT_RUNTIME: diff --git a/site/source/docs/getting_started/Tutorial.rst b/site/source/docs/getting_started/Tutorial.rst index 91258ca9fcd8c..256bab304cbdf 100644 --- a/site/source/docs/getting_started/Tutorial.rst +++ b/site/source/docs/getting_started/Tutorial.rst @@ -28,10 +28,10 @@ using ``./emcc`` or ``./em++``. For the next section you will need to open a command prompt: - On Linux or macOS, open a *Terminal*. -- On Windows open the :ref:`Emscripten Command Prompt `, a command +- On Windows, open the :ref:`Emscripten Command Prompt `, a command prompt that has been pre-configured with the correct system paths and settings to point to the :term:`active ` Emscripten tools. To access - this prompt, type **Emscripten** in the Windows 8 start screen, and then + this prompt, type **Emscripten** in the Windows start menu, and then select the **Emscripten Command Prompt** option. Navigate with the command prompt to the emscripten directory under the SDK. This @@ -39,7 +39,7 @@ is a folder below the :term:`emsdk root directory`, typically **/upstream/emscripten/**. The examples below will depend on finding files relative to that location. -.. note:: In older emscripten versions the directory structure was different: +.. note:: In older emscripten versions, the directory structure was different: the version number appeared, and the backend (fastcomp/upstream) did not, so you would use something like **/emscripten/1.20.0/**. @@ -52,7 +52,7 @@ If you haven't run Emscripten before, run it now with: :: ./emcc -v If the output contains warnings about missing tools, see -:ref:`verifying-the-emscripten-environment` for debugging help. Otherwise +:ref:`verifying-the-emscripten-environment` for debugging help. Otherwise, continue to the next sections where we'll build some code. @@ -61,7 +61,7 @@ Running Emscripten You can now compile your first C/C++ file to JavaScript. -First, lets have a look at the file to be compiled: **hello_world.c**. This is +First, let's have a look at the file to be compiled: **hello_world.c**. This is the simplest test code in the SDK, and as you can see, all it does is print "hello, world!" to the console and then exit. @@ -86,15 +86,6 @@ execute it. You can run them using :term:`node.js`: This prints "hello, world!" to the console, as expected. -.. note:: Older node.js versions do not have WebAssembly support yet. In that - case you will see an error message suggesting that you build with - ``-sWASM=0`` to disable WebAssembly, and then emscripten will emit the compiled - code as JavaScript. In general, WebAssembly is recommended as it has - widespread browser support and is more efficient both to execute and to - download (and therefore emscripten emits it by default), but sometimes you - may need your code to run in an environment where it is not yet present and - so should disable it. - .. tip:: If an error occurs when calling *emcc*, run it with the ``-v`` option to print out a lot of useful debug information. @@ -116,11 +107,11 @@ file as the target file: :: You can now open ``hello.html`` in a web browser. -.. note:: Unfortunately several browsers (including *Chrome*, *Safari*, and - *Internet Explorer*) do not support ``file://`` :term:`XHR` requests, and - can't load extra files needed by the HTML (like a ``.wasm`` file, or packaged - file data as mentioned lower down). For these browsers you'll need to serve - the files using a :ref:`local webserver ` and then open +.. note:: Unfortunately, several browsers (including *Chrome* and *Safari*) do + not support ``file://`` :term:`XHR` requests, and can't load extra files + needed by the HTML (like a ``.wasm`` file, or packaged file data as mentioned + lower down). For these browsers, you'll need to serve the files using a + :ref:`local webserver ` and then open ``http://localhost:8000/hello.html``). Once you have the HTML loaded in your browser, you'll see a text area for diff --git a/site/source/docs/getting_started/downloads.rst b/site/source/docs/getting_started/downloads.rst index aee0d2308e0a2..3ef3796a2e2aa 100644 --- a/site/source/docs/getting_started/downloads.rst +++ b/site/source/docs/getting_started/downloads.rst @@ -8,7 +8,7 @@ Download and install ` if you prefer that to downloading binaries using the emsdk. -.. tip:: if you'd like to install emscripten using the **unofficial** packages +.. tip:: If you'd like to install emscripten using the **unofficial** packages instead of the **officially supported** emsdk, see the bottom of the page. .. _sdk-installation-instructions: @@ -16,12 +16,12 @@ Download and install Installation instructions using the emsdk (recommended) ======================================================= -First check the :ref:`Platform-specific notes +First, check the :ref:`Platform-specific notes ` below and install any prerequisites. The core Emscripten SDK (emsdk) driver is a Python script. You can get it for -the first time with +the first time with: :: @@ -52,6 +52,8 @@ GitHub and set them as :term:`active `: # Activate PATH and other environment variables in the current terminal source ./emsdk_env.sh + .. tip:: If you want to avoid executing `source ./emsdk_env.sh` every time you open a new terminal, you can follow the instructions given by the `emsdk activate` command above to add this command to your startup scripts. + .. note:: On Windows, run ``emsdk.bat`` instead of ``./emsdk``, and ``emsdk_env.bat`` instead of ``source ./emsdk_env.sh``. .. note:: On Windows, if you use the ``activate`` command, the step of ``emsdk_env.bat`` is optional. If you want to know more, see :ref:`activate SDK version `. @@ -65,7 +67,7 @@ commands. Emsdk install targets --------------------- -In the description above we asked the emsdk to install and activate ``latest``, +In the description above, we asked the emsdk to install and activate ``latest``, which is the latest tagged release. That is often what you want. You can also install a specific version by specifying it, for example, @@ -99,7 +101,7 @@ target, and note that you must specify the backend explicitly, # Get a tip-of-tree ./emsdk install tot -(In the above examples we installed the various targets; remember to also +(In the above examples, we installed the various targets; remember to also ``activate`` them as in the full example from earlier.) .. _platform-notes-installation_instructions-SDK: @@ -110,7 +112,7 @@ Platform-specific notes Windows +++++++ -#. Install Python 3.6 or newer (older versions may not work due to `a GitHub change with SSL `_). +#. Install Python 3.8 or newer. .. note:: Instead of running emscripten on Windows directly, you can use the Windows Subsystem for Linux to run it in a Linux environment. @@ -120,8 +122,8 @@ macOS .. note:: Emscripten requires macOS 10.14 Mojave or above. -If you use the Emscripten SDK it includes a bundled version of Python 3. -Otherwise you will need to manually install and use Python 3.6 or newer. +If you use the Emscripten SDK, it includes a bundled version of Python 3. +Otherwise, you will need to manually install and use Python 3.8 or newer. These instructions explain how to install **all** the :ref:`required tools `. You can :ref:`test whether some of these are already diff --git a/site/source/docs/getting_started/test-suite.rst b/site/source/docs/getting_started/test-suite.rst index 7fd08990a203a..095ddc1fdf8f0 100644 --- a/site/source/docs/getting_started/test-suite.rst +++ b/site/source/docs/getting_started/test-suite.rst @@ -99,7 +99,7 @@ the first failure. that are normally run in parallel you can force them to run serially using ``-j1``. -One a test is fixed you continue where you left off using ``--start-at`` option: +Once a test is fixed, you continue where you left off using ``--start-at`` option: .. code-block:: bash diff --git a/site/source/docs/introducing_emscripten/about_emscripten.rst b/site/source/docs/introducing_emscripten/about_emscripten.rst index 9cccbe37b9510..c0c97c1508560 100644 --- a/site/source/docs/introducing_emscripten/about_emscripten.rst +++ b/site/source/docs/introducing_emscripten/about_emscripten.rst @@ -12,7 +12,7 @@ toolchain to WebAssembly. Using Emscripten you can: - Compile the C/C++ **runtimes** of other languages into WebAssembly, and then run code in those other languages in an *indirect* way (for example, this has been done for - `Python `_ and + `Python `_ and `Lua `_). Practically any **portable** C or C++ codebase can be compiled into WebAssembly diff --git a/site/source/docs/porting/Debugging.rst b/site/source/docs/porting/Debugging.rst index 3a7c3b67bfbe2..8573de6f3af75 100644 --- a/site/source/docs/porting/Debugging.rst +++ b/site/source/docs/porting/Debugging.rst @@ -4,317 +4,438 @@ Debugging ========= -One of the main advantages of debugging cross-platform Emscripten code is that the same cross-platform source code can be debugged on either the native platform or using the web browser's increasingly powerful toolset — including debugger, profiler, and other tools. - -Emscripten provides a lot of functionality and tools to aid debugging: - -- :ref:`Compiler debug information flags ` that allow you to preserve debug information in compiled code and even create source maps so that you can step through the native C++ source when debugging in the browser. -- :ref:`Debug mode `, which emits debug logs and stores intermediate build files for analysis. -- :ref:`Compiler settings ` to enable runtime checking of memory accesses and common allocation errors. -- :ref:`debugging-manual-debugging` of Emscripten-generated code is also supported, which is in some ways even better than on native platforms. -- :ref:`debugging-autodebugger`, which automatically instruments LLVM bitcode to write out each store to memory. - -This article describes the main tools and settings provided by Emscripten for debugging, along with a section explaining how to debug a number of :ref:`debugging-emscripten-specific-issues`. - - -.. _debugging-debug-information-g: - -Debugging in the browser -======================== - -:ref:`Emcc ` can output debug information in two formats, either as -DWARF symbols or as source maps. Both allow you to view and debug the -*C/C++ source code* in a browser's debugger. DWARF offers the most precise and -detailed debugging experience and is supported as an experiment in Chrome 88 -with an `extension `. See -`here ` for a detailed -usage guide. Source maps are more widely supported in Firefox, Chrome, and -Safari, but unlike DWARF they cannot be used to inspect variables, for example. - -:ref:`Emcc ` strips out most of the debug information from -:ref:`optimized builds ` by default. DWARF can be produced with -the *emcc* :ref:`-g flag `, and source maps can be emitted with the -:ref:`-gsource-map ` option. Be aware that optimisation levels -:ref:`-O1 ` and above increasingly remove LLVM debug information, and -also disable runtime :ref:`ASSERTIONS ` checks. Passing a -``-g`` flag also affects the generated JavaScript code and preserves -white-space, function names, and variable names, - -.. tip:: Even for medium-sized projects, DWARF debug information can be of - substantial size and negatively impact the page performance, particularly - compiling and loading of the module. Debug information can also be emitted in - a file on the side instead with the - :ref:`-gseparate-dwarf ` option! The debug information - size also affects the linking time, because the debug information in all - object files needs to be linked as well. Passing the - :ref:`-gsplit-dwarf ` option can help here, which causes - clang to leave debug information scattered across object files. That debug - information needs to be linked into a DWARF package file (``.dwp``) using the - ``emdwp`` tool then, but that could happen in parallel to the linking of - the compiled output! When running it - after linking, it's as simple as ``emdwp -e foo.wasm -o foo.wasm.dwp``, or - ``emdwp -e foo.debug.wasm -o foo.debug.wasm.dwp`` when used together with - ``-gseparate-dwarf`` (the dwp file should have the same file name as the main - symbol file with an extra ``.dwp`` extension). - -The ``-g`` flag can also be specified with an integer levels: -:ref:`-g0 `, :ref:`-g1 `, :ref:`-g2 ` (default with -``-gsource-map``), and :ref:`-g3 ` (default with ``-g``). Each level -builds on the last to provide progressively more debug information in the -compiled output. - -.. note:: Because Binaryen optimization degrades the quality of DWARF info further, ``-O1 -g`` will skip running the Binaryen optimizer (``wasm-opt``) entirely unless required by other options. You can also throw in ``-sERROR_ON_WASM_CHANGES_AFTER_LINK`` option if you want to ensure the debug info is preserved. See `Skipping Binaryen `_ for more details. - -.. note:: Some optimizations may be disabled when used in conjunction with the debug flags both in the Binaryen optimizer (even if it runs) and JavaScript optimizer. For example, if you compile with ``-O3 -g``, the Binaryen optimizer will skip some of the optimization passes that do not produce valid DWARF information, and also some of the normal JavaScript optimization will be disabled in order to better provide the requested debugging information. +One of the main advantages of debugging cross-platform Emscripten code is that +the same cross-platform source code can be debugged on either the native +platform or using the web browser's increasingly powerful toolset — including a +debugger, profiler, and other tools. -.. _debugging-EMCC_DEBUG: +This article describes the main tools and settings provided by Emscripten for +debugging, organized by common developer use cases. + + +Overview: Emitting and Controlling Debug Information +==================================================== +Debugging-related information comes in several forms: in Wasm object and binary +files (as DWARF sections or Wasm name section), side output files (as source +maps, symbol maps, or DWARF sidecar or package files), and even in the code +itself (as assertions or instrumentation, or JS whitespace and comments). For +information on DWARF, see :ref:`below `. In addition to DWARF, +wasm files may contain a `name section +`_ +which includes names for each function; these function names are displayed by +browsers when they generate `stack traces +`_ and in +developer tools. Source maps are also supported by Emscripten and by browser +DevTools (see :ref:`below `). + +This document contains an overview of the flags used to emit and control +debugging behavior, and use-case-based examples. + +Unlike traditional Unix-style C toolchains, flags must be passed at link time to +preserve or generate debug information (these defaults aim to avoid unintended +bloat in production builds). The most common of these are the :ref:`-g flags +`; see the flag documentation or the use cases below for more detail. + +Flags that cause DWARF generation (e.g. ``-g3``, ``-gline-tables-only``) also +generate a name section in the binary and suppress minification of the JS glue +file (since most DWARF use cases are for interactive debugging or where the +binary will be stripped). Other flags (e.g. ``-g2``, ``-gsource-map``) should +affect only a specific behavior or type of debug info, and are generally +composable. + + +.. _debugging-interactive: + +Interactive, Source-Level Debugging +============================================= -Debug mode (EMCC_DEBUG) -======================= +For stepping through C/C++ source code in a browser's debugger, you can use +debug information in either DWARF or source map format. -The ``EMCC_DEBUG`` environment variable can be set to enable Emscripten's debug mode: +DWARF offers the best debugging experience and is supported in Chrome with an +`extension `_. See `here +`_ for a detailed usage +guide. Source maps are more widely supported, but they provide only location +mapping and cannot be used easily to inspect variables. + + +.. _debugging-dwarf: + +DWARF +----- + +In a traditional Unix-style C toolchain, flags such as ``-g`` are passed to the +compiler, placing DWARF sections in the object files. This DWARF info is +combined by the linker and appears in the output, independently of any +optimization settings. In contrast, although :ref:`emcc ` supports many +of the common `clang flags +`_ +to generate DWARF into the object files, final debug output is also controlled +by link-time flags, and is more affected by optimization. For example ``emcc`` +strips out most of the debug information after linking if a debugging-related +flag is not provided at link time, even if the input object files contain DWARF. + +DWARF can be produced at compile time with the *emcc* :ref:`-g flag `. +Optimization levels above :ref:`-O1 ` or :ref:`-Og ` +increasingly degrade LLVM debug information (as with other architectures), and +optimization flags at link time also disable Emscripten's runtime +:ref:`ASSERTIONS ` checks. Passing a ``-g`` flag at link +time also affects the generated JavaScript code (preserving white-space, +function names, and variable names, which makes the JavaScript debuggable). + +The ``-g`` flag can also be specified with integer levels: :ref:`-g0 `, +:ref:`-g1 `, :ref:`-g2 `, and :ref:`-g3 ` (equivalent +to ``-g``). At compile time these flags control the amount of DWARF in the +object files. At link time, each adds sucessively more kinds of information in +the wasm and JS files (DWARF is only retained after linking when using ``-g`` or +``-g3``). + +Example: .. code-block:: bash - # Linux or macOS - EMCC_DEBUG=1 emcc test/hello_world.cpp -o hello.html + emcc source.c -c -o source.o -g # source.o has DWARF sections emcc source.o -o + program.js -g # program.wasm has DWARF and a name section + + +.. tip:: Even for medium-sized projects, DWARF debug information can be large. + Debug information can be emitted in a separate file with the + :ref:`-gseparate-dwarf ` option. To speed up linking, + the :ref:`-gsplit-dwarf ` option can be used at compile + time. See `this article + `_ + for more details on debugging large files, and see :ref:`the next section + ` for more ways to reduce debug info size. + + +.. note:: Because Binaryen optimization degrades the quality of DWARF info + further, higher link-time optimization settings are + not recommended. The ``-O1`` setting will skip running the Binaryen optimizer + (``wasm-opt``) entirely unless required by other options. You can also add the + ``-sERROR_ON_WASM_CHANGES_AFTER_LINK`` option if you want to ensure the debug + info is preserved. See `Skipping Binaryen + `_ + for more details. + + +.. _debugging-symbolization: + +Symbolizing Production Crash Logs +============================================= + +Even when not using an interactive debugger, it's valuable to have source +information for compiled code locations, particularly for stack traces or crash +logs. This is also true for fully-optimized production builds. + +`Source maps `_ are commonly used for +languages that compile to JavaScript (mapping locations in the compiled JS +output to locations in the original source code), but WebAssembly is also +supported. Emscripten can emit source maps with the :ref:`-gsource-map +` link-time flag. Source maps are preserved even with full +post-link optimizations, so they work well for this use case. Source maps are +generated by Emscripten from DWARF information. Therefore the linked object +files must have DWARF. The final linked output will not have DWARF unless `-g` +is also passed at link time. + +DWARF can also be used for this purpose. Typically a binary containing DWARF +would be generated at build time, and then stripped. The stripped copy would be +served to users, and the original would be saved for symbolication purposes. For +this use case, full information about about types and variables from the sources +isn't needed; the `-gline-tables-only +`_ +compile-time flag causes clang to generate only the line table information, +saving DWARF size and compile/linking time. + +Source maps are easier to parse and more widely supported by ecosystem tooling. +And as noted above, preserving DWARF inhibits some Binaryen optimizations. +However DWARF has the advantage that it includes information about inlining, +which can result in more accurate stack traces. + +Examples: - # Windows - set EMCC_DEBUG=1 - emcc test/hello_world.cpp -o hello.html - set EMCC_DEBUG=0 +.. code-block:: bash -With ``EMCC_DEBUG=1`` set, :ref:`emcc ` emits debug output and generates intermediate files for the compiler's various stages. ``EMCC_DEBUG=2`` additionally generates intermediate files for each JavaScript optimizer pass. + emcc source.c -c -o source.o -g # source.o has DWARF sections (-gsource-map also works here) + emcc source.o -o program.js -gsource-map # program.wasm.map contains a source map -The debug logs and intermediate files are output to -**TEMP_DIR/emscripten_temp**, where ``TEMP_DIR`` is the OS default temporary -directory (e.g. **/tmp** on UNIX). + emcc source.o -o program2.js -g # program2.wasm has DWARF + llvm-strip program2.wasm -o program2_stripped.wasm # program2_stripped.wasm has no debug info -The debug logs can be analysed to profile and review the changes that were made in each step. +Emscripten includes a tool called ``emsymbolizer`` that can map wasm code +addresses to sources using several different kinds of debug info, including +DWARF (in wasm object or linked files) and source maps for line/column info, and +symbol maps (see :ref:`emcc-emit-symbol-map`), name sections and object file +symbol tables for function names. -.. note:: The more limited amount of debug information can also be enabled by specifying the :ref:`verbose output ` compiler flag (``emcc -v``). +Fast Edit+Compile with minimal debug information +================================================ -.. _debugging-compilation-settings: +When you want the fastest builds, you generally want to avoid generating large +debug information during compile, because it takes time to link into the final +binary. It is still worthwhile to use the ``-g2`` flag (at link time only) +because browsers understand the name section even when devtools are not in use, +resulting in more useful stack traces at minimal cost. -Compiler settings -================== +Example: -Emscripten has a number of compiler settings that can be useful for debugging. These are set using the :ref:`emcc -s` option, and will override any optimization flags. For example: +.. code-block:: bash + + emcc source.c -c -o source.o # source.o has no debug info + emcc source.o -o program.js -g2 # program.wasm has a name section, program.js is unminified + +Sometimes the use of the ``-O1`` or ``-Og`` flag at compile time can also result +in faster builds, because optimizations early in the pipeline can reduce the +amount of IR that is processed by later phases such as instruction selection and +linking. It also of course reduces test runtime. + +.. _debugging-memory-safety: + +Detecting Memory Errors and Undefined Behavior +============================================== + +The best tools for detecting memory safety and undefined behavior issues are +Clang's sanitizers, such as the Undefined Behavior Sanitizer (UBSan) and the +Address Sanitizer (ASan). For more information, see :ref:`Sanitizers`. + + +Emscripten has several other compiler settings that can be useful for catching +errors at runtime. These are set using the :ref:`emcc -s` +option. For example: .. code-block:: bash - emcc -O1 -sASSERTIONS test/hello_world + emcc -O1 -sASSERTIONS test/hello_world.c Some important settings are: - .. _debugging-ASSERTIONS: - ``ASSERTIONS=1`` is used to enable runtime checks for common memory allocation errors (e.g. writing more memory than was allocated). It also defines how Emscripten should handle errors in program flow. The value can be set to ``ASSERTIONS=2`` in order to run additional tests. - - ``ASSERTIONS=1`` is enabled by default. Assertions are turned off for optimized code (:ref:`-O1 ` and above). + ``ASSERTIONS=1`` is used to enable runtime checks for many types of common + errors. It also defines how Emscripten should handle errors in program flow. + The value can be set to ``ASSERTIONS=2`` in order to run additional tests. + ``ASSERTIONS=1`` is enabled by default at ``-O0`` and disabled at higher + optimization levels, but can be overridden. - .. _debugging-SAFE-HEAP: - ``SAFE_HEAP=1`` adds additional memory access checks, and will give clear errors for problems like dereferencing 0 and memory alignment issues. - - You can also set ``SAFE_HEAP_LOG`` to log ``SAFE_HEAP`` operations. + ``SAFE_HEAP=1`` adds additional memory access checks with a Binaryen pass, + and will give clear errors for problems like dereferencing 0 and memory + alignment issues. You can also set ``SAFE_HEAP_LOG`` to log ``SAFE_HEAP`` + operations. :ref:`ASan` provides most of the functionality + of this pass (plus some extras) and is generally preferred to try first + unless :ref:`alignment issues` are + important for your platform. - .. _debugging-STACK_OVERFLOW_CHECK: - Passing the ``STACK_OVERFLOW_CHECK=1`` linker flag adds a runtime magic - token value at the end of the stack, which is checked in certain locations - to verify that the user code does not accidentally write past the end of the - stack. While overrunning the Emscripten stack is not a security issue for - JavaScript (which is unaffected), writing past the stack causes memory - corruption in global data and dynamically allocated memory sections in the - Emscripten HEAP, which makes the application fail in unexpected ways. The - value ``STACK_OVERFLOW_CHECK=2`` enables slightly more detailed stack guard + ``STACK_OVERFLOW_CHECK=1`` adds a runtime magic token value at the end of + the stack, which is checked in certain locations to verify that the user + code does not accidentally write past the end of the stack. While + overrunning the Emscripten stack is not a security issue for JavaScript + (which is unaffected), writing past the stack causes memory corruption in + global data and dynamically allocated memory sections in the Emscripten + HEAP, which makes the application fail in unexpected ways. The value + ``STACK_OVERFLOW_CHECK=2`` enables slightly more detailed stack guard checks, which can give a more precise callstack at the expense of some performance. Default value is 1 if ``ASSERTIONS=1`` is set, and disabled otherwise. -A number of other useful debug settings are defined in `src/settings.js `_. For more information, search that file for the keywords "check" and "debug". - -.. _debugging-sanitizers: - -Sanitizers -========== - -Emscripten also supports some of Clang's sanitizers, such as :ref:`sanitizer_ubsan` and :ref:`sanitizer_asan`. - -.. _debugging-emcc-v: - -emcc verbose output -=================== - -Compiling with the :ref:`emcc -v ` will cause Emscripten to output -the sub-command that it runs as well as passes ``-v`` to Clang. -.. _debugging-manual-debugging: -Manual print debugging -====================== +A number of other useful debug settings are defined in `src/settings.js +`_. For +more information, search that file for the keywords "check" and "debug". -You can also manually instrument the source code with ``printf()`` statements, then compile and run the code to investigate issues. Note that ``printf()`` is line-buffered, make sure to add ``\n`` to see output in the console. -If you have a good idea of the problem line you can add ``print(new Error().stack)`` to the JavaScript to get a stack trace at that point. - -Debug printouts can even execute arbitrary JavaScript. For example:: +.. _debugging-profiling: - function _addAndPrint($left, $right) { - $left = $left | 0; - $right = $right | 0; - //--- - if ($left < $right) console.log('l` using ``--profiling``, (which is currently the same as +:ref:`-g2 `), and then run the code in the browser's devtools profiler. +You should then be able to see in which functions most of the time is spent. -Chrome devtools support source-level debugging on WebAssembly files with DWARF information. To use that, you need the Wasm debugging extension plugin here: -https://goo.gle/wasm-debugging-extension +Memory +------ -See `Debugging WebAssembly with modern tools -`_ for the details. +The browser's memory profiling tools generally only understand allocations at +the JavaScript level. From that perspective, the entire linear memory that the +emscripten-compiled application uses is a single big allocation (of a +``WebAssembly.Memory``). To get information about usage inside that object, you +need other tools: + +* Emscripten supports the `mallinfo() + `_, API, which gives + you information from ``dlmalloc`` about current allocations. +* Emscripten also has a ``--memoryprofiler`` option that displays memory usage + in a visual manner. Note that you need to emit HTML (e.g. with a command like + ``emcc test/hello_world.c --memoryprofiler -o page.html``) as the memory + profiler output is rendered onto the page. To view it, load ``page.html`` in + your browser (remember to use a :ref:`local webserver `). + The display auto-updates, so you can open the devtools console and run a + command like ``_malloc(1024 * 1024)``. That will allocate 1MB of memory, which + will then show up on the memory profiler display. +.. _debugging-manual-debugging: -Handling C++ Exceptions from JavaScript -======================================= -See :ref:`handling-c-exceptions-from-javascript`. +Manual print debugging +====================== +You can also manually instrument the source code with ``printf()`` statements, +then compile and run the code to investigate issues. The output from the +`stdout` and `stderr` streams is copied to the browser console by default. Note +that ``printf()`` is line-buffered, so make sure to add ``\n`` to see output in +the console. The functions in the :ref:`console.h ` header can also +be used to access the console more directly. .. _debugging-emscripten-specific-issues: -Emscripten-specific issues +Emscripten-Specific Issues ========================== Memory Alignment Issues ----------------------- -The :ref:`Emscripten memory representation ` is compatible with C and C++. However, when undefined behavior is involved you may see differences with native architectures, and also differences between Emscripten's output for asm.js and WebAssembly: - -- In asm.js, loads and stores must be aligned, and performing a normal load or store on an unaligned address can fail silently (access the wrong address). If the compiler knows a load or store is unaligned, it can emulate it in a way that works but is slow. -- In WebAssembly, unaligned loads and stores will work. Each one is annotated with its expected alignment. If the actual alignment does not match, it will still work, but may be slow on some CPU architectures. +The :ref:`Emscripten memory representation ` is +compatible with C and C++. In WebAssembly, unaligned loads and stores will work; +each may be annotated with its expected alignment. However if the actual +alignment does not match, it may be very slow on some systems. .. tip:: :ref:`SAFE_HEAP ` can be used to reveal memory alignment issues. -Generally it is best to avoid unaligned reads and writes — often they occur as the result of undefined behavior, as mentioned above. In some cases, however, they are unavoidable — for example if the code to be ported reads an ``int`` from a packed structure in some pre-existing data format. In that case, to make things work properly in asm.js, and be fast in WebAssembly, you must be sure that the compiler knows the load or store is unaligned. To do so you can: +Generally it is best to avoid unaligned reads and writes. Often they occur as +the result of undefined behavior. In some cases, however, they are unavoidable — +for example if the code to be ported reads an ``int`` from a packed structure in +some pre-existing data format. In that case, to make it as fast as possible in +WebAssembly, you can make sure that the compiler knows the load or store is +unaligned. To do so you can: - Manually read individual bytes and reconstruct the full value -- Use the :c:type:`emscripten_align* ` typedefs, which define unaligned versions of the basic types (``short``, ``int``, ``float``, ``double``). All operations on those types are not fully aligned (use the ``1`` variants in most cases, which mean no alignment whatsoever). - +- Use the :c:type:`emscripten_align* ` typedefs, which + define unaligned versions of the basic types (``short``, ``int``, ``float``, + ``double``). All operations on those types are not fully aligned (use the + ``1`` variants in most cases, which mean no alignment whatsoever). Function Pointer Issues ----------------------- -If you get an ``abort()`` from a function pointer call to ``nullFunc`` or ``b0`` or ``b1`` (possibly with an error message saying "incorrect function pointer"), the problem is that the function pointer was not found in the expected function pointer table when called. +If you get an ``abort()`` from a function pointer call to ``nullFunc`` or ``b0`` +or ``b1`` (possibly with an error message saying "incorrect function pointer"), +the problem is that the function pointer was not found in the expected function +pointer table when called. -.. note:: ``nullFunc`` is the function used to populate empty index entries in the function pointer tables (``b0`` and ``b1`` are shorter names used for ``nullFunc`` in more optimized builds). A function pointer to an invalid index will call this function, which simply calls ``abort()``. +.. note:: ``nullFunc`` is the function used to populate empty index entries in + the function pointer tables (``b0`` and ``b1`` are shorter names used for + ``nullFunc`` in more optimized builds). A function pointer to an invalid + index will call this function, which simply calls ``abort()``. There are several possible causes: -- Your code is calling a function pointer that has been cast from another type (this is undefined behavior but it does happen in real-world code). In optimized Emscripten output, each function pointer type is stored in a separate table based on its original signature, so you *must* call a function pointer with that same signature to get the right behavior (see :ref:`portability-function-pointer-issues` in the code portability section for more information). -- Your code is calling a method on a ``NULL`` pointer or dereferencing 0. This sort of bug can be caused by any sort of coding error, but manifests as a function pointer error because the function can't be found in the expected table at runtime. - -In order to debug these sorts of issues: - -- Compile with ``-Werror``. This turns warnings into errors, which can be useful as some cases of undefined behavior would otherwise show warnings. -- Use ``-sASSERTIONS=2`` to get some useful information about the function pointer being called, and its type. -- Look at the browser stack trace to see where the error occurs and which function should have been called. -- Enable clang warnings on dangerous function pointer casts using ``-Wcast-function-type``. +- Your code is calling a function pointer that has been cast from another type + (this is undefined behavior but it does happen in real-world code). In + optimized Emscripten output, each function pointer type is stored in a + separate table based on its original signature, so you *must* call a function + pointer with that same signature to get the right behavior (see + :ref:`portability-function-pointer-issues` in the code portability section for + more information). +- Your code is calling a method on a ``NULL`` pointer or dereferencing 0. This + sort of bug can be caused by any sort of coding error, but manifests as a + function pointer error because the function can't be found in the expected + table at runtime. + + +To debug these sorts of issues: + +- Compile with ``-Werror`` (or otherwise fix warnings, many of which highlight + undefined behavior). +- Use ``-sASSERTIONS=2`` to get some useful information about the function + pointer being called, and its type. +- Look at the browser stack trace to see where the error occurs and which + function should have been called. +- Enable clang warnings on dangerous function pointer casts using + ``-Wcast-function-type``. - Build with :ref:`SAFE_HEAP=1 `. - :ref:`Sanitizers` can help here, in particular UBSan. -Another function pointer issue is when the wrong function is called. :ref:`SAFE_HEAP=1 ` can help with this as it detects some possible errors with function table accesses. - Infinite loops -------------- -Infinite loops cause your page to hang. After a period the browser will notify the user that the page is stuck and offer to halt or close it. +Infinite loops cause your page to hang. After a period the browser will notify +the user that the page is stuck and offer to halt or close it. If your code hits +an infinite loop, one easy way to find the problem code is to use a *JavaScript +profiler*. In the Firefox profiler, if the code enters an infinite loop you will +see a block of code doing the same thing repeatedly near the end of the profile. -If your code hits an infinite loop, one easy way to find the problem code is to use a *JavaScript profiler*. In the Firefox profiler, if the code enters an infinite loop you will see a block of code doing the same thing repeatedly near the end of the profile. +.. note:: The :ref:`emscripten-runtime-environment-main-loop` may need to be + re-coded if your application uses an infinite main loop. -.. note:: The :ref:`emscripten-runtime-environment-main-loop` may need to be re-coded if your application uses an infinite main loop. +.. _other-debugging-tools: -.. _debugging-profiling: +Debugging Emscripten +==================== -Profiling -========= - -Speed ------ - -To profile your code for speed, build with :ref:`profiling info `, -then run the code in the browser's devtools profiler. You should then be able to -see in which functions is most of the time spent. - -.. _debugging-profiling-memory: - -Memory ------- - -The browser's memory profiling tools generally only understand -allocations at the JavaScript level. From that perspective, the entire linear -memory that the emscripten-compiled application uses is a single big allocation -(of a ``WebAssembly.Memory``). The devtools will not show information about -usage inside that object, so you need other tools for that, which we will now -describe. - -Emscripten supports -`mallinfo() `_, which lets -you get information from ``dlmalloc`` about current allocations. For example -usage, see -`the test `_. - -Emscripten also has a ``--memoryprofiler`` option that displays memory usage -in a visual manner, letting you see how fragmented it is and so forth. To use -it, you can do something like +.. _debugging-EMCC_DEBUG: -.. code-block:: bash +Debugging the compiler driver +----------------------------- - emcc test/hello_world.c --memoryprofiler -o page.html +Compiling with the :ref:`emcc -v ` will cause emcc to output the +sub-commands that it runs as well as passes ``-v`` to Clang. The ``EMCC_DEBUG`` +environment variable can be set to emit even more debug output and generate +intermediate files for the compiler's various stages. -Note that you need to emit HTML as in that example, as the memory profiler -output is rendered onto the page. To view it, load ``page.html`` in your -browser (remember to use a :ref:`local webserver `). The display -auto-updates, so you can open the devtools console and run a command like -``_malloc(1024 * 1024)``. That will allocate 1MB of memory, which will then show -up on the memory profiler display. .. _debugging-autodebugger: AutoDebugger -============ +------------ -The *AutoDebugger* is the 'nuclear option' for debugging Emscripten code. +The *AutoDebugger* is the 'nuclear option' for debugging Emscripten code. It +will rewrite the output so it prints out each store to memory. This is useful +for comparing the output for different compiler settings in order to detect +regressions. To run the *AutoDebugger*, compile with the environment variable +``EMCC_AUTODEBUG=1`` set. .. warning:: This option is primarily intended for Emscripten core developers. -The *AutoDebugger* will rewrite the output so it prints out each store to memory. This is useful because you can compare the output for different compiler settings in order to detect regressions. +The *AutoDebugger* will rewrite the output so it prints out each store to +memory. This is useful because you can compare the output for different compiler +settings in order to detect regressions. -The *AutoDebugger* can potentially find **any** problem in the generated code, so it is strictly more powerful than the ``CHECK_*`` settings and ``SAFE_HEAP``. One use of the *AutoDebugger* is to quickly emit lots of logging output, which can then be reviewed for odd behavior. The *AutoDebugger* is also particularly useful for :ref:`debugging regressions `. +The *AutoDebugger* can potentially find **any** problem in the generated code, +so it is strictly more powerful than the ``CHECK_*`` settings and ``SAFE_HEAP``. +One use of the *AutoDebugger* is to quickly emit lots of logging output, which +can then be reviewed for odd behavior. The *AutoDebugger* is also particularly +useful for :ref:`debugging regressions `. The *AutoDebugger* has some limitations: -- It generates a lot of output. Using *diff* can be very helpful for identifying changes. -- It prints out simple numerical values rather than pointer addresses (because pointer addresses change between runs, and hence can't be compared). This is a limitation because sometimes inspection of addresses can show errors where the pointer address is 0 or impossibly large. It is possible to modify the tool to print out addresses as integers in ``tools/autodebugger.py``. +- It generates a lot of output. Using *diff* can be very helpful for + identifying changes. +- It prints out simple numerical values rather than pointer addresses (because + pointer addresses change between runs, and hence can't be compared). This is + a limitation because sometimes inspection of addresses can show errors where + the pointer address is 0 or impossibly large. It is possible to modify the + tool to print out addresses as integers in ``tools/autodebugger.py``. -To run the *AutoDebugger*, compile with the environment variable ``EMCC_AUTODEBUG=1`` set. For example: +To run the *AutoDebugger*, compile with the environment variable +``EMCC_AUTODEBUG=1`` set. For example: .. code-block:: bash # Linux or macOS EMCC_AUTODEBUG=1 emcc test/hello_world.cpp -o hello.html - # Windows set EMCC_AUTODEBUG=1 emcc test/hello_world.cpp -o hello.html @@ -329,7 +450,9 @@ AutoDebugger Regression Workflow Use the following workflow to find regressions with the *AutoDebugger*: - Compile the working code with ``EMCC_AUTODEBUG=1`` set in the environment. -- Compile the code using ``EMCC_AUTODEBUG=1`` in the environment again, but this time with the settings that cause the regression. Following this step we have one build before the regression and one after. +- Compile the code using ``EMCC_AUTODEBUG=1`` in the environment again, but this + time with the settings that cause the regression. Following this step we have + one build before the regression and one after. - Run both versions of the compiled code and save their output. - Compare the output using a *diff* tool. @@ -340,17 +463,19 @@ Any difference between the outputs is likely to be caused by the bug. and other issues don't cause false positives. + Useful Links ============ -- `Blogpost about reading compiler output `_. -- `GDC 2014: Getting started with asm.js and Emscripten `_ (Debugging slides). - `Links to Wasm debugging-related documents `_ Need help? ========== -The :ref:`Emscripten Test Suite ` contains good examples of almost all functionality offered by Emscripten. If you have a problem, it is a good idea to search the suite to determine whether test code with similar behavior is able to run. +The :ref:`Emscripten Test Suite ` contains good examples +of almost all functionality offered by Emscripten. If you have a problem, it is +a good idea to search the suite to determine whether test code with similar +behavior is able to run. If you've tried the ideas here and you need more help, please :ref:`contact`. diff --git a/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst b/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst index a3f8287047e88..7676b1ab28528 100644 --- a/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst +++ b/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst @@ -362,20 +362,18 @@ Implement a C API in JavaScript It is possible to implement a C API in JavaScript! This is the approach used in many of Emscripten's libraries, like SDL1 and OpenGL. -You can use it to write your own APIs to call from C/C++. To do this -you define the interface, decorating with ``extern`` to mark the methods -in the API as external symbols. You then implement the symbols in -JavaScript by simply adding their definition to `library.js`_ (by -default). When compiling the C code, the compiler looks in the JavaScript -libraries for relevant external symbols. - -By default, the implementation is added to **library.js** (and this is -where you'll find parts of Emscripten's *libc*). You can put -the JavaScript implementation in your own library file and add it using -the :ref:`emcc option ` ``--js-library``. See -`test_js_libraries`_ in **test/test_other.py** for a complete working -example, including the syntax you should use inside the JavaScript library -file. +You can use it to write your own APIs to call from C/C++. To do this you define +the interface, decorating with ``extern`` to mark the methods in the API as +external symbols. You can then implement the symbols in JavaScript by simply +adding their definition to one of the `core JS library`_ files. Undefined +native symbols will be resolved by looking for them in JavaScript library files. + +The `core JS library`_ files are where you will find Emscripten internals. For +example, parts of Emscripten's *libc* are implemented there. You can also put +the JavaScript implementation in your own library file and add it using the +:ref:`emcc option ` ``--js-library``. See `test_jslib`_ in +**test/test_other.py** for a complete working example, including the syntax you +should use inside the JavaScript library file. As a simple example, consider the case where you have some C code like this: @@ -613,30 +611,63 @@ Calling JavaScript functions as function pointers from C ======================================================== You can use ``addFunction`` to return an integer value that represents a -function pointer. Passing that integer to C code then lets it call that value as -a function pointer, and the JavaScript function you sent to ``addFunction`` will -be called. +function pointer. Passing that integer to C code then lets it call that value +as a function pointer, and the JavaScript function you sent to ``addFunction`` +will be called. See `test_add_function in test/test_core.py`_ for an example. You should build with ``-sALLOW_TABLE_GROWTH`` to allow new functions to be added to the table. Otherwise by default the table has a fixed size. -.. note:: When using ``addFunction`` on LLVM Wasm backend, you need to provide - an additional second argument, a Wasm function signature string. Each - character within a signature string represents a type. The first character - represents the return type of a function, and remaining characters are for - parameter types. +When using ``addFunction`` with a JavaScript function, you need to provide +an additional second argument, a Wasm function signature string, explained +below. See `test/interop/test_add_function_post.js `_ for an example. + + +.. _interacting-with-code-function-signatures: + +Function Signatures +=================== + +The LLVM Wasm backend requires a Wasm function signature string when using +``addFunction`` and in JavaScript libraries. Each character within a signature +string represents a type. The first character represents the return type of a +function, and remaining characters are for parameter types. - ``'v'``: void type - ``'i'``: 32-bit integer type - - ``'j'``: 64-bit integer type (currently does not exist in JavaScript) + - ``'j'``: 64-bit integer type (see note below) - ``'f'``: 32-bit float type - ``'d'``: 64-bit float type + - ``'p'``: 32-bit or 64-bit pointer (MEMORY64) + +For example, if you add a function that takes an integer and does not return +anything, the signature is ``'vi'``. + +When ``'j'`` is used there are several ways in which the parameter value will +be passed to JavaScript. By default, the value will either be passed as a +single BigInt or a pair of JavaScript numbers (double) depending on whether +the ``WASM_BIGINT`` settings is enabled. In addition, if you only require 53 +bits of precision you can add the ``__i53abi`` decorator, which will ignore +the upper bits and the value will be received as a single JavaScript number +(double). It cannot be used with ``addFunction``. Here is an example of a +library function that sets the size of a file using a 64-bit value passed as +a 53 bit (double) and returns an integer error code: + +.. code-block:: c + + extern "C" int _set_file_size(int handle, uint64_t size); + +.. code-block:: javascript - For example, if you add a function that takes an integer and does not return - anything, you can do ``addFunction(your_function, 'vi');``. See - `test/interop/test_add_function_post.js `_ for an example. + _set_file_size__i53abi: true, // Handle 64-bit + _set_file_size__sig: 'iij', // Function signature + _set_file_size: function(handle, size) { ... return error; } + +Using ``-sWASM_BIGINT`` when linking is an alternative method of handling +64-bit types in libraries. ```Number()``` may be needed on the JavaScript +side to convert it to a useable value. See `settings reference `_. .. _interacting-with-code-access-memory: @@ -810,8 +841,8 @@ on Emscripten. If you would like to port existing Node-API addon to WebAssembly or compile the same binding code to both Node.js native addon and WebAssembly, you can give it a try. See `Emnapi documentation`_ for more details. -.. _library.js: https://github.com/emscripten-core/emscripten/blob/main/src/library.js -.. _test_js_libraries: https://github.com/emscripten-core/emscripten/blob/1.29.12/tests/test_core.py#L5043 +.. _core JS library: https://github.com/emscripten-core/emscripten/blob/main/src/lib/ +.. _test_jslib: https://github.com/emscripten-core/emscripten/blob/4.0.9/test/test_core.py#L6261 .. _tools/system_libs.py: https://github.com/emscripten-core/emscripten/blob/main/tools/system_libs.py .. _library_\*.js: https://github.com/emscripten-core/emscripten/tree/main/src .. _test_add_function in test/test_core.py: https://github.com/emscripten-core/emscripten/blob/1.29.12/tests/test_core.py#L6237 diff --git a/site/source/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.rst b/site/source/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.rst index 744ed75cfbbe2..81857db241d7a 100644 --- a/site/source/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.rst +++ b/site/source/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.rst @@ -318,7 +318,7 @@ You can bind to C++ operators using ``[Operator=]``: .. note:: - The operator name can be anything (``add`` is just an example). - - Support is currently limited to operators that contain ``=``: ``+=``, ``*=``, ``-=`` etc., and to the array indexing operator ``[]``. + - Support is currently limited to the following binary operators: ``+``, ``-``, ``*``, ``/``, ``%``, ``^``, ``&``, ``|``, ``=``, ``<``, ``>``, ``+=``, ``-=``, ``*=``, ``/=``, ``%=``, ``^=``, ``&=``, ``|=``, ``<<``, ``>>``, ``>>=``, ``<<=``, ``==``, ``!=``, ``<=``, ``>=``, ``<=>``, ``&&``, ``||``, and to the array indexing operator ``[]``. enums @@ -416,6 +416,55 @@ When C++ code has a pointer to a ``Base`` instance and calls ``virtualFunc()``, - You *must* implement all the methods you mentioned in the IDL of the ``JSImplementation`` class (``ImplJS``) or compilation will fail with an error. - You will also need to provide an interface definition for the ``Base`` class in the IDL file. +Function overloads +================== + +C++ allows function overloads, where multiple member functions have the same name but different arguments. By default, the *WebIDL Binder* allows you to bind overloaded functions if they differ only in the number of arguments: + +.. code-block:: cpp + + // C++ + class OverloadTest { + public: + void test(int arg1, int arg2) { ... } + void test(int arg) { ... } + }; + +.. code-block:: idl + + // WebIDL + interface OverloadTest { + void OverloadTest(); + void test(long arg1, long arg2); + void test(long arg); + }; + +If your overloaded functions differ in some other way (say, in the types) then you can use the ``[BindTo]`` attribute to tell the tool what function name to bind to (that is, to call): + +.. code-block:: cpp + + // C++ + class BindToTest { + public: + void test(const char* arg) { ... } + void test(int arg) { ... } + }; + +.. code-block:: idl + + // WebIDL + interface BindToTest { + void BindToTest(); + [BindTo="test"] void testString([Const] DOMString arg); + [BindTo="test"] void testInt(long arg); + }; + +In this case the C++ function ``test(const char*)`` will be named ``testString`` in JavaScript and ``test(int)`` will be named ``testInt``. + +.. note:: + + You can also use ``[BindTo]`` to just rename a function, e.g. if you want to rename ``MyFunctionName`` to ``myFunctionName``. + Pointers and comparisons ========================= diff --git a/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst b/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst index 8d7c725c37f8d..b1b6e030cab38 100644 --- a/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst +++ b/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst @@ -243,11 +243,27 @@ a C++ object is no longer needed and can be deleted: Automatic memory management --------------------------- -JavaScript only gained support for `finalizers`_ in ECMAScript 2021, or ECMA-262 -Edition 12. The new API is called `FinalizationRegistry`_ and it still does not -offer any guarantees that the provided finalization callback will be called. -Embind uses this for cleanup if available, but only for smart pointers, -and only as a last resort. +Embind integrates with the `Explicit Resource Management`_ proposal. + +It allows to automatically delete short-lived C++ objects at the end of the +scope when they're declared with a `using` keyword: + +.. code:: javascript + + using x = new Module.MyClass; + x.method(); + +At the moment of writing, this proposal is natively supported in +Chromium-based browsers as well as Babel and TypeScript via transpilation. + +Embind also supports `finalizers`_, which were added in ECMAScript 2021 under a +`FinalizationRegistry`_ API. Unlike the `using` keyword, finalizers are not +guaranteed to be called, and even if they are, there are no guarantees about +their timing or order of execution, which makes them unsuitable for general +RAII-style resource management. + +Embind uses it for cleanup if available, but only for smart pointers, and only +as a last resort. .. warning:: It is strongly recommended that JavaScript code explicitly deletes any C++ object handles it has received. @@ -597,6 +613,7 @@ implemented in JavaScript. .. code:: cpp struct Interface { + virtual ~Interface() {} virtual void invoke(const std::string& str) = 0; }; @@ -859,7 +876,7 @@ Class properties can be defined several ways as seen below. class_("Person") .constructor<>() // Bind directly to a class member with automatically generated getters/setters using a - // reference return policy so the object does not need to be deleted JS. + // reference return policy so the object does not need to be deleted from JS. .property("location", &Person::location, return_value_policy::reference()) // Same as above, but this will return a copy and the object must be deleted or it will // leak! @@ -1070,7 +1087,7 @@ Out of the box, *embind* provides converters for many standard C++ types: \*\*Requires BigInt support to be enabled with the `-sWASM_BIGINT` flag. For convenience, *embind* provides factory functions to register -``std::vector`` (:cpp:func:`register_vector`), ``std::map`` +``std::vector>`` (:cpp:func:`register_vector`), ``std::map, class Allocator=std::allocator>>`` (:cpp:func:`register_map`), and ``std::optional`` (:cpp:func:`register_optional`) types: .. code:: cpp @@ -1078,7 +1095,7 @@ For convenience, *embind* provides factory functions to register EMSCRIPTEN_BINDINGS(stl_wrappers) { register_vector("VectorInt"); register_map("MapIntInt"); - register_optional("Optional); + register_optional(); } A full example is shown below: @@ -1211,6 +1228,16 @@ registered using :cpp:func:`EMSCRIPTEN_DECLARE_VAL_TYPE` in combination with register_type("(message: string) => void"); } + +``nonnull`` Pointers +-------------------- + +C++ functions that return pointers generate TS definitions with `` | +null`` to allow ``nullptr`` by default. If the C++ function is guaranteed to +return a valid object, then a policy parameter of ``nonnull()`` can be +added to the function binding to omit ``| null`` from TS. This avoids having to +handle the ``null`` case in TS. + Performance =========== @@ -1234,3 +1261,4 @@ real-world applications has proved to be more than acceptable. .. _Making sine, square, sawtooth and triangle waves: http://stuartmemo.com/making-sine-square-sawtooth-and-triangle-waves/ .. _embind_tsgen.cpp: https://github.com/emscripten-core/emscripten/blob/main/test/other/embind_tsgen.cpp .. _embind_tsgen.d.ts: https://github.com/emscripten-core/emscripten/blob/main/test/other/embind_tsgen.d.ts +.. _Explicit Resource Management: https://tc39.es/proposal-explicit-resource-management/ diff --git a/site/source/docs/porting/emscripten-runtime-environment.rst b/site/source/docs/porting/emscripten-runtime-environment.rst index 2a8e41776ba1b..6010e3ce7d203 100644 --- a/site/source/docs/porting/emscripten-runtime-environment.rst +++ b/site/source/docs/porting/emscripten-runtime-environment.rst @@ -73,11 +73,11 @@ Typically you will have a small section with ``#ifdef __EMSCRIPTEN__`` for the t // Our "main loop" function. This callback receives the current time as // reported by the browser, and the user data we provide in the call to // emscripten_request_animation_frame_loop(). - EM_BOOL one_iter(double time, void* userData) { + bool one_iter(double time, void* userData) { // Can render to the screen here, etc. puts("one iteration"); // Return true to keep the loop running. - return EM_TRUE; + return true; } int main() { diff --git a/site/source/docs/porting/exceptions.rst b/site/source/docs/porting/exceptions.rst index 07f003b9328bf..4dffc6fe76264 100644 --- a/site/source/docs/porting/exceptions.rst +++ b/site/source/docs/porting/exceptions.rst @@ -171,3 +171,57 @@ Using Exceptions and setjmp-longjmp Together ============================================ See :ref:`using-exceptions-and-setjmp-longjmp-together`. + + +Limitations regarding std::terminate() +====================================== + + * Currently `std::set_terminate + `_ is NOT supported + when a thrown exception does not have a matching handler and unwinds all the + stack up to the topmost caller and crashes the program, i.e., there is no + ``catch`` that catches it and the callers are not marked as ``noexcept``. + This applies to both Emscripten-style and WebAssembly exceptions. That + functionality requires `two-phase exception handling + `_, which neither + supports. So the following program does NOT print ``my set_terminate``: + + .. code-block:: cpp + + #include + #include + + int main() { + std::set_terminate([] { + std::cerr << "my set_terminate" << std::endl; + std::abort(); + }); + throw 3; + } + + * When the exception handling encounters a termination condition, libc++abi + spec says we call `__cxa_begin_catch()` to mark the exception as handled and + then call `std::terminate()`. But currently Wasm EH does not support calling + `__cxa_begin_catch()`. So the following program prints ``exception_ptr is + null``, where it is supposed to print ``exception_ptr is NOT null``; note + that the use of ``noexcept`` here means that the ``throw 3`` will turn into + a termination condition. + + .. code-block:: cpp + + #include + #include + + int main() noexcept { + std::set_terminate([] { + auto ptr = std::current_exception(); + if (ptr) + std::cerr << "exception_ptr is NOT null" << std::endl; + else + std::cerr << "exception_ptr is null" << std::endl; + std::abort(); + }); + throw 3; + } + + This can possibly be supported in the future. diff --git a/site/source/docs/porting/files/Synchronous-Virtual-XHR-Backed-File-System-Usage.rst b/site/source/docs/porting/files/Synchronous-Virtual-XHR-Backed-File-System-Usage.rst index c8904f3f530e1..af5f07b372dfa 100644 --- a/site/source/docs/porting/files/Synchronous-Virtual-XHR-Backed-File-System-Usage.rst +++ b/site/source/docs/porting/files/Synchronous-Virtual-XHR-Backed-File-System-Usage.rst @@ -63,6 +63,6 @@ Instructions .. include:: ../../../../../test/test_browser.py :literal: - :start-after: create_file('main.html', - :end-before: """ % (worker_filename, self.port)) + :start-after: create_file('main.html', ''' + :end-before: ''' % self.PORT) :code: html diff --git a/site/source/docs/porting/pthreads.rst b/site/source/docs/porting/pthreads.rst index 6f00b3971d6c3..75ec035f35694 100644 --- a/site/source/docs/porting/pthreads.rst +++ b/site/source/docs/porting/pthreads.rst @@ -136,7 +136,7 @@ The Emscripten implementation for the pthreads API should follow the POSIX stand - The Emscripten implementation does also not support multiprocessing via ``fork()`` and ``join()``. -- For web security purposes, there exists a fixed limit (by default 20) of threads that can be spawned when running in Firefox Nightly. `#1052398 `_. To adjust the limit, navigate to about:config and change the value of the pref "dom.workers.maxPerDomain". +- For web security purposes, there exists a fixed limit (by default 20) of threads that can be spawned when running in Firefox Nightly. `#1052398 `_. To adjust the limit, navigate to about:config and change the value of the pref "dom.workers.maxPerDomain". - Some of the features in the pthreads specification are unsupported since the upstream musl library that Emscripten utilizes does not support them, or they are marked optional and a conformant implementation need not support them. Such unsupported features in Emscripten include prioritization of threads, and pthread_rwlock_unlock() is not performed in thread priority order. The functions pthread_mutexattr_set/getprotocol(), pthread_mutexattr_set/getprioceiling() and pthread_attr_set/getscope() are no-ops. @@ -144,7 +144,7 @@ The Emscripten implementation for the pthreads API should follow the POSIX stand - Note that the function emscripten_num_logical_cores() will always return the value of navigator.hardwareConcurrency, i.e. the number of logical cores on the system, even when shared memory is not supported. This means that it is possible for emscripten_num_logical_cores() to return a value greater than 1, while at the same time emscripten_has_threading_support() can return false. The return value of emscripten_has_threading_support() denotes whether the browser has shared memory support available. -- Pthreads + memory growth (``ALLOW_MEMORY_GROWTH``) is especially tricky, see `Wasm design issue #1271 `_. This currently causes JS accessing the Wasm memory to be slow - but this will likely only be noticeable if the JS does large amounts of memory reads and writes (Wasm runs at full speed, so moving work over can fix this). This also requires that your JS be aware that the HEAP* views may need to be updated - JS code embedded with ``--js-library`` etc will automatically be transformed to use the ``GROWABLE_HEAP_*`` helper functions where ``HEAP*`` are used, but external code that uses ``Module.HEAP*`` directly may encounter problems with views being smaller than memory. +- Pthreads + memory growth (``ALLOW_MEMORY_GROWTH``) is especially tricky, see `Wasm design issue #1271 `_. This currently causes JS accessing the Wasm memory to be slow - but this will likely only be noticeable if the JS does large amounts of memory reads and writes (Wasm runs at full speed, so moving work over can fix this). This also requires that your JS be aware that the HEAP* views may need to be updated - JS code embedded with ``--js-library`` etc will automatically be transformed to auto-update memory views before each access, but external code that uses ``Module.HEAP*`` directly may encounter problems with views being smaller than memory. .. _Allocator_performance: diff --git a/site/source/docs/porting/setjmp-longjmp.rst b/site/source/docs/porting/setjmp-longjmp.rst index d0250f4dc6288..3baa72d4b7481 100644 --- a/site/source/docs/porting/setjmp-longjmp.rst +++ b/site/source/docs/porting/setjmp-longjmp.rst @@ -10,7 +10,7 @@ by the ``SUPPORT_LONGJMP`` setting, which can take these values: * ``emscripten``: JavaScript-based support * ``wasm``: WebAssembly exception handling-based support * 0: No support -* 1: Default support, depending on the exception mode. ``wasm`` if ``-fwasm-exception`` is used, ``emscripten`` otherwise. +* 1: Default support, depending on the exception mode. ``wasm`` if ``-fwasm-exceptions`` is used, ``emscripten`` otherwise. If :ref:`native Wasm exceptions ` are used, ``SUPPORT_LONGJMP`` defaults to ``wasm``, and if :ref:`JavaScript-based diff --git a/site/source/docs/porting/simd.rst b/site/source/docs/porting/simd.rst index e7437c5c06109..6409c0811b024 100644 --- a/site/source/docs/porting/simd.rst +++ b/site/source/docs/porting/simd.rst @@ -1,4 +1,4 @@ -.. Porting SIMD code: +.. _Porting SIMD code: .. role:: raw-html(raw) :format: html @@ -12,7 +12,7 @@ Emscripten supports the `WebAssembly SIMD 1. Enable LLVM/Clang SIMD autovectorizer to automatically target WebAssembly SIMD, without requiring changes to C/C++ source code. 2. Write SIMD code using the GCC/Clang SIMD Vector Extensions (``__attribute__((vector_size(16)))``) 3. Write SIMD code using the WebAssembly SIMD intrinsics (``#include ``) -4. Compile existing SIMD code that uses the x86 SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 or 128-bit subset of the AVX intrinsics (``#include <*mmintrin.h>``) +4. Compile existing SIMD code that uses the x86 SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX or AVX2 intrinsics (``#include <*mmintrin.h>``) 5. Compile existing SIMD code that uses the ARM NEON intrinsics (``#include ``) These techniques can be freely combined in a single program. @@ -54,9 +54,9 @@ LLVM maintains a WebAssembly SIMD Intrinsics header file that is provided with E int main() { #ifdef __wasm_simd128__ - v128 v1 = wasm_f32x4_make(1.2f, 3.4f, 5.6f, 7.8f); - v128 v2 = wasm_f32x4_make(2.1f, 4.3f, 6.5f, 8.7f); - v128 v3 = v1 + v2; + v128_t v1 = wasm_f32x4_make(1.2f, 3.4f, 5.6f, 7.8f); + v128_t v2 = wasm_f32x4_make(2.1f, 4.3f, 6.5f, 8.7f); + v128_t v3 = wasm_f32x4_add(v1, v2); // Prints "v3: [3.3, 7.7, 12.1, 16.5]" printf("v3: [%.1f, %.1f, %.1f, %.1f]\n", wasm_f32x4_extract_lane(v3, 0), @@ -97,6 +97,7 @@ When developing SIMD code to use WebAssembly SIMD, implementors should be aware .. list-table:: WebAssembly SIMD instructions with performance implications :widths: 10 10 30 :header-rows: 1 + :class: wrap-table-content * - WebAssembly SIMD instruction - Arch @@ -152,8 +153,9 @@ Emscripten supports compiling existing codebases that use x86 SSE instructions b * **SSE4.1**: pass ``-msse4.1`` and ``#include ``. Use ``#ifdef __SSE4_1__`` to gate code. * **SSE4.2**: pass ``-msse4.2`` and ``#include ``. Use ``#ifdef __SSE4_2__`` to gate code. * **AVX**: pass ``-mavx`` and ``#include ``. Use ``#ifdef __AVX__`` to gate code. +* **AVX2**: pass ``-mavx2`` and ``#include ``. Use ``#ifdef __AVX2__`` to gate code. -Currently only the SSE1, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, and 128-bit AVX instruction sets are supported. Each of these instruction sets add on top of the previous ones, so e.g. when targeting SSE3, the instruction sets SSE1 and SSE2 are also available. +Currently only the SSE1, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, and AVX instruction sets are supported. Each of these instruction sets add on top of the previous ones, so e.g. when targeting SSE3, the instruction sets SSE1 and SSE2 are also available. The following tables highlight the availability and expected performance of different SSE* intrinsics. This can be useful for understanding the performance limitations that the Wasm SIMD specification has when running on x86 hardware. @@ -176,6 +178,7 @@ In addition to consulting the tables below, you can turn on diagnostics for slow .. list-table:: x86 SSE intrinsics available via #include and -msse :widths: 20 30 :header-rows: 1 + :class: wrap-table-content * - Intrinsic name - WebAssembly SIMD support @@ -410,6 +413,7 @@ The following table highlights the availability and expected performance of diff .. list-table:: x86 SSE2 intrinsics available via #include and -msse2 :widths: 20 30 :header-rows: 1 + :class: wrap-table-content * - Intrinsic name - WebAssembly SIMD support @@ -862,6 +866,7 @@ The following table highlights the availability and expected performance of diff .. list-table:: x86 SSE3 intrinsics available via #include and -msse3 :widths: 20 30 :header-rows: 1 + :class: wrap-table-content * - Intrinsic name - WebAssembly SIMD support @@ -901,6 +906,7 @@ The following table highlights the availability and expected performance of diff .. list-table:: x86 SSSE3 intrinsics available via #include and -mssse3 :widths: 20 30 :header-rows: 1 + :class: wrap-table-content * - Intrinsic name - WebAssembly SIMD support @@ -947,6 +953,7 @@ The following table highlights the availability and expected performance of diff .. list-table:: x86 SSE4.1 intrinsics available via #include and -msse4.1 :widths: 20 30 :header-rows: 1 + :class: wrap-table-content * - Intrinsic name - WebAssembly SIMD support @@ -1051,9 +1058,9 @@ The following table highlights the availability and expected performance of diff * - _mm_packus_epi32 - ✅ wasm_u16x8_narrow_i32x4 * - _mm_round_pd - - ✅ wasm_f64x2_ceil/wasm_f64x2_floor/wasm_f64x2_nearest/wasm_f64x2_trunc + - ✅ wasm_f64x2_ceil, wasm_f64x2_floor, wasm_f64x2_nearest, wasm_f64x2_trunc * - _mm_round_ps - - ✅ wasm_f32x4_ceil/wasm_f32x4_floor/wasm_f32x4_nearest/wasm_f32x4_trunc + - ✅ wasm_f32x4_ceil, wasm_f32x4_floor, wasm_f32x4_nearest, wasm_f32x4_trunc * - _mm_round_sd - ⚠️ emulated with a shuffle * - _mm_round_ss @@ -1094,6 +1101,7 @@ The following table highlights the availability and expected performance of diff .. list-table:: x86 AVX intrinsics available via #include and -mavx :widths: 20 30 :header-rows: 1 + :class: wrap-table-content * - Intrinsic name - WebAssembly SIMD support @@ -1136,8 +1144,92 @@ The following table highlights the availability and expected performance of diff * - _mm_testz_ps - 💣 emulated with complex SIMD+scalar sequence -Only the 128-bit wide instructions from AVX instruction set are available. 256-bit wide AVX instructions are not provided. +Only the 128-bit wide instructions from AVX instruction set are listed. The 256-bit wide AVX instructions are emulated by two 128-bit wide instructions. +The following table highlights the availability and expected performance of different AVX2 intrinsics. Refer to `Intel Intrinsics Guide on AVX2 `_. + +.. list-table:: x86 AVX2 intrinsics available via #include and -mavx2 + :widths: 20 30 + :header-rows: 1 + + * - Intrinsic name + - WebAssembly SIMD support + * - _mm_broadcastss_ps + - 💡 emulated with a general shuffle + * - _mm_broadcastsd_pd + - 💡 emulated with a general shuffle + * - _mm_blend_epi32 + - 💡 emulated with a general shuffle + * - _mm_broadcastb_epi8 + - 💡 emulated with a general shuffle + * - _mm_broadcastw_epi16 + - 💡 emulated with a general shuffle + * - _mm_broadcastd_epi32 + - 💡 emulated with a general shuffle + * - _mm_broadcastq_epi64 + - 💡 emulated with a general shuffle + * - _mm256_permutevar8x32_epi32 + - ❌ scalarized + * - _mm256_permute4x64_pd + - 💡 emulated with two general shuffle + * - _mm256_permutevar8x32_ps + - ❌ scalarized + * - _mm256_permute4x64_epi64 + - 💡 emulated with two general shuffle + * - _mm_maskload_epi32 + - ❌ scalarized + * - _mm_maskload_epi64 + - ❌ scalarized + * - _mm_maskstore_epi32 + - ❌ scalarized + * - _mm_maskstore_epi64 + - ❌ scalarized + * - _mm_sllv_epi32 + - ❌ scalarized + * - _mm_sllv_epi64 + - ❌ scalarized + * - _mm_srav_epi32 + - ❌ scalarized + * - _mm_srlv_epi32 + - ❌ scalarized + * - _mm_srlv_epi64 + - ❌ scalarized + * - _mm_mask_i32gather_pd + - ❌ scalarized + * - _mm_mask_i64gather_pd + - ❌ scalarized + * - _mm_mask_i32gather_ps + - ❌ scalarized + * - _mm_mask_i64gather_ps + - ❌ scalarized + * - _mm_mask_i32gather_epi32 + - ❌ scalarized + * - _mm_mask_i64gather_epi32 + - ❌ scalarized + * - _mm_mask_i32gather_epi64 + - ❌ scalarized + * - _mm_mask_i64gather_epi64 + - ❌ scalarized + * - _mm_i32gather_pd + - ❌ scalarized + * - _mm_i64gather_pd + - ❌ scalarized + * - _mm_i32gather_ps + - ❌ scalarized + * - _mm_i64gather_ps + - ❌ scalarized + * - _mm_i32gather_epi32 + - ❌ scalarized + * - _mm_i64gather_epi32 + - ❌ scalarized + * - _mm_i32gather_epi64 + - ❌ scalarized + * - _mm_i64gather_epi64 + - ❌ scalarized + +All the 128-bit wide instructions from AVX2 instruction set are listed. +Only a small part of the 256-bit AVX2 instruction set are listed, most of the +256-bit wide AVX2 instructions are emulated by two 128-bit wide instructions. ====================================================== Compiling SIMD code targeting ARM NEON instruction set @@ -1175,6 +1267,7 @@ status `_ (2.4.4), the open source tool used to create the official Python documentation and many other sites. This is a very mature and stable tool, and was selected for, among other reasons, its support for defining API items and linking to them from code. +The site is built using `Sphinx `_ (7.1.2), the open source tool used to create the official Python documentation and many other sites. This is a very mature and stable tool, and was selected for, among other reasons, its support for defining API items and linking to them from code. The site uses a custom theme, which is based on the :ref:`read-the-docs-theme`. diff --git a/site/source/docs/tools_reference/emcc.rst b/site/source/docs/tools_reference/emcc.rst index f51ad5c02c264..68208354eb2be 100644 --- a/site/source/docs/tools_reference/emcc.rst +++ b/site/source/docs/tools_reference/emcc.rst @@ -54,7 +54,7 @@ Options that are modified or new in *emcc* are listed below: ``-O1`` [compile+link] - Simple optimizations. During the compile step these include LLVM ``-O1`` optimizations. During the link step this does not include various runtime assertions in JS that `-O0` would do. + Simple optimizations. During the compile step these include LLVM ``-O1`` optimizations. During the link step this omits various runtime assertions in JS that `-O0` would include. .. _emcc-O2: @@ -68,7 +68,7 @@ Options that are modified or new in *emcc* are listed below: ``-O3`` [compile+link] - Like ``-O2``, but with additional optimizations that may take longer to run. + Like ``-O2``, but with additional optimizations that may take longer to run and may increase code size. .. note:: This is a good setting for a release build. @@ -76,8 +76,8 @@ Options that are modified or new in *emcc* are listed below: ``-Og`` [compile+link] - Like ``-O1``. In future versions, this option might disable different - optimizations in order to improve debuggability. + Like ``-O1``, with an additional flag to extend the liveness of variables for improved debugging. + In future versions, additional optimizations might also be disabled. .. _emcc-Os: @@ -172,21 +172,32 @@ Options that are modified or new in *emcc* are listed below: .. _emcc-gsource-map: -``-gsource-map`` - [link] +``-gsource-map[=inline]`` + [compile+link] + [same as -g3 if passed at compile time, otherwise applies at link] Generate a source map using LLVM debug information (which must - be present in object files, i.e., they should have been compiled with ``-g``). + be present in object files, i.e., they should have been compiled with ``-g`` + or ``-gsource-map``). + When this option is provided, the **.wasm** file is updated to have a ``sourceMappingURL`` section. The resulting URL will have format: ```` + ```` + ``.map``. ```` defaults to being empty (which means the source map is served from the same directory as the Wasm file). It can be changed using :ref:`--source-map-base `. + Path substitution can be applied to the referenced sources using the + ``-sSOURCE_MAP_PREFIXES`` (:ref:`link `). + If ``inline`` is specified, the sources content is embedded in the source map + (in this case you don't need path substitution, but it comes with the cost of + having a large source map file). + .. _emcc-gN: ``-g`` [compile+link] - Controls the level of debuggability. Each level builds on the previous one: + If used at compile time, adds progressively more DWARF information to the object file, + according to the underlying behavior of clang. + If used at link time, controls the level of debuggability overall. Each level builds on the previous one: - .. _emcc-g0: @@ -196,29 +207,31 @@ Options that are modified or new in *emcc* are listed below: - .. _emcc-g1: - ``-g1``: When linking, preserve whitespace in JavaScript. + ``-g1``: Preserve whitespace in JavaScript. - .. _emcc-g2: - ``-g2``: When linking, preserve function names in compiled code. + ``-g2``: Also preserve function names in compiled code (via the wasm name section). - .. _emcc-g3: - ``-g3``: When compiling to object files, keep debug info, including JS whitespace, function names, and LLVM debug info (DWARF) if any (this is the same as :ref:`-g `). + ``-g3``: Also keep LLVM debug info (DWARF) if there is any in the object files (this is the same as :ref:`-g `). .. _emcc-profiling: ``--profiling`` - [same as -g2 if passed at compile time, otherwise applies at link] - Use reasonable defaults when emitting JavaScript to make the build readable but still useful for profiling. This sets ``-g2`` (preserve whitespace and function names) and may also enable optimizations that affect performance and otherwise might not be performed in ``-g2``. + [link] + Make the output suitable for profiling. This means including function names in the wasm and JS output, and + preserving whitespace in the JS output. It does not affect optimizations (to ensure that performance profiles + reflect production builds). Currenly this is the same as ``-g2``. .. _emcc-profiling-funcs: ``--profiling-funcs`` [link] - Preserve function names in profiling, but otherwise minify whitespace and names as we normally do in optimized builds. This is useful if you want to look at profiler results based on function names, but do *not* intend to read the emitted code. + Preserve wasm function names as in ``--profiling``, but otherwise minify whitespace and names as we normally do in optimized builds. This is useful if you want to look at profiler results based on function names, but do *not* intend to read the emitted code. ``--tracing`` [link] @@ -237,10 +250,20 @@ Options that are modified or new in *emcc* are listed below: Save a map file between function indexes in the Wasm and function names. By storing the names on a file on the side, you can avoid shipping the names, and can still reconstruct meaningful stack traces by translating the indexes back - to the names. + to the names. This is a simpler format than source maps, but less detailed + because it only describes function names and not source locations. .. note:: When used with ``-sWASM=2``, two symbol files are created. ``[name].js.symbols`` (with WASM symbols) and ``[name].wasm.js.symbols`` (with ASM.js symbols) +.. _emcc-emit-minification-map: + +``--emit-minification-map `` + [link] + In cases where emscripten performs import/export minificiton this option can + be used to output a file that maps minified names back to their original + names. The format of this file is single line per import/export of the form + ``:``. + .. _emcc-lto: ``-flto`` @@ -253,7 +276,7 @@ Options that are modified or new in *emcc* are listed below: [link] Runs the :term:`Closure Compiler`. Possible values are: - - ``0``: No closure compiler (default in ``-O2`` and below). + - ``0``: No closure compiler (default). - ``1``: Run closure compiler. This greatly reduces the size of the support JavaScript code (everything but the WebAssembly or asm.js). Note that this increases compile time significantly. - ``2``: Run closure compiler on *all* the emitted code, even on **asm.js** output in **asm.js** mode. This can further reduce code size, but does prevent a significant amount of **asm.js** optimizations, so it is not recommended unless you want to reduce code size at all costs. @@ -261,7 +284,6 @@ Options that are modified or new in *emcc* are listed below: - Consider using ``-sMODULARIZE`` when using closure, as it minifies globals to names that might conflict with others in the global scope. ``MODULARIZE`` puts all the output into a function (see ``src/settings.js``). - Closure will minify the name of `Module` itself, by default! Using ``MODULARIZE`` will solve that as well. Another solution is to make sure a global variable called `Module` already exists before the closure-compiled code runs, because then it will reuse that variable. - - Closure is only run if JavaScript opts are being done (``-O2`` or above). ``--closure-args=`` [link] @@ -558,9 +580,9 @@ Options that are modified or new in *emcc* are listed below: [compile] Tells *emcc* to emit an object file which can then be linked with other object files to produce an executable. -``--output_eol windows|linux`` +``--output-eol windows|linux`` [link] - Specifies the line ending to generate for the text files that are outputted. If "--output_eol windows" is passed, the final output files will have Windows \r\n line endings in them. With "--output_eol linux", the final generated files will be written with Unix \n line endings. + Specifies the line ending to generate for the text files that are outputted. If "--output-eol windows" is passed, the final output files will have Windows ``\r\n`` line endings in them. With "--output-eol linux", the final generated files will be written with Unix ``\n`` line endings. ``--cflags`` [other] diff --git a/site/source/docs/tools_reference/emsdk.rst b/site/source/docs/tools_reference/emsdk.rst index 320a28bf27dcd..3e077332d26c9 100644 --- a/site/source/docs/tools_reference/emsdk.rst +++ b/site/source/docs/tools_reference/emsdk.rst @@ -121,9 +121,11 @@ The following topics explain how to perform both common and advanced maintenance .. _emsdk-get-latest-sdk: -How do I just get the latest SDK? ---------------------------------- -Use the ``update`` argument to fetch the current registry of available tools, and then specify the ``latest`` install target to get the most recent SDK: :: +How do I just get the latest SDK release? +----------------------------------------- + +Use the ``update`` argument to fetch the current registry of available tools, +and then specify the ``latest`` install target to get the most recent SDK: :: # Fetch the latest registry of available tools. ./emsdk update @@ -134,7 +136,25 @@ Use the ``update`` argument to fetch the current registry of available tools, an # Set up the compiler configuration to point to the "latest" SDK. ./emsdk activate latest +How do I install a specific version? +------------------------------------ + +Use the commands above, replacing ``latest`` with the version you want, for +example: :: + + ./emsdk install 4.0.7 + ./emsdk activate 4.0.7 + +(you may need to do ``./emsdk update`` before). + +Each release also has an *asserts version* which is built with more runtime +checks in LLVM and Binaryen. This can be useful if you think you have +encountered a bug in one of these tools. The names of asserts versions are the +same as release versions, with an added suffix of ``-asserts``, e.g., +``4.0.7-asserts``, which you can use with: :: + ./emsdk install 4.0.7-asserts + ./emsdk activate 4.0.7-asserts How do I use emsdk? ------------------- diff --git a/site/source/docs/tools_reference/settings_reference.rst b/site/source/docs/tools_reference/settings_reference.rst index 063ad031f4863..e5a45fdedb64d 100644 --- a/site/source/docs/tools_reference/settings_reference.rst +++ b/site/source/docs/tools_reference/settings_reference.rst @@ -4,13 +4,13 @@ Emscripten Compiler Settings ============================ -The following is a complete list of settings that can be passed -to emscripten via ``-s`` on the command line. For example -``-sASSERTIONS`` or ``-sASSERTIONS=0``. For more details see the -:ref:`emcc ` documentation. +The following is a complete list of settings that can be passed to emscripten +via ``-s`` on the command line. For example ``-sASSERTIONS`` or +``-sASSERTIONS=0``. For more details see the :ref:`emcc ` +documentation. -Unless otherwise noted these settings only apply when linking -and have no effect during compilation. +Unless otherwise noted these settings only apply when linking and have no effect +during compilation. .. Auto-generated by update_settings_docs.py. **DO NOT EDIT** @@ -23,6 +23,7 @@ Whether we should add runtime assertions. This affects both JS and how system libraries are built. ASSERTIONS == 2 gives even more runtime checks, that may be very slow. That includes internal dlmalloc assertions, for example. +ASSERTIONS defaults to 0 in optimized builds (-O1 and above). Default value: 1 @@ -84,16 +85,25 @@ Default value: true EXIT_RUNTIME ============ -If 0, the runtime is not quit when main() completes (allowing code to -run afterwards, for example from the browser main event loop). atexit()s -are also not executed, and we can avoid including code for runtime shutdown, -like flushing the stdio streams. -Set this to 1 if you do want atexit()s or stdio streams to be flushed -on exit. +If 0, support for shutting down the runtime is not emitted into the build. +This means that the program is not quit when main() completes, but execution +will yield to the event loop of the JS environment, allowing event handlers +to run afterwards. If 0, C++ global destructors will not be emitted into the +build either, to save on code size. Calling exit() will throw an unwinding +exception, but will not shut down the runtime. + +Set this to 1 if you do want to retain the ability to shut down the program. +If 1, then completing main() will by default call exit(), unless a refcount +keeps the runtime alive. Call emscripten_exit_with_live_runtime() to finish +main() while keeping the runtime alive. Calling emscripten_force_exit() will +shut down the runtime, invoking atexit()s, and flushing stdio streams. This setting is controlled automatically in STANDALONE_WASM mode: - For a command (has a main function) this is always 1 -- For a reactor (no a main function) this is always 0 +- For a reactor (no main function) this is always 0 + +For more details, see documentation for emscripten_force_exit() and +emscripten_exit_with_live_runtime(). Default value: false @@ -304,8 +314,6 @@ Assumes WASM_BIGINT. .. note:: Applicable during both linking and compilation -.. note:: This is an experimental setting - Default value: 0 .. _initial_table: @@ -428,6 +436,8 @@ function call that uses DataView to enforce LE byte order for HEAP buffer; This makes generated JavaScript run on BE as well as LE machines. (If 0, only LE systems are supported). Does not affect generated wasm. +.. note:: This is an experimental setting + Default value: false .. _safe_heap: @@ -468,7 +478,8 @@ sent value to memory and loaded the received type from the same memory (using truncs/extends/ reinterprets). This means that when types do not match the emulated values may not match (this is true of native too, for that matter - this is all undefined behavior). This approaches appears good enough to -support Python, which is the main use case motivating this feature. +support Python (the original motiviation for this feature) and Glib (the +continued motivation). Default value: false @@ -481,17 +492,6 @@ Print out exceptions in emscriptened code. Default value: false -.. _demangle_support: - -DEMANGLE_SUPPORT -================ - -If 1, export `demangle` and `stackTrace` JS library functions. - -.. note:: This setting is deprecated - -Default value: false - .. _library_debug: LIBRARY_DEBUG @@ -891,7 +891,12 @@ Default value: false USE_WEBGPU ========== -Enables support for WebGPU (via "webgpu/webgpu.h"). +Enables the built-in implementation of ````. +Deprecated: Please try migrating to ``--use-port=emdawnwebgpu``, +which implements a newer, incompatible version of webgpu.h (see +tools/ports/emdawnwebgpu.py for more info). + +.. note:: This setting is deprecated Default value: false @@ -916,7 +921,7 @@ GL_DISABLE_HALF_FLOAT_EXTENSION_IF_BROKEN From Safari 8 (where WebGL was introduced to Safari) onwards, OES_texture_half_float and OES_texture_half_float_linear extensions are broken and do not function correctly, when used as source textures. -See https://bugs.webkit.org/show_bug.cgi?id=183321, https://bugs.webkit.org/show_bug.cgi?id=169999, +See https://webkit.org/b/183321, https://webkit.org/b/169999, https://stackoverflow.com/questions/54248633/cannot-create-half-float-oes-texture-from-uint16array-on-ipad Default value: false @@ -928,7 +933,7 @@ GL_WORKAROUND_SAFARI_GETCONTEXT_BUG Workaround Safari WebGL issue: After successfully acquiring WebGL context on a canvas, calling .getContext() will always return that context independent of which 'webgl' or 'webgl2' -context version was passed. See https://bugs.webkit.org/show_bug.cgi?id=222758 and +context version was passed. See https://webkit.org/b/222758 and https://github.com/emscripten-core/emscripten/issues/13295. Set this to 0 to force-disable the workaround if you know the issue will not affect you. @@ -1017,9 +1022,9 @@ ones we identify at runtime using ``ENVIRONMENT_IS_*``. Specifically: at compile time, there is no runtime behavior change. Note that by default we do not include the 'shell' environment since direct -usage of d8, js, jsc is extremely rare. +usage of d8, spidermonkey and jsc is extremely rare. -Default value: 'web,webview,worker,node' +Default value: ['web', 'webview', 'worker', 'node'] .. _lz4: @@ -1157,18 +1162,19 @@ This option implies EXPORT_EXCEPTION_HANDLING_HELPERS. Default value: false -.. _wasm_exnref: +.. _wasm_legacy_exceptions: -WASM_EXNREF -=========== +WASM_LEGACY_EXCEPTIONS +====================== -Emit instructions for the new Wasm exception handling proposal with exnref, -which was adopted on Oct 2023. The implementation of the new proposal is -still in progress and this feature is currently experimental. +If true, emit instructions for the legacy Wasm exception handling proposal: +https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/legacy/Exceptions.md +If false, emit instructions for the standardized exception handling proposal: +https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md .. note:: Applicable during both linking and compilation -Default value: false +Default value: true .. _nodejs_catch_exit: @@ -1351,16 +1357,6 @@ If enabled will output which functions have been instrumented and why. Default value: false -.. _asyncify_lazy_load_code: - -ASYNCIFY_LAZY_LOAD_CODE -======================= - -Allows lazy code loading: where emscripten_lazy_load_code() is written, we -will pause execution, load the rest of the code, and then resume. - -Default value: false - .. _asyncify_debug: ASYNCIFY_DEBUG @@ -1421,7 +1417,7 @@ A list of imported module functions that will potentially do asynchronous work. The imported function should return a ``Promise`` when doing asynchronous work. -Note when using ``--js-library``, the function can be marked with +Note when using JS library files, the function can be marked with ``_async:: true`` in the library instead of this setting. Default value: [] @@ -1440,17 +1436,6 @@ having "FS" in this list. Default value: [] -.. _extra_exported_runtime_methods: - -EXTRA_EXPORTED_RUNTIME_METHODS -============================== - -Deprecated, use EXPORTED_RUNTIME_METHODS instead. - -.. note:: This setting is deprecated - -Default value: [] - .. _incoming_module_js_api: INCOMING_MODULE_JS_API @@ -1855,20 +1840,6 @@ testing. See test_chunked_synchronous_xhr in runner.py and library.js. Default value: false -.. _headless: - -HEADLESS -======== - -If 1, will include shim code that tries to 'fake' a browser environment, in -order to let you run a browser program (say, using SDL) in the shell. -Obviously nothing is rendered, but this can be useful for benchmarking and -debugging if actual rendering is not the issue. Note that the shim code is -very partial - it is hard to fake a whole browser! - so keep your -expectations low for this to work. - -Default value: false - .. _deterministic: DETERMINISTIC @@ -1891,14 +1862,9 @@ MODULARIZE By default we emit all code in a straightforward way into the output .js file. That means that if you load that in a script tag in a web page, it will use the global scope. With ``MODULARIZE`` set, we instead emit -the code wrapped in a function that returns a promise. The promise is -resolved with the module instance when it is safe to run the compiled code, -similar to the ``onRuntimeInitialized`` callback. You do not need to use the -``onRuntimeInitialized`` callback when using ``MODULARIZE``. - -(If WASM_ASYNC_COMPILATION is off, that is, if compilation is -*synchronous*, then it would not make sense to return a Promise, and instead -the Module object itself is returned, which is ready to be used.) +the code wrapped in an async function. This function returns a promise that +resolves to a module instance once it is safe to run the compiled code +(similar to the ``onRuntimeInitialized`` callback). The default name of the function is ``Module``, but can be changed using the ``EXPORT_NAME`` option. We recommend renaming it to a more typical name for a @@ -1949,6 +1915,22 @@ factory function, you can use --extern-pre-js or --extern-post-js. While intended usage is to add code that is optimized with the rest of the emitted code, allowing better dead code elimination and minification. +Experimental Feature - Instance ES Modules: + +Note this feature is still under active development and is subject to change! + +To enable this feature use -sMODULARIZE=instance. Enabling this mode will +produce an ES module that is a singleton with ES module exports. The +module will export a default value that is an async init function and will +also export named values that correspond to the Wasm exports and runtime +exports. The init function must be called before any of the exports can be +used. An example of using the module is below. + + import init, { foo, bar } from "./my_module.mjs" + await init(optionalArguments); + foo(); + bar(); + Default value: false .. _export_es6: @@ -1963,18 +1945,6 @@ This is implicitly enabled if the output suffix is set to 'mjs'. Default value: false -.. _use_es6_import_meta: - -USE_ES6_IMPORT_META -=================== - -Use the ES6 Module relative import feature 'import.meta.url' -to auto-detect WASM Module path. -It might not be supported on old browsers / toolchains. This setting -may not be disabled when Node.js is targeted (-sENVIRONMENT=*node*). - -Default value: true - .. _export_name: EXPORT_NAME @@ -2172,10 +2142,9 @@ WASM_BIGINT WebAssembly integration with JavaScript BigInt. When enabled we don't need to legalize i64s into pairs of i32s, as the wasm VM will use a BigInt where an -i64 is used. If WASM_BIGINT is present, the default minimum supported browser -versions will be increased to the min version that supports BigInt. +i64 is used. -Default value: false +Default value: true .. _emit_producers_section: @@ -2525,7 +2494,7 @@ Default value: false WASM_WORKERS ============ -If true, enables support for Wasm Workers. Wasm Workers enable applications +Enables support for Wasm Workers. Wasm Workers enable applications to create threads using a lightweight web-specific API that builds on top of Wasm SharedArrayBuffer + Atomics API. [compile+link] - affects user code at compile and system libraries at link. @@ -2542,6 +2511,18 @@ full documentation in site/source/docs/api_reference/wasm_audio_worklets.rst Default value: 0 +.. _audio_worklet_support_audio_params: + +AUDIO_WORKLET_SUPPORT_AUDIO_PARAMS +================================== + +If true, enables utilizing k- and a-rate AudioParams based properties in +Wasm Audio Worklet code. If false, AudioParams are not used. Set to false +for a tiny improvement to code size and AudioWorklet CPU performance when +audio synthesis is synchronized using custom WebAssembly Memory-based means. + +Default value: true + .. _webaudio_debug: WEBAUDIO_DEBUG @@ -2717,11 +2698,14 @@ Default value: 0 TEXTDECODER =========== -Is enabled, use the JavaScript TextDecoder API for string marshalling. -Enabled by default, set this to 0 to disable. +The default value or 1 means the generated code will use TextDecoder if +available and fall back to custom decoder code when not available. If set to 2, we assume TextDecoder is present and usable, and do not emit -any JS code to fall back if it is missing. In single threaded -Oz build modes, -TEXTDECODER defaults to value == 2 to save code size. +any JS code to fall back if it is missing. Setting this zero to avoid even +conditional usage of TextDecoder is no longer supported. +Note: In -Oz builds, the default value of TEXTDECODER is set to 2, to save on +code size (except when AUDIO_WORKLET is specified, or when `shell` is part +of ENVIRONMENT since TextDecoder is not available in those environments). Default value: 1 @@ -2887,7 +2871,8 @@ are desired to work. Pass -sMIN_FIREFOX_VERSION=majorVersion to drop support for Firefox versions older than < majorVersion. Firefox 79 was released on 2020-07-28. MAX_INT (0x7FFFFFFF, or -1) specifies that target is not supported. -Minimum supported value is 34 which was released on 2014-12-01. +Minimum supported value is 68 which was released on 2019-07-09 (see +feature_matrix.py) Default value: 79 @@ -2905,23 +2890,27 @@ bundled with macOS 10.14.0 Mojave. NOTE: Emscripten is unable to produce code that would work in iOS 9.3.5 and older, i.e. iPhone 4s, iPad 2, iPad 3, iPad Mini 1, Pod Touch 5 and older, see https://github.com/emscripten-core/emscripten/pull/7191. +Multithreaded Emscripten code will need Safari 12.2 (iPhone 5s+) at minimum, +with support for DedicatedWorkerGlobalScope.name parameter. MAX_INT (0x7FFFFFFF, or -1) specifies that target is not supported. -Minimum supported value is 90000 which was released in 2015. +Minimum supported value is 120200 which was released on 2019-03-25 (see +feature_matrix.py). -Default value: 140100 +Default value: 150000 .. _min_chrome_version: MIN_CHROME_VERSION ================== -Specifies the oldest version of Chrome. E.g. pass -sMIN_CHROME_VERSION=58 to -drop support for Chrome 57 and older. +Specifies the oldest version of Chrome. E.g. pass -sMIN_CHROME_VERSION=78 to +drop support for Chrome 77 and older. This setting also applies to modern Chromium-based Edge, which shares version numbers with Chrome. Chrome 85 was released on 2020-08-25. MAX_INT (0x7FFFFFFF, or -1) specifies that target is not supported. -Minimum supported value is 32, which was released on 2014-01-04. +Minimum supported value is 74, which was released on 2019-04-23 (see +feature_matrix.py). Default value: 85 @@ -2932,24 +2921,12 @@ MIN_NODE_VERSION Specifies minimum node version to target for the generated code. This is distinct from the minimum version required run the emscripten compiler. -This version aligns with the current Ubuuntu TLS 20.04 (Focal). Version is encoded in MMmmVV, e.g. 181401 denotes Node 18.14.01. -Minimum supported value is 101900, which was released 2020-02-05. +Minimum supported value is 122209, which was released 2022-01-11 (see +feature_matrix.py). This version aligns with the Ubuntu TLS 22.04 (Jammy). Default value: 160000 -.. _support_errno: - -SUPPORT_ERRNO -============= - -Whether we support setting errno from JS library code. -In MINIMAL_RUNTIME builds, this option defaults to 0. - -.. note:: This setting is deprecated - -Default value: true - .. _minimal_runtime: MINIMAL_RUNTIME @@ -2989,6 +2966,7 @@ For large .wasm modules and production environments, this should be set to 1 for faster startup speeds. However this setting is disabled by default since it requires server side configuration and for really small pages there is no observable difference (also has a ~100 byte impact to code size) +This setting is only compatible with html output. Default value: false @@ -3024,7 +3002,7 @@ little bit of code size and performance when catching exceptions. - 0: No setjmp/longjmp handling - 1: Default setjmp/longjmp/handling, depending on the mode of exceptions. - 'wasm' if '-fwasm-exception' is used, 'emscripten' otherwise. + 'wasm' if '-fwasm-exceptions' is used, 'emscripten' otherwise. [compile+link] - at compile time this enables the transformations needed for longjmp support at codegen time, while at link it allows linking in the @@ -3074,19 +3052,6 @@ disable HTML minification altogether. Default value: true -.. _maybe_wasm2js: - -MAYBE_WASM2JS -============= - -Whether we *may* be using wasm2js. This compiles to wasm normally, but lets -you run wasm2js *later* on the wasm, and you can pick between running the -normal wasm or that wasm2js code. For details of how to do that, see the -test_maybe_wasm2js test. This option can be useful for debugging and -bisecting. - -Default value: false - .. _asan_shadow_size: ASAN_SHADOW_SIZE @@ -3098,27 +3063,20 @@ future release. Default value: -1 -.. _use_offset_converter: - -USE_OFFSET_CONVERTER -==================== +.. _source_map_prefixes: -Whether we should use the offset converter. This is needed for older -versions of v8 (<7.7) that does not give the hex module offset into wasm -binary in stack traces, as well as for avoiding using source map entries -across function boundaries. - -Default value: false +SOURCE_MAP_PREFIXES +=================== -.. _load_source_map: +List of path substitutions to apply in the "sources" field of the source map. +Corresponds to the ``--prefix`` option used in ``tools/wasm-sourcemap.py``. +Must be used with ``-gsource-map``. -LOAD_SOURCE_MAP -=============== +This setting allows to map path prefixes to the proper ones so that the final +(possibly relative) URLs point to the correct locations : +``-sSOURCE_MAP_PREFIXES=/old/path=/new/path`` -Whether we should load the WASM source map at runtime. -This is enabled automatically when using -gsource-map with sanitizers. - -Default value: false +Default value: [] .. _default_to_cxx: @@ -3295,7 +3253,9 @@ Default value: true RUNTIME_DEBUG ============= -If true, add tracing to core runtime functions. +If non-zero, add tracing to core runtime functions. Can be set to 2 for +extra tracing (for example, tracing that occurs on each turn of the event +loop or each user callback, which can flood the console). This setting is enabled by default if any of the following debugging settings are enabled: - PTHREADS_DEBUG @@ -3309,7 +3269,7 @@ are enabled: - SOCKET_DEBUG - FETCH_DEBUG -Default value: false +Default value: 0 .. _legacy_runtime: @@ -3335,3 +3295,63 @@ Use _ for non-pointer arguments, p for pointer/i53 arguments, and P for optional Example use -sSIGNATURE_CONVERSIONS=someFunction:_p,anotherFunction:p Default value: [] + +.. _source_phase_imports: + +SOURCE_PHASE_IMPORTS +==================== + +Experimental support for wasm source phase imports. +This is only currently implemented in the pre-release/nightly version of node, +and not yet supported by browsers. +Requires EXPORT_ES6 + +Default value: false + +.. _wasm_esm_integration: + +WASM_ESM_INTEGRATION +==================== + +Experimental support for wasm ESM integration. +Requires EXPORT_ES6 and MODULARIZE=instance + +Default value: false + +.. _js_base64_api: + +JS_BASE64_API +============= + +Enable use of the JS arraybuffer-base64 API: +https://github.com/tc39/proposal-arraybuffer-base64 +To run the resulting code currently requires passing `--js_base_64` to node +or chrome. + +.. note:: This is an experimental setting + +Default value: false + +.. _growable_arraybuffers: + +GROWABLE_ARRAYBUFFERS +===================== + +Enable support for GrowableSharedArrayBuffer. +This features is only available behind a flag in recent versions of +node/chrome. + +.. note:: This is an experimental setting + +Default value: false + +.. _wasm_js_types: + +WASM_JS_TYPES +============= + +Experimental support for WebAssembly js-types proposal. +It's currently only available under a flag in certain browsers, +so we disable it by default to save on code size. + +Default value: false diff --git a/site/source/get_api_items.py b/site/source/get_api_items.py index ee94f6909e6d3..0a7db796bd284 100755 --- a/site/source/get_api_items.py +++ b/site/source/get_api_items.py @@ -22,7 +22,7 @@ # if you change here, change everywhere. api_item_filename = 'api_items.py' -api_reference_items = dict() +api_reference_items = {} def parseFiles(): @@ -65,7 +65,7 @@ def addapiitems(matchobj): filepath = api_reference_directory + file print(file) # open file - with open(filepath, 'r') as infile: + with open(filepath) as infile: for line in infile: # parse line for API items re.sub(r'^\.\.\s+((\w+)\:(\w+)\:\:(.*))', addapiitems, line) @@ -78,9 +78,7 @@ def exportItems(): # write function lead in infile.write("# Auto-generated file (see get_api_items.py)\n\ndef get_mapped_items():\n mapped_wiki_inline_code = dict()\n") - items = list((key, value) for key, value in api_reference_items.items()) - items.sort() - for key, value in items: + for key, value in sorted(api_reference_items.items()): # Write out each API item to add infile.write(" mapped_wiki_inline_code['%s'] = '%s'\n" % (key, value)) diff --git a/site/source/get_wiki.py b/site/source/get_wiki.py index 899531541f222..9d25a481fe5c3 100755 --- a/site/source/get_wiki.py +++ b/site/source/get_wiki.py @@ -57,7 +57,7 @@ def errorhandler(func, path, exc_info): try: shutil.rmtree(output_dir, ignore_errors=False, onerror=errorhandler) print('Old wiki clone removed') - except IOError: + except OSError: print('No directory to clean found') diff --git a/site/source/index.rst b/site/source/index.rst index 3c298717a6d30..9578868a536fd 100644 --- a/site/source/index.rst +++ b/site/source/index.rst @@ -31,3 +31,4 @@ docs/optimizing/Profiling-Toolchain docs/site/about + genindex diff --git a/src/Fetch.js b/src/Fetch.js index 27fa8a90f6aaa..03203d0f53af5 100644 --- a/src/Fetch.js +++ b/src/Fetch.js @@ -18,56 +18,55 @@ var Fetch = { // dbInstance: undefined, #if FETCH_SUPPORT_INDEXEDDB - // Be cautious that `onerror` may be run synchronously - openDatabase(dbname, dbversion, onsuccess, onerror) { - try { + async openDatabase(dbname, dbversion) { + return new Promise((resolve, reject) => { + try { #if FETCH_DEBUG - dbg(`fetch: indexedDB.open(dbname="${dbname}", dbversion="${dbversion}");`); + dbg(`fetch: indexedDB.open(dbname="${dbname}", dbversion="${dbversion}");`); #endif - var openRequest = indexedDB.open(dbname, dbversion); - } catch (e) { - return onerror(e); - } + var openRequest = indexedDB.open(dbname, dbversion); + } catch (e) { + return reject(e); + } - openRequest.onupgradeneeded = (event) => { + openRequest.onupgradeneeded = (event) => { #if FETCH_DEBUG - dbg('fetch: IndexedDB upgrade needed. Clearing database.'); + dbg('fetch: IndexedDB upgrade needed. Clearing database.'); #endif - var db = /** @type {IDBDatabase} */ (event.target.result); - if (db.objectStoreNames.contains('FILES')) { - db.deleteObjectStore('FILES'); - } - db.createObjectStore('FILES'); - }; - openRequest.onsuccess = (event) => onsuccess(event.target.result); - openRequest.onerror = onerror; + var db = /** @type {IDBDatabase} */ (event.target.result); + if (db.objectStoreNames.contains('FILES')) { + db.deleteObjectStore('FILES'); + } + db.createObjectStore('FILES'); + }; + openRequest.onsuccess = (event) => resolve(event.target.result); + openRequest.onerror = reject; + }); }, #endif - init() { + async init() { Fetch.xhrs = new HandleAllocator(); #if FETCH_SUPPORT_INDEXEDDB #if PTHREADS if (ENVIRONMENT_IS_PTHREAD) return; #endif - var onsuccess = (db) => { + + addRunDependency('library_fetch_init'); + try { + var db = await Fetch.openDatabase('emscripten_filesystem', 1); #if FETCH_DEBUG dbg('fetch: IndexedDB successfully opened.'); #endif Fetch.dbInstance = db; - removeRunDependency('library_fetch_init'); - }; - - var onerror = () => { + } catch (e) { #if FETCH_DEBUG dbg('fetch: IndexedDB open failed.'); #endif Fetch.dbInstance = false; + } finally { removeRunDependency('library_fetch_init'); - }; - - addRunDependency('library_fetch_init'); - Fetch.openDatabase('emscripten_filesystem', 1, onsuccess, onerror); + } #endif // ~FETCH_SUPPORT_INDEXEDDB } } @@ -244,7 +243,7 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) { #if FETCH_DEBUG dbg('fetch: XHR failed, no URL specified!'); #endif - onerror(fetch, 0, 'no url specified!'); + onerror(fetch, 'no url specified!'); return; } var url_ = UTF8ToString(url); @@ -345,6 +344,12 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) { {{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.readyState, 'xhr.readyState', 'i16') }}} {{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.status, 'xhr.status', 'i16') }}} if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + {{{ C_STRUCTS.emscripten_fetch_t.statusText }}}, 64); + if (fetchAttrSynchronous) { + // The response url pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is + // freed when emscripten_fetch_close() is called. + var ruPtr = stringToNewUTF8(xhr.responseURL); + {{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.responseUrl, 'ruPtr', '*') }}} + } } xhr.onload = (e) => { @@ -357,12 +362,12 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) { #if FETCH_DEBUG dbg(`fetch: xhr of URL "${xhr.url_}" / responseURL "${xhr.responseURL}" succeeded with status ${xhr.status}`); #endif - onsuccess?.(fetch, xhr, e); + onsuccess(fetch, xhr, e); } else { #if FETCH_DEBUG dbg(`fetch: xhr of URL "${xhr.url_}" / responseURL "${xhr.responseURL}" failed with status ${xhr.status}`); #endif - onerror?.(fetch, xhr, e); + onerror(fetch, e); } }; xhr.onerror = (e) => { @@ -374,7 +379,7 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) { dbg(`fetch: xhr of URL "${xhr.url_}" / responseURL "${xhr.responseURL}" finished with error, readyState ${xhr.readyState} and status ${xhr.status}`); #endif saveResponseAndStatus(); - onerror?.(fetch, xhr, e); + onerror(fetch, e); }; xhr.ontimeout = (e) => { // check if xhr was aborted by user and don't try to call back @@ -384,7 +389,7 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) { #if FETCH_DEBUG dbg(`fetch: xhr of URL "${xhr.url_}" / responseURL "${xhr.responseURL}" timed out, readyState ${xhr.readyState} and status ${xhr.status}`); #endif - onerror?.(fetch, xhr, e); + onerror(fetch, e); }; xhr.onprogress = (e) => { // check if xhr was aborted by user and don't try to call back @@ -409,14 +414,13 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) { writeI53ToI64(fetch + {{{ C_STRUCTS.emscripten_fetch_t.dataOffset }}}, e.loaded - ptrLen); writeI53ToI64(fetch + {{{ C_STRUCTS.emscripten_fetch_t.totalBytes }}}, e.total); {{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.readyState, 'xhr.readyState', 'i16') }}} + var status = xhr.status; // If loading files from a source that does not give HTTP status code, assume success if we get data bytes - if (xhr.readyState >= 3 && xhr.status === 0 && e.loaded > 0) xhr.status = 200; - {{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.status, 'xhr.status', 'i16') }}} + if (xhr.readyState >= 3 && xhr.status === 0 && e.loaded > 0) status = 200; + {{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.status, 'status', 'i16') }}} if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + {{{ C_STRUCTS.emscripten_fetch_t.statusText }}}, 64); - onprogress?.(fetch, xhr, e); - if (ptr) { - _free(ptr); - } + onprogress(fetch, e); + _free(ptr); }; xhr.onreadystatechange = (e) => { // check if xhr was aborted by user and don't try to call back @@ -428,7 +432,13 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) { if (xhr.readyState >= 2) { {{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.status, 'xhr.status', 'i16') }}} } - onreadystatechange?.(fetch, xhr, e); + if (!fetchAttrSynchronous && (xhr.readyState === 2 && xhr.responseURL.length > 0)) { + // The response url pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is + // freed when emscripten_fetch_close() is called. + var ruPtr = stringToNewUTF8(xhr.responseURL); + {{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.responseUrl, 'ruPtr', '*') }}} + } + onreadystatechange(fetch, e); }; #if FETCH_DEBUG dbg(`fetch: xhr.send(data=${data})`); @@ -439,7 +449,7 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) { #if FETCH_DEBUG dbg(`fetch: xhr failed with exception: ${e}`); #endif - onerror?.(fetch, xhr, e); + onerror(fetch, e); } } @@ -475,14 +485,14 @@ function startFetch(fetch, successcb, errorcb, progresscb, readystatechangecb) { }); }; - var reportProgress = (fetch, xhr, e) => { + var reportProgress = (fetch, e) => { doCallback(() => { if (onprogress) {{{ makeDynCall('vp', 'onprogress') }}}(fetch); else progresscb?.(fetch); }); }; - var reportError = (fetch, xhr, e) => { + var reportError = (fetch, e) => { #if FETCH_DEBUG dbg(`fetch: operation failed: ${e}`); #endif @@ -493,7 +503,7 @@ function startFetch(fetch, successcb, errorcb, progresscb, readystatechangecb) { }); }; - var reportReadyStateChange = (fetch, xhr, e) => { + var reportReadyStateChange = (fetch, e) => { #if FETCH_DEBUG dbg(`fetch: ready state change. e: ${e}`); #endif @@ -574,14 +584,12 @@ function startFetch(fetch, successcb, errorcb, progresscb, readystatechangecb) { } function fetchGetResponseHeadersLength(id) { - return lengthBytesUTF8(Fetch.xhrs.get(id).getAllResponseHeaders()) + 1; + return lengthBytesUTF8(Fetch.xhrs.get(id).getAllResponseHeaders()); } function fetchGetResponseHeaders(id, dst, dstSizeBytes) { var responseHeaders = Fetch.xhrs.get(id).getAllResponseHeaders(); - var lengthBytes = lengthBytesUTF8(responseHeaders) + 1; - stringToUTF8(responseHeaders, dst, dstSizeBytes); - return Math.min(lengthBytes, dstSizeBytes); + return stringToUTF8(responseHeaders, dst, dstSizeBytes) + 1; } //Delete the xhr JS object, allowing it to be garbage collected. diff --git a/src/IDBStore.js b/src/IDBStore.js index a4e9960d0a634..085a912942cd7 100644 --- a/src/IDBStore.js +++ b/src/IDBStore.js @@ -6,11 +6,10 @@ var IDBStore = { indexedDB() { - if (typeof indexedDB != 'undefined') return indexedDB; - var ret = null; - if (typeof window == 'object') ret = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; - assert(ret, 'IDBStore used, but indexedDB not supported'); - return ret; +#if ASSERTIONS + assert(typeof indexedDB != 'undefined', 'IDBStore used, but indexedDB not supported'); +#endif + return indexedDB; }, DB_VERSION: 22, DB_STORE_NAME: 'FILE_DATA', diff --git a/src/URIUtils.js b/src/URIUtils.js deleted file mode 100644 index 6f098fcf9f269..0000000000000 --- a/src/URIUtils.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @license - * Copyright 2017 The Emscripten Authors - * SPDX-License-Identifier: MIT - */ - -// Prefix of data URIs emitted by SINGLE_FILE and related options. -var dataURIPrefix = 'data:application/octet-stream;base64,'; - -/** - * Indicates whether filename is a base64 data URI. - * @noinline - */ -var isDataURI = (filename) => filename.startsWith(dataURIPrefix); - -/** - * Indicates whether filename is delivered via file protocol (as opposed to http/https) - * @noinline - */ -var isFileURI = (filename) => filename.startsWith('file://'); diff --git a/src/arrayUtils.js b/src/arrayUtils.js deleted file mode 100644 index a5f7449078f7d..0000000000000 --- a/src/arrayUtils.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @license - * Copyright 2017 The Emscripten Authors - * SPDX-License-Identifier: MIT - */ - -/** @type {function(string, boolean=, number=)} */ -function 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; -} - -function intArrayToString(array) { - var ret = []; - for (var i = 0; i < array.length; i++) { - var chr = array[i]; - if (chr > 0xFF) { -#if ASSERTIONS - assert(false, `Character code ${chr} (${String.fromCharCode(chr)}) at offset ${i} not in 0x00-0xFF.`); -#endif - chr &= 0xFF; - } - ret.push(String.fromCharCode(chr)); - } - return ret.join(''); -} diff --git a/src/audio_worklet.js b/src/audio_worklet.js index 985f69d2ca083..ab8dfa2d9ef66 100644 --- a/src/audio_worklet.js +++ b/src/audio_worklet.js @@ -1,5 +1,5 @@ // This file is the main bootstrap script for Wasm Audio Worklets loaded in an -// Emscripten application. Build with -sAUDIO_WORKLET=1 linker flag to enable +// Emscripten application. Build with -sAUDIO_WORKLET linker flag to enable // targeting Audio Worklets. // AudioWorkletGlobalScope does not have a onmessage/postMessage() functionality @@ -12,102 +12,231 @@ // the node constructor's "processorOptions" field, we can share the necessary // bootstrap information from the main thread to the AudioWorkletGlobalScope. +#if MINIMAL_RUNTIME +var instantiatePromise; +#endif + +if (ENVIRONMENT_IS_AUDIO_WORKLET) { + +#if AUDIO_WORKLET_SUPPORT_AUDIO_PARAMS function createWasmAudioWorkletProcessor(audioParams) { +#else +function createWasmAudioWorkletProcessor() { +#endif class WasmAudioWorkletProcessor extends AudioWorkletProcessor { constructor(args) { super(); - // Copy needed stack allocation functions from the Module object - // to global scope, these will be accessed in hot paths, so maybe - // they'll be a bit faster to access directly, rather than referencing - // them as properties of the Module object. - globalThis.stackAlloc = Module['stackAlloc']; - globalThis.stackSave = Module['stackSave']; - globalThis.stackRestore = Module['stackRestore']; - globalThis.HEAPU32 = Module['HEAPU32']; - globalThis.HEAPF32 = Module['HEAPF32']; - // Capture the Wasm function callback to invoke. let opts = args.processorOptions; - this.callbackFunction = Module['wasmTable'].get(opts['cb']); - this.userData = opts['ud']; +#if ASSERTIONS + assert(opts.callback) + assert(opts.samplesPerChannel) +#endif + this.callback = {{{ makeDynCall('iipipipp', 'opts.callback') }}}; + this.userData = opts.userData; + // Then the samples per channel to process, fixed for the lifetime of the + // context that created this processor. Even though this 'render quantum + // size' is fixed at 128 samples in the 1.0 spec, it will be variable in + // the 1.1 spec. It's passed in now, just to prove it's settable, but will + // eventually be a property of the AudioWorkletGlobalScope (globalThis). + this.samplesPerChannel = opts.samplesPerChannel; + this.bytesPerChannel = this.samplesPerChannel * {{{ getNativeTypeSize('float') }}}; + + // Prepare the output views; see createOutputViews(). The 'STACK_ALIGN' + // deduction stops the STACK_OVERFLOW_CHECK failing (since the stack will + // be full if we allocate all the available space) leaving room for a + // single AudioSampleFrame as a minumum. There's an arbitrary maximum of + // 64 frames, for the case where a multi-MB stack is passed. + this.outputViews = new Array(Math.min(((wwParams.stackSize - {{{ STACK_ALIGN }}}) / this.bytesPerChannel) | 0, /*sensible limit*/ 64)); +#if ASSERTIONS + console.assert(this.outputViews.length > 0, `AudioWorklet needs more stack allocating (at least ${this.bytesPerChannel})`); +#endif + this.createOutputViews(); + +#if ASSERTIONS + // Explicitly verify this later in process(). Note to self, stackSave is a + // bit of a misnomer as it simply gets the stack address. + this.ctorOldStackPtr = stackSave(); +#endif + } + + /** + * Create up-front as many typed views for marshalling the output data as + * may be required, allocated at the *top* of the worklet's stack (and whose + * addresses are fixed). + */ + createOutputViews() { + // These are still alloc'd to take advantage of the overflow checks, etc. + var oldStackPtr = stackSave(); + var viewDataIdx = {{{ getHeapOffset('stackAlloc(this.outputViews.length * this.bytesPerChannel)', 'float') }}}; +#if WEBAUDIO_DEBUG + console.log(`AudioWorklet creating ${this.outputViews.length} buffer one-time views (for a stack size of ${wwParams.stackSize} at address ${ptrToString(viewDataIdx * 4)})`); +#endif + // Inserted in reverse so the lowest indices are closest to the stack top + for (var n = this.outputViews.length - 1; n >= 0; n--) { + this.outputViews[n] = HEAPF32.subarray(viewDataIdx, viewDataIdx += this.samplesPerChannel); + } + stackRestore(oldStackPtr); } +#if AUDIO_WORKLET_SUPPORT_AUDIO_PARAMS static get parameterDescriptors() { return audioParams; } +#endif + /** + * Marshals all inputs and parameters to the Wasm memory on the thread's + * stack, then performs the wasm audio worklet call, and finally marshals + * audio output data back. + * + * @param {Object} parameters + */ +#if AUDIO_WORKLET_SUPPORT_AUDIO_PARAMS process(inputList, outputList, parameters) { - // Marshal all inputs and parameters to the Wasm memory on the thread stack, - // then perform the wasm audio worklet call, - // and finally marshal audio output data back. - - let numInputs = inputList.length, - numOutputs = outputList.length, - numParams = 0, i, j, k, dataPtr, - stackMemoryNeeded = (numInputs + numOutputs) * 8, - oldStackPtr = stackSave(), - inputsPtr, outputsPtr, outputDataPtr, paramsPtr, - didProduceAudio, paramArray; - - // Calculate how much stack space is needed. - for (i of inputList) stackMemoryNeeded += i.length * 512; - for (i of outputList) stackMemoryNeeded += i.length * 512; - for (i in parameters) stackMemoryNeeded += parameters[i].byteLength + 8, ++numParams; - - // Allocate the necessary stack space. - inputsPtr = stackAlloc(stackMemoryNeeded); - - // Copy input audio descriptor structs and data to Wasm - k = inputsPtr >> 2; - dataPtr = inputsPtr + numInputs * 8; - for (i of inputList) { +#else + /** @suppress {checkTypes} */ + process(inputList, outputList) { +#endif + +#if ALLOW_MEMORY_GROWTH + // Recreate the output views if the heap has changed + // TODO: add support for GROWABLE_ARRAYBUFFERS + if (HEAPF32.buffer != this.outputViews[0].buffer) { + this.createOutputViews(); + } +#endif + + var numInputs = inputList.length; + var numOutputs = outputList.length; + + var entry; // reused list entry or index + var subentry; // reused channel or other array in each list entry or index + + // Calculate the required stack and output buffer views (stack is further + // split into aligned structs and the raw float data). + var stackMemoryStruct = (numInputs + numOutputs) * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}}; + var stackMemoryData = 0; + for (entry of inputList) { + stackMemoryData += entry.length; + } + stackMemoryData *= this.bytesPerChannel; + // Collect the total number of output channels (mapped to array views) + var outputViewsNeeded = 0; + for (entry of outputList) { + outputViewsNeeded += entry.length; + } + stackMemoryData += outputViewsNeeded * this.bytesPerChannel; + var numParams = 0; +#if AUDIO_WORKLET_SUPPORT_AUDIO_PARAMS + for (entry in parameters) { + ++numParams; + stackMemoryStruct += {{{ C_STRUCTS.AudioParamFrame.__size__ }}}; + stackMemoryData += parameters[entry].byteLength; + } +#endif + var oldStackPtr = stackSave(); +#if ASSERTIONS + console.assert(oldStackPtr == this.ctorOldStackPtr, 'AudioWorklet stack address has unexpectedly moved'); + console.assert(outputViewsNeeded <= this.outputViews.length, `Too many AudioWorklet outputs (need ${outputViewsNeeded} but have stack space for ${this.outputViews.length})`); +#endif + + // Allocate the necessary stack space. All pointer variables are in bytes; + // 'structPtr' starts at the first struct entry (all run sequentially) + // and is the working start to each record; 'dataPtr' is the same for the + // audio/params data, starting after *all* the structs. + // 'structPtr' begins 16-byte aligned, allocated from the internal + // _emscripten_stack_alloc(), as are the output views, and so to ensure + // the views fall on the correct addresses (and we finish at stacktop) we + // request additional bytes, taking this alignment into account, then + // offset `dataPtr` by the difference. + var stackMemoryAligned = (stackMemoryStruct + stackMemoryData + 15) & ~15; + var structPtr = stackAlloc(stackMemoryAligned); + var dataPtr = structPtr + (stackMemoryAligned - stackMemoryData); + + // Copy input audio descriptor structs and data to Wasm (recall, structs + // first, audio data after). 'inputsPtr' is the start of the C callback's + // input AudioSampleFrame. + var /*const*/ inputsPtr = structPtr; + for (entry of inputList) { // Write the AudioSampleFrame struct instance - HEAPU32[k++] = i.length; - HEAPU32[k++] = dataPtr; + {{{ makeSetValue('structPtr', C_STRUCTS.AudioSampleFrame.numberOfChannels, 'entry.length', 'u32') }}}; + {{{ makeSetValue('structPtr', C_STRUCTS.AudioSampleFrame.samplesPerChannel, 'this.samplesPerChannel', 'u32') }}}; + {{{ makeSetValue('structPtr', C_STRUCTS.AudioSampleFrame.data, 'dataPtr', '*') }}}; + structPtr += {{{ C_STRUCTS.AudioSampleFrame.__size__ }}}; // Marshal the input audio sample data for each audio channel of this input - for (j of i) { - HEAPF32.set(j, dataPtr>>2); - dataPtr += 512; + for (subentry of entry) { + HEAPF32.set(subentry, {{{ getHeapOffset('dataPtr', 'float') }}}); + dataPtr += this.bytesPerChannel; } } - // Copy output audio descriptor structs to Wasm - outputsPtr = dataPtr; - k = outputsPtr >> 2; - outputDataPtr = (dataPtr += numOutputs * 8) >> 2; - for (i of outputList) { +#if AUDIO_WORKLET_SUPPORT_AUDIO_PARAMS + // Copy parameters descriptor structs and data to Wasm. 'paramsPtr' is the + // start of the C callback's input AudioParamFrame. + var /*const*/ paramsPtr = structPtr; + for (entry = 0; subentry = parameters[entry++];) { + // Write the AudioParamFrame struct instance + {{{ makeSetValue('structPtr', C_STRUCTS.AudioParamFrame.length, 'subentry.length', 'u32') }}}; + {{{ makeSetValue('structPtr', C_STRUCTS.AudioParamFrame.data, 'dataPtr', '*') }}}; + structPtr += {{{ C_STRUCTS.AudioParamFrame.__size__ }}}; + // Marshal the audio parameters array + HEAPF32.set(subentry, {{{ getHeapOffset('dataPtr', 'float') }}}); + dataPtr += subentry.length * {{{ getNativeTypeSize('float') }}}; + } +#else + var paramsPtr = 0; +#endif + + // Copy output audio descriptor structs to Wasm. 'outputsPtr' is the start + // of the C callback's output AudioSampleFrame. 'dataPtr' will now be + // aligned with the output views, ending at stacktop (which is why this + // needs to be last). + var /*const*/ outputsPtr = structPtr; + for (entry of outputList) { // Write the AudioSampleFrame struct instance - HEAPU32[k++] = i.length; - HEAPU32[k++] = dataPtr; - // Reserve space for the output data - dataPtr += 512 * i.length; + {{{ makeSetValue('structPtr', C_STRUCTS.AudioSampleFrame.numberOfChannels, 'entry.length', 'u32') }}}; + {{{ makeSetValue('structPtr', C_STRUCTS.AudioSampleFrame.samplesPerChannel, 'this.samplesPerChannel', 'u32') }}}; + {{{ makeSetValue('structPtr', C_STRUCTS.AudioSampleFrame.data, 'dataPtr', '*') }}}; + structPtr += {{{ C_STRUCTS.AudioSampleFrame.__size__ }}}; + // Advance the output pointer to the next output (matching the pre-allocated views) + dataPtr += this.bytesPerChannel * entry.length; } - // Copy parameters descriptor structs and data to Wasm - paramsPtr = dataPtr; - k = paramsPtr >> 2; - dataPtr += numParams * 8; - for (i = 0; paramArray = parameters[i++];) { - // Write the AudioParamFrame struct instance - HEAPU32[k++] = paramArray.length; - HEAPU32[k++] = dataPtr; - // Marshal the audio parameters array - HEAPF32.set(paramArray, dataPtr>>2); - dataPtr += paramArray.length*4; +#if ASSERTIONS + // If all the maths worked out, we arrived at the original stack address + console.assert(dataPtr == oldStackPtr, `AudioWorklet stack missmatch (audio data finishes at ${dataPtr} instead of ${oldStackPtr})`); + + // Sanity checks. If these trip the most likely cause, beyond unforeseen + // stack shenanigans, is that the 'render quantum size' changed after + // construction (which shouldn't be possible). + if (numOutputs) { + // First that the output view addresses match the stack positions + dataPtr -= this.bytesPerChannel; + for (entry = 0; entry < outputViewsNeeded; entry++) { + console.assert(dataPtr == this.outputViews[entry].byteOffset, 'AudioWorklet internal error in addresses of the output array views'); + dataPtr -= this.bytesPerChannel; + } + // And that the views' size match the passed in output buffers + for (entry of outputList) { + for (subentry of entry) { + console.assert(subentry.byteLength == this.bytesPerChannel, `AudioWorklet unexpected output buffer size (expected ${this.bytesPerChannel} got ${subentry.byteLength})`); + } + } } +#endif // Call out to Wasm callback to perform audio processing - if (didProduceAudio = this.callbackFunction(numInputs, inputsPtr, numOutputs, outputsPtr, numParams, paramsPtr, this.userData)) { + var didProduceAudio = this.callback(numInputs, inputsPtr, numOutputs, outputsPtr, numParams, paramsPtr, this.userData); + if (didProduceAudio) { // Read back the produced audio data to all outputs and their channels. - // (A garbage-free function TypedArray.copy(dstTypedArray, dstOffset, - // srcTypedArray, srcOffset, count) would sure be handy.. but web does - // not have one, so manually copy all bytes in) - for (i of outputList) { - for (j of i) { - for (k = 0; k < 128; ++k) { - j[k] = HEAPF32[outputDataPtr++]; - } + // The preallocated 'outputViews' already have the correct offsets and + // sizes into the stack (recall from createOutputViews() that they run + // backwards). + for (entry of outputList) { + for (subentry of entry) { + subentry.set(this.outputViews[--outputViewsNeeded]); } } } @@ -122,66 +251,32 @@ function createWasmAudioWorkletProcessor(audioParams) { return WasmAudioWorkletProcessor; } +#if MIN_FIREFOX_VERSION < 138 || MIN_CHROME_VERSION != TARGET_NOT_SUPPORTED || MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED +// If this browser does not support the up-to-date AudioWorklet standard +// that has a MessagePort over to the AudioWorklet, then polyfill that by +// a hacky AudioWorkletProcessor that provides the MessagePort. +// Firefox added support in https://hg-edge.mozilla.org/integration/autoland/rev/ab38a1796126f2b3fc06475ffc5a625059af59c1 +// Chrome ticket: https://crbug.com/446920095 +// Safari ticket: https://webkit.org/b/299386 +/** + * @suppress {duplicate, checkTypes} + */ +var port = globalThis.port || {}; + // Specify a worklet processor that will be used to receive messages to this // AudioWorkletGlobalScope. We never connect this initial AudioWorkletProcessor // to the audio graph to do any audio processing. class BootstrapMessages extends AudioWorkletProcessor { constructor(arg) { super(); - // Initialize the global Emscripten Module object that contains e.g. the - // Wasm Module and Memory objects. After this we are ready to load in the - // main application JS script, which the main thread will addModule() - // to this scope. - globalThis.Module = arg['processorOptions']; -#if !MINIMAL_RUNTIME - // Default runtime relies on an injected instantiateWasm() function to - // initialize the Wasm Module. - globalThis.Module['instantiateWasm'] = (info, receiveInstance) => { - var instance = new WebAssembly.Instance(Module['wasm'], info); - receiveInstance(instance, Module['wasm']); - return instance.exports; - }; -#endif -#if WEBAUDIO_DEBUG - console.log('AudioWorklet global scope looks like this:'); - console.dir(globalThis); -#endif + startWasmWorker(arg.processorOptions) // Listen to messages from the main thread. These messages will ask this // scope to create the real AudioWorkletProcessors that call out to Wasm to // do audio processing. - let p = globalThis['messagePort'] = this.port; - p.onmessage = async (msg) => { - let d = msg.data; - if (d['_wpn']) { - // '_wpn' is short for 'Worklet Processor Node', using an identifier - // that will never conflict with user messages -#if MODULARIZE - // Instantiate the MODULARIZEd Module function, which is stored for us - // under the special global name AudioWorkletModule in - // MODULARIZE+AUDIO_WORKLET builds. - if (globalThis.AudioWorkletModule) { - // This populates the Module object with all the Wasm properties - globalThis.Module = await AudioWorkletModule(Module); - // We have now instantiated the Module function, can discard it from - // global scope - delete globalThis.AudioWorkletModule; - } -#endif - // Register a real AudioWorkletProcessor that will actually do audio processing. - registerProcessor(d['_wpn'], createWasmAudioWorkletProcessor(d['audioParams'])); -#if WEBAUDIO_DEBUG - console.log(`Registered a new WasmAudioWorkletProcessor "${d['_wpn']}" with AudioParams: ${d['audioParams']}`); -#endif - // Post a Wasm Call message back telling that we have now registered the - // AudioWorkletProcessor class, and should trigger the user onSuccess - // callback of the - // emscripten_create_wasm_audio_worklet_processor_async() call. - p.postMessage({'_wsc': d['callback'], 'x': [d['contextHandle'], 1/*EM_TRUE*/, d['userData']] }); // "WaSm Call" - } else if (d['_wsc']) { - // '_wsc' is short for 'wasm call', using an identifier that will never - // conflict with user messages - Module['wasmTable'].get(d['_wsc'])(...d['x']); - }; + if (!(port instanceof MessagePort)) { + this.port.onmessage = port.onmessage; + /** @suppress {checkTypes} */ + port = this.port; } } @@ -197,4 +292,47 @@ class BootstrapMessages extends AudioWorkletProcessor { }; // Register the dummy processor that will just receive messages. -registerProcessor('message', BootstrapMessages); +registerProcessor('em-bootstrap', BootstrapMessages); +#endif + +port.onmessage = async (msg) => { +#if MINIMAL_RUNTIME + // Wait for the module instantiation before processing messages. + await instantiatePromise; +#endif + let d = msg.data; + if (d['_boot']) { + startWasmWorker(d); +#if WEBAUDIO_DEBUG + console.log('AudioWorklet global scope looks like this:'); + console.dir(globalThis); +#endif + } else if (d['_wpn']) { + // '_wpn' is short for 'Worklet Processor Node', using an identifier + // that will never conflict with user messages + // Register a real AudioWorkletProcessor that will actually do audio processing. +#if AUDIO_WORKLET_SUPPORT_AUDIO_PARAMS + registerProcessor(d['_wpn'], createWasmAudioWorkletProcessor(d.audioParams)); +#else + registerProcessor(d['_wpn'], createWasmAudioWorkletProcessor()); +#endif +#if WEBAUDIO_DEBUG + console.log(`Registered a new WasmAudioWorkletProcessor "${d['_wpn']}" with AudioParams: ${d.audioParams}`); +#endif + // Post a Wasm Call message back telling that we have now registered the + // AudioWorkletProcessor, and should trigger the user onSuccess callback + // of the emscripten_create_wasm_audio_worklet_processor_async() call. + // + // '_wsc' is short for 'wasm call', using an identifier that will never + // conflict with user messages. + // + // Note: we convert the pointer arg manually here since the call site + // ($_EmAudioDispatchProcessorCallback) is used with various signatures + // and we do not know the types in advance. + port.postMessage({'_wsc': d.callback, args: [d.contextHandle, 1/*EM_TRUE*/, {{{ to64('d.userData') }}}] }); + } else if (d['_wsc']) { + getWasmTableEntry(d['_wsc'])(...d.args); + }; +} + +} // ENVIRONMENT_IS_AUDIO_WORKLET diff --git a/src/base64Decode.js b/src/base64Decode.js deleted file mode 100644 index 46a15327d619e..0000000000000 --- a/src/base64Decode.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @license - * Copyright 2020 The Emscripten Authors - * SPDX-License-Identifier: MIT - */ - -#if WASM2JS && 0 // TODO: Figure out a way to enable this kind of sharing. - -// Binaryen defines the following function if Wasm2JS is being used: -// function base64DecodeToExistingUint8Array(uint8Array, offset, b64); -// so should reuse that when available. However that lives inside the asm module -// for the time being, so cannot access it directly from here. Hence this block -// is disabled atm. - -function base64Decode(b64) { -#if ASSERTIONS - assert(b64.length % 4 == 0); -#endif - return base64DecodeToExistingUint8Array(new Uint8Array(b64.length*3>>2), 0, b64); -} - -#else - -// Precreate a reverse lookup table from chars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" back to bytes to make decoding fast. -for (var base64ReverseLookup = new Uint8Array(123/*'z'+1*/), i = 25; i >= 0; --i) { - base64ReverseLookup[48+i] = 52+i; // '0-9' - base64ReverseLookup[65+i] = i; // 'A-Z' - base64ReverseLookup[97+i] = 26+i; // 'a-z' -} -base64ReverseLookup[43] = 62; // '+' -base64ReverseLookup[47] = 63; // '/' - - -// Decodes a _known valid_ base64 string (without validation) and returns it as a new Uint8Array. -// Benchmarked to be around 5x faster compared to a simple -// "Uint8Array.from(atob(b64), c => c.charCodeAt(0))" (TODO: perhaps use this form in -Oz builds?) -/** @noinline */ -function base64Decode(b64) { -#if ENVIRONMENT_MAY_BE_NODE - if (typeof ENVIRONMENT_IS_NODE != 'undefined' && ENVIRONMENT_IS_NODE) { - var buf = Buffer.from(b64, 'base64'); - return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength); - } -#endif - -#if ASSERTIONS - assert(b64.length % 4 == 0); -#endif - var b1, b2, i = 0, j = 0, bLength = b64.length, output = new Uint8Array((bLength*3>>2) - (b64[bLength-2] == '=') - (b64[bLength-1] == '=')); - for (; i < bLength; i += 4, j += 3) { - b1 = base64ReverseLookup[b64.charCodeAt(i+1)]; - b2 = base64ReverseLookup[b64.charCodeAt(i+2)]; - output[j] = base64ReverseLookup[b64.charCodeAt(i)] << 2 | b1 >> 4; - output[j+1] = b1 << 4 | b2 >> 2; - output[j+2] = b2 << 6 | base64ReverseLookup[b64.charCodeAt(i+3)]; - } - return output; -} - -#endif // ~WASM2JS diff --git a/src/base64Utils.js b/src/base64Utils.js deleted file mode 100644 index 20ab91b154ada..0000000000000 --- a/src/base64Utils.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @license - * Copyright 2017 The Emscripten Authors - * SPDX-License-Identifier: MIT - */ - -#if POLYFILL && (ENVIRONMENT_MAY_BE_SHELL || (ENVIRONMENT_MAY_BE_NODE && MIN_NODE_VERSION < 160000)) -#include "polyfill/atob.js" -#endif - -// Converts a string of base64 into a byte array (Uint8Array). -function intArrayFromBase64(s) { -#if ENVIRONMENT_MAY_BE_NODE - if (typeof ENVIRONMENT_IS_NODE != 'undefined' && ENVIRONMENT_IS_NODE) { - var buf = Buffer.from(s, 'base64'); - return new Uint8Array(buf.buffer, buf.byteOffset, buf.length); - } -#endif - - var decoded = atob(s); - var bytes = new Uint8Array(decoded.length); - for (var i = 0 ; i < decoded.length ; ++i) { - bytes[i] = decoded.charCodeAt(i); - } - return bytes; -} - -// If filename is a base64 data URI, parses and returns data (Buffer on node, -// Uint8Array otherwise). If filename is not a base64 data URI, returns undefined. -function tryParseAsDataURI(filename) { - if (!isDataURI(filename)) { - return; - } - - return intArrayFromBase64(filename.slice(dataURIPrefix.length)); -} diff --git a/src/closure-externs/audio-worklet-externs.js b/src/closure-externs/audio-worklet-externs.js new file mode 100644 index 0000000000000..45ba6da819d9b --- /dev/null +++ b/src/closure-externs/audio-worklet-externs.js @@ -0,0 +1,11 @@ +/* + * AudioWorkletGlobalScope globals + */ +var registerProcessor = function(name, obj) {}; +var currentFrame; +var currentTime; +var sampleRate; +/** + * @suppress {duplicate, checkTypes} + */ +var port; diff --git a/src/closure-externs/closure-externs.js b/src/closure-externs/closure-externs.js index 86495adcc15a4..dbb17a89417aa 100644 --- a/src/closure-externs/closure-externs.js +++ b/src/closure-externs/closure-externs.js @@ -14,16 +14,11 @@ // Special placeholder for `import.meta` and `await import`. var EMSCRIPTEN$IMPORT$META; var EMSCRIPTEN$AWAIT$IMPORT; +var EMSCRIPTEN$AWAIT; // Don't minify createRequire var createRequire; -// Don't minify startWorker which we use to start workers once the runtime is ready. -/** - * @param {Object} Module - */ -var startWorker = function(Module) {}; - // Closure externs used by library_sockfs.js /** @@ -69,6 +64,11 @@ var Atomics = {}; Atomics.compareExchange = function() {}; Atomics.exchange = function() {}; Atomics.wait = function() {}; +/** + * @param {number=} maxWaitMilliseconds + * @suppress {duplicate, checkTypes} + */ +Atomics.waitAsync = function(i32a, index, value, maxWaitMilliseconds) {}; Atomics.notify = function() {}; Atomics.load = function() {}; Atomics.store = function() {}; @@ -108,6 +108,10 @@ WebAssembly.Instance.prototype.exports; * @type {!ArrayBuffer} */ WebAssembly.Memory.prototype.buffer; +/** + * @returns {ArrayBuffer} + */ +WebAssembly.Memory.prototype.toResizableBuffer = function() {}; /** * @type {number} */ @@ -218,14 +222,6 @@ var outerHeight; var event; var devicePixelRatio; -/* - * AudioWorkletGlobalScope globals - */ -var registerProcessor = function(name, obj) {}; -var currentFrame; -var currentTime; -var sampleRate; - /* * Avoid closure minifying anything to "id". See #13965 */ @@ -261,3 +257,14 @@ var moduleRtn; */ Navigator.prototype.webkitGetUserMedia = function( constraints, successCallback, errorCallback) {}; + +/** + * A symbol from the explicit resource management proposal that isn't yet part of Closure. + * @type {symbol} + */ +Symbol.dispose; + +// Common between node-externs and v8-externs +var os = {}; + +AudioWorkletProcessor.parameterDescriptors; diff --git a/src/closure-externs/modularize-externs.js b/src/closure-externs/modularize-externs.js index fd29362e99559..b0fec4a9dc4b0 100644 --- a/src/closure-externs/modularize-externs.js +++ b/src/closure-externs/modularize-externs.js @@ -1,6 +1,7 @@ -// Due to the way MODULARIZE works, Closure is run on generated code that does not define _scriptName, -// but only after MODULARIZE has finished, _scriptName is injected to the generated code. -// Therefore it cannot be minified. +// In MODULARIZE mode the JS code may be executed later, after `document.currentScript` is gone, so we store +// it to `_scriptName` outside the wrapper function. Therefore, it cannot be minified. +// In EXPORT_ES6 mode we use `import.meta.url` and for Node.js CommonJS builds we use `__filename`. + /** * @suppress {duplicate, undefinedVars} */ diff --git a/src/closure-externs/node-externs.js b/src/closure-externs/node-externs.js index 483a4783d8d23..afb0c14d52d94 100644 --- a/src/closure-externs/node-externs.js +++ b/src/closure-externs/node-externs.js @@ -86,6 +86,12 @@ Buffer.from = function(arrayBufferOrString, byteOffsetOrEncoding, length) {}; */ Buffer.alloc = function(size, fill, encoding) {}; +/** + * @return {boolean} + * @nosideeffects + */ +Buffer.isBuffer = function(obj) {}; + /** * @param {number=} start * @param {number=} end @@ -104,3 +110,37 @@ Buffer.prototype.toString = function(encoding, start, end) {}; Worker.prototype.ref = function() {}; Worker.prototype.unref = function() {}; + +/** + * @type {number} + */ +fs.Stats.prototype.atimeMs; + +/** + * @type {number} + */ +fs.Stats.prototype.mtimeMs; + +/** + * @type {number} + */ +fs.Stats.prototype.ctimeMs; + +/** + * @type {number} + */ +fs.Stats.prototype.blksize; + +/** + * @param {string} p + * @return {boolean} + * @nosideeffects + */ +path.isAbsolute; + +/** + * @type {Object.} + */ +path.posix; + +crypto.randomFillSync; diff --git a/src/closure-externs/v8-externs.js b/src/closure-externs/v8-externs.js index 11eda1ddff882..4f602ac2cdc82 100644 --- a/src/closure-externs/v8-externs.js +++ b/src/closure-externs/v8-externs.js @@ -32,3 +32,10 @@ var scriptArgs = []; * @suppress {duplicate} */ var quit = function(status) {}; + +/** + * @param {string} cmd + * @param {Array.=} args + * @return {string} + */ +os.system = function (cmd, args) {}; diff --git a/src/closure-externs/webgpu-externs.js b/src/closure-externs/webgpu-externs.js index 9a855e8e532be..9dc1a6943ed51 100644 --- a/src/closure-externs/webgpu-externs.js +++ b/src/closure-externs/webgpu-externs.js @@ -199,6 +199,8 @@ GPUAdapter.prototype.isFallbackAdapter; GPUAdapter.prototype.requestDevice = function() {}; /** @return {!Promise} */ GPUAdapter.prototype.requestAdapterInfo = function() {}; +/** @type {!GPUAdapterInfo} */ +GPUAdapter.prototype.info; /** @constructor */ function GPUDevice() {} @@ -250,6 +252,8 @@ GPUDevice.prototype.pushErrorScope = function() {}; GPUDevice.prototype.popErrorScope = function() {}; /** @type {!Function} */ GPUDevice.prototype.onuncapturederror; +/** @type {!GPUAdapterInfo} */ +GPUDevice.prototype.adapterInfo; /** @constructor */ function GPUBuffer() {} diff --git a/src/compiler.mjs b/src/compiler.mjs deleted file mode 100755 index 403cd6f73d4c4..0000000000000 --- a/src/compiler.mjs +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env node -/** - * @license - * Copyright 2010 The Emscripten Authors - * SPDX-License-Identifier: MIT - */ - -// LLVM => JavaScript compiler, main entry point - -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import * as url from 'node:url'; - -import {Benchmarker, applySettings, assert, loadSettingsFile, printErr, read} from './utility.mjs'; - -function find(filename) { - assert(filename); - const dirname = url.fileURLToPath(new URL('.', import.meta.url)); - const prefixes = [dirname, process.cwd()]; - for (let i = 0; i < prefixes.length; ++i) { - const combined = path.join(prefixes[i], filename); - if (fs.existsSync(combined)) { - return combined; - } - } - return filename; -} - -// Load default settings -loadSettingsFile(find('settings.js')); -loadSettingsFile(find('settings_internal.js')); - -const argv = process.argv.slice(2); -const symbolsOnlyArg = argv.indexOf('--symbols-only'); -if (symbolsOnlyArg != -1) { - argv.splice(symbolsOnlyArg, 1); -} - -// Load settings from JSON passed on the command line -const settingsFile = argv[0]; -assert(settingsFile); -const user_settings = JSON.parse(read(settingsFile)); -applySettings(user_settings); - -export const symbolsOnly = symbolsOnlyArg != -1; - -// In case compiler.js is run directly (as in gen_sig_info) -// ALL_INCOMING_MODULE_JS_API might not be populated yet. -if (!ALL_INCOMING_MODULE_JS_API.length) { - ALL_INCOMING_MODULE_JS_API = INCOMING_MODULE_JS_API; -} - -EXPORTED_FUNCTIONS = new Set(EXPORTED_FUNCTIONS); -WASM_EXPORTS = new Set(WASM_EXPORTS); -SIDE_MODULE_EXPORTS = new Set(SIDE_MODULE_EXPORTS); -INCOMING_MODULE_JS_API = new Set(INCOMING_MODULE_JS_API); -ALL_INCOMING_MODULE_JS_API = new Set(ALL_INCOMING_MODULE_JS_API); -EXPORTED_RUNTIME_METHODS = new Set(EXPORTED_RUNTIME_METHODS); -WEAK_IMPORTS = new Set(WEAK_IMPORTS); -if (symbolsOnly) { - INCLUDE_FULL_LIBRARY = 1; -} - -// Side modules are pure wasm and have no JS -assert( - !SIDE_MODULE || (ASYNCIFY && symbolsOnly), - 'JS compiler should only run on side modules if asyncify is used.', -); - -// Load compiler code - -// We can't use static import statements here because several of these -// file depend on having the settings defined in the global scope (which -// we do dynamically above. -await import('./modules.mjs'); -await import('./parseTools.mjs'); -if (!STRICT) { - await import('./parseTools_legacy.mjs'); -} -const jsifier = await import('./jsifier.mjs'); - -// =============================== -// Main -// =============================== - -const B = new Benchmarker(); - -try { - jsifier.runJSify(symbolsOnly); - - B.print('glue'); -} catch (err) { - if (err.toString().includes('Aborting compilation due to previous errors')) { - // Compiler failed on user error, don't print the stacktrace in this case. - printErr(err); - } else { - // Compiler failed on internal compiler error! - printErr('Internal compiler error in src/compiler.js!'); - printErr('Please create a bug report at https://github.com/emscripten-core/emscripten/issues/'); - printErr( - 'with a log of the build and the input files used to run. Exception message: "' + - (err.stack || err), - ); - } - - // Work around a node.js bug where stdout buffer is not flushed at process exit: - // Instead of process.exit() directly, wait for stdout flush event. - // See https://github.com/joyent/node/issues/1669 and https://github.com/emscripten-core/emscripten/issues/2582 - // Workaround is based on https://github.com/RReverser/acorn/commit/50ab143cecc9ed71a2d66f78b4aec3bb2e9844f6 - process.stdout.once('drain', () => process.exit(1)); - // Make sure to print something to force the drain event to occur, in case the - // stdout buffer was empty. - console.log(' '); - // Work around another node bug where sometimes 'drain' is never fired - make - // another effort to emit the exit status, after a significant delay (if node - // hasn't fired drain by then, give up) - setTimeout(() => process.exit(1), 500); -} diff --git a/src/cpuprofiler.js b/src/cpuprofiler.js index 473adc96ca79c..a4d36dee5470c 100644 --- a/src/cpuprofiler.js +++ b/src/cpuprofiler.js @@ -225,7 +225,7 @@ var emscriptenCpuProfiler = { if (i != 2) cs += ' <- '; var fn = funcs[i]; var at = fn.indexOf('@'); - if (at != -1) fn = fn.substr(0, at); + if (at != -1) fn = fn.slice(0, at); fn = fn.trim(); cs += '"' + fn + '"'; } @@ -593,7 +593,7 @@ var emscriptenCpuProfiler = { detectWebGLContext() { if (Module['canvas']?.GLctxObject?.GLctx) return Module['canvas'].GLctxObject.GLctx; else if (typeof GLctx != 'undefined') return GLctx; - else if (Module.ctx) return Module.ctx; + else if (Module['ctx']) return Module['ctx']; return null; }, @@ -655,7 +655,7 @@ var emscriptenCpuProfiler = { case 9: glCtx[f] = (a1, a2, a3, a4, a5, a6, a7, a8, a9) => { this.enterSection(section); var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7, a8, a9); this.endSection(section); return ret; }; break; case 10: glCtx[f] = (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) => { this.enterSection(section); var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); this.endSection(section); return ret; }; break; case 11: glCtx[f] = (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) => { this.enterSection(section); var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); this.endSection(section); return ret; }; break; - default: throw 'hookWebGL failed! Unexpected length ' + glCtx[realf].length; + default: throw new Error('hookWebGL failed! Unexpected length ' + glCtx[realf].length); } }, @@ -756,7 +756,7 @@ function cpuprofiler_add_hooks() { emscriptenCpuProfiler.initialize(); } -if (typeof document != 'undefined') { +if (globalThis.document) { emscriptenCpuProfiler.initialize(); } diff --git a/src/deterministic.js b/src/deterministic.js index 4a99e37487b9b..88d5d35e11c8e 100644 --- a/src/deterministic.js +++ b/src/deterministic.js @@ -20,7 +20,7 @@ Date.now = deterministicNow; // Setting performance.now to deterministicNow doesn't work so we instead // use a helper function in parseTools (getPerformanceNow()) to call it // directly. -// if (typeof performance == 'object') performance.now = Date.now; +// if (globalThis.performance) performance.now = Date.now; Module['thisProgram'] = 'thisProgram'; // for consistency between different builds than between runs of the same build diff --git a/src/embind/embind.js b/src/embind/embind.js deleted file mode 100644 index 876cb4761adc7..0000000000000 --- a/src/embind/embind.js +++ /dev/null @@ -1,2362 +0,0 @@ -// Copyright 2012 The Emscripten Authors. All rights reserved. -// Emscripten is available under two separate licenses, the MIT license and the -// University of Illinois/NCSA Open Source License. Both these licenses can be -// found in the LICENSE file. - -/*global addToLibrary*/ - -/*global Module, asm*/ -/*global _malloc, _free, _memcpy*/ -/*global FUNCTION_TABLE, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64*/ -/*global readLatin1String*/ -/*global Emval, emval_handle_array, __emval_decref*/ -/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ - -// -- jshint doesn't understand library syntax, so we need to specifically tell it about the symbols we define -/*global typeDependencies, flushPendingDeletes, getTypeName, getBasestPointer, throwBindingError, UnboundTypeError, embindRepr, registeredInstances, registeredTypes*/ -/*global ensureOverloadTable, embind__requireFunction, awaitingDependencies, makeLegalFunctionName, embind_charCodes:true, registerType, createNamedFunction, RegisteredPointer, throwInternalError*/ -/*global floatReadValueFromPointer, integerReadValueFromPointer, enumReadValueFromPointer, replacePublicSymbol, craftInvokerFunction, tupleRegistrations*/ -/*global finalizationRegistry, attachFinalizer, detachFinalizer, releaseClassHandle, runDestructor*/ -/*global ClassHandle, makeClassHandle, structRegistrations, whenDependentTypesAreResolved, BindingError, deletionQueue, delayFunction:true, upcastPointer*/ -/*global exposePublicSymbol, heap32VectorToArray, newFunc, char_0, char_9*/ -/*global getInheritedInstanceCount, getLiveInheritedInstances, setDelayFunction, InternalError, runDestructors*/ -/*global requireRegisteredType, unregisterInheritedInstance, registerInheritedInstance, PureVirtualError, throwUnboundTypeError*/ -/*global assert, validateThis, downcastPointer, registeredPointers, RegisteredClass, getInheritedInstance */ -/*global throwInstanceAlreadyDeleted, shallowCopyInternalPointer*/ -/*global RegisteredPointer_fromWireType, constNoSmartPtrRawPointerToWireType, nonConstNoSmartPtrRawPointerToWireType, genericPointerToWireType*/ - -#include "embind/embind_shared.js" - -var LibraryEmbind = { - $UnboundTypeError__postset: "UnboundTypeError = Module['UnboundTypeError'] = extendError(Error, 'UnboundTypeError');", - $UnboundTypeError__deps: ['$extendError'], - $UnboundTypeError: undefined, - $PureVirtualError__postset: "PureVirtualError = Module['PureVirtualError'] = extendError(Error, 'PureVirtualError');", - $PureVirtualError__deps: ['$extendError'], - $PureVirtualError: undefined, - $GenericWireTypeSize: {{{ 2 * POINTER_SIZE }}}, -#if EMBIND_AOT - $InvokerFunctions: '<<< EMBIND_AOT_OUTPUT >>>', -#endif - // If register_type is used, emval will be registered multiple times for - // different type id's, but only a single type object is needed on the JS side - // for all of them. Store the type for reuse. - $EmValType__deps: ['_emval_decref', '$Emval', '$readPointer', '$GenericWireTypeSize'], - $EmValType: `{ - name: 'emscripten::val', - 'fromWireType': (handle) => { - var rv = Emval.toValue(handle); - __emval_decref(handle); - return rv; - }, - 'toWireType': (destructors, value) => Emval.toHandle(value), - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': readPointer, - destructorFunction: null, // This type does not need a destructor - - // TODO: do we need a deleteObject here? write a test where - // emval is passed into JS via an interface - }`, - $init_embind__deps: [ - '$getInheritedInstanceCount', '$getLiveInheritedInstances', - '$flushPendingDeletes', '$setDelayFunction'], - $init_embind__postset: 'init_embind();', - $init_embind: () => { - Module['getInheritedInstanceCount'] = getInheritedInstanceCount; - Module['getLiveInheritedInstances'] = getLiveInheritedInstances; - Module['flushPendingDeletes'] = flushPendingDeletes; - Module['setDelayFunction'] = setDelayFunction; - }, - - $throwUnboundTypeError__deps: ['$registeredTypes', '$typeDependencies', '$UnboundTypeError', '$getTypeName'], - $throwUnboundTypeError: (message, types) => { - var unboundTypes = []; - var seen = {}; - function visit(type) { - if (seen[type]) { - return; - } - if (registeredTypes[type]) { - return; - } - if (typeDependencies[type]) { - typeDependencies[type].forEach(visit); - return; - } - unboundTypes.push(type); - seen[type] = true; - } - types.forEach(visit); - - throw new UnboundTypeError(`${message}: ` + unboundTypes.map(getTypeName).join([', '])); - }, - - // Creates a function overload resolution table to the given method 'methodName' in the given prototype, - // if the overload table doesn't yet exist. - $ensureOverloadTable__deps: ['$throwBindingError'], - $ensureOverloadTable: (proto, methodName, humanName) => { - if (undefined === proto[methodName].overloadTable) { - var prevFunc = proto[methodName]; - // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. - proto[methodName] = function(...args) { - // TODO This check can be removed in -O3 level "unsafe" optimizations. - if (!proto[methodName].overloadTable.hasOwnProperty(args.length)) { - throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`); - } - return proto[methodName].overloadTable[args.length].apply(this, args); - }; - // Move the previous function into the overload table. - proto[methodName].overloadTable = []; - proto[methodName].overloadTable[prevFunc.argCount] = prevFunc; - } - }, - - /* - Registers a symbol (function, class, enum, ...) as part of the Module JS object so that - hand-written code is able to access that symbol via 'Module.name'. - name: The name of the symbol that's being exposed. - value: The object itself to expose (function, class, ...) - numArguments: For functions, specifies the number of arguments the function takes in. For other types, unused and undefined. - - To implement support for multiple overloads of a function, an 'overload selector' function is used. That selector function chooses - the appropriate overload to call from an function overload table. This selector function is only used if multiple overloads are - actually registered, since it carries a slight performance penalty. */ - $exposePublicSymbol__deps: ['$ensureOverloadTable', '$throwBindingError'], - $exposePublicSymbol__docs: '/** @param {number=} numArguments */', - $exposePublicSymbol: (name, value, numArguments) => { - if (Module.hasOwnProperty(name)) { - if (undefined === numArguments || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments])) { - throwBindingError(`Cannot register public name '${name}' twice`); - } - - // We are exposing a function with the same name as an existing function. Create an overload table and a function selector - // that routes between the two. - ensureOverloadTable(Module, name, name); - if (Module.hasOwnProperty(numArguments)) { - throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`); - } - // Add the new function into the overload table. - Module[name].overloadTable[numArguments] = value; - } - else { - Module[name] = value; - if (undefined !== numArguments) { - Module[name].numArguments = numArguments; - } - } - }, - - $replacePublicSymbol__deps: ['$throwInternalError'], - $replacePublicSymbol__docs: '/** @param {number=} numArguments */', - $replacePublicSymbol: (name, value, numArguments) => { - if (!Module.hasOwnProperty(name)) { - throwInternalError('Replacing nonexistent public symbol'); - } - // If there's an overload table for this symbol, replace the symbol in the overload table instead. - if (undefined !== Module[name].overloadTable && undefined !== numArguments) { - Module[name].overloadTable[numArguments] = value; - } - else { - Module[name] = value; - Module[name].argCount = numArguments; - } - }, - - // from https://github.com/imvu/imvujs/blob/master/src/error.js - $extendError__deps: ['$createNamedFunction'], - $extendError: (baseErrorType, errorName) => { - var errorClass = createNamedFunction(errorName, function(message) { - this.name = errorName; - this.message = message; - - var stack = (new Error(message)).stack; - if (stack !== undefined) { - this.stack = this.toString() + '\n' + - stack.replace(/^Error(:[^\n]*)?\n/, ''); - } - }); - errorClass.prototype = Object.create(baseErrorType.prototype); - errorClass.prototype.constructor = errorClass; - errorClass.prototype.toString = function() { - if (this.message === undefined) { - return this.name; - } else { - return `${this.name}: ${this.message}`; - } - }; - - return errorClass; - }, - - $createNamedFunction: (name, body) => Object.defineProperty(body, 'name', { - value: name - }), - // All browsers that support WebAssembly also support configurable function name, - // but we might be building for very old browsers via WASM2JS. -#if MIN_CHROME_VERSION < 43 || MIN_SAFARI_VERSION < 100101 || MIN_FIREFOX_VERSION < 38 - // In that case, check if configurable function name is supported at init time - // and, if not, replace with a fallback that returns function as-is as those browsers - // don't support other methods either. - $createNamedFunction__postset: ` - if (!Object.getOwnPropertyDescriptor(Function.prototype, 'name').configurable) { - createNamedFunction = (name, body) => body; - } - `, -#endif - - $embindRepr: (v) => { - if (v === null) { - return 'null'; - } - var t = typeof v; - if (t === 'object' || t === 'array' || t === 'function') { - return v.toString(); - } else { - return '' + v; - } - }, - - // raw pointer -> instance - $registeredInstances__deps: ['$init_embind'], - $registeredInstances: {}, - - $getBasestPointer__deps: ['$throwBindingError'], - $getBasestPointer: (class_, ptr) => { - if (ptr === undefined) { - throwBindingError('ptr should not be undefined'); - } - while (class_.baseClass) { - ptr = class_.upcast(ptr); - class_ = class_.baseClass; - } - return ptr; - }, - - $registerInheritedInstance__deps: ['$registeredInstances', '$getBasestPointer', '$throwBindingError'], - $registerInheritedInstance: (class_, ptr, instance) => { - ptr = getBasestPointer(class_, ptr); - if (registeredInstances.hasOwnProperty(ptr)) { - throwBindingError(`Tried to register registered instance: ${ptr}`); - } else { - registeredInstances[ptr] = instance; - } - }, - - $unregisterInheritedInstance__deps: ['$registeredInstances', '$getBasestPointer', '$throwBindingError'], - $unregisterInheritedInstance: (class_, ptr) => { - ptr = getBasestPointer(class_, ptr); - if (registeredInstances.hasOwnProperty(ptr)) { - delete registeredInstances[ptr]; - } else { - throwBindingError(`Tried to unregister unregistered instance: ${ptr}`); - } - }, - - $getInheritedInstance__deps: ['$registeredInstances', '$getBasestPointer'], - $getInheritedInstance: (class_, ptr) => { - ptr = getBasestPointer(class_, ptr); - return registeredInstances[ptr]; - }, - - $getInheritedInstanceCount__deps: ['$registeredInstances'], - $getInheritedInstanceCount: () => Object.keys(registeredInstances).length, - - $getLiveInheritedInstances__deps: ['$registeredInstances'], - $getLiveInheritedInstances: () => { - var rv = []; - for (var k in registeredInstances) { - if (registeredInstances.hasOwnProperty(k)) { - rv.push(registeredInstances[k]); - } - } - return rv; - }, - - // class typeID -> {pointerType: ..., constPointerType: ...} - $registeredPointers: {}, - - $registerType__deps: ['$sharedRegisterType'], - $registerType__docs: '/** @param {Object=} options */', - $registerType: function(rawType, registeredInstance, options = {}) { - if (!('argPackAdvance' in registeredInstance)) { - throw new TypeError('registerType registeredInstance requires argPackAdvance'); - } - return sharedRegisterType(rawType, registeredInstance, options); - }, - - _embind_register_void__deps: ['$readLatin1String', '$registerType'], - _embind_register_void: (rawType, name) => { - name = readLatin1String(name); - registerType(rawType, { - isVoid: true, // void return values can be optimized out sometimes - name, - 'argPackAdvance': 0, - 'fromWireType': () => undefined, - // TODO: assert if anything else is given? - 'toWireType': (destructors, o) => undefined, - }); - }, - - _embind_register_bool__docs: '/** @suppress {globalThis} */', - _embind_register_bool__deps: ['$readLatin1String', '$registerType', '$GenericWireTypeSize'], - _embind_register_bool: (rawType, name, trueValue, falseValue) => { - name = readLatin1String(name); - registerType(rawType, { - name, - 'fromWireType': function(wt) { - // ambiguous emscripten ABI: sometimes return values are - // true or false, and sometimes integers (0 or 1) - return !!wt; - }, - 'toWireType': function(destructors, o) { - return o ? trueValue : falseValue; - }, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': function(pointer) { - return this['fromWireType'](HEAPU8[pointer]); - }, - destructorFunction: null, // This type does not need a destructor - }); - }, - - $integerReadValueFromPointer__deps: [], - $integerReadValueFromPointer: (name, width, signed) => { - // integers are quite common, so generate very specialized functions - switch (width) { - case 1: return signed ? - (pointer) => {{{ makeGetValue('pointer', 0, 'i8') }}} : - (pointer) => {{{ makeGetValue('pointer', 0, 'u8') }}}; - case 2: return signed ? - (pointer) => {{{ makeGetValue('pointer', 0, 'i16') }}} : - (pointer) => {{{ makeGetValue('pointer', 0, 'u16') }}} - case 4: return signed ? - (pointer) => {{{ makeGetValue('pointer', 0, 'i32') }}} : - (pointer) => {{{ makeGetValue('pointer', 0, 'u32') }}} -#if WASM_BIGINT - case 8: return signed ? - (pointer) => {{{ makeGetValue('pointer', 0, 'i64') }}} : - (pointer) => {{{ makeGetValue('pointer', 0, 'u64') }}} -#endif - default: - throw new TypeError(`invalid integer width (${width}): ${name}`); - } - }, - - $enumReadValueFromPointer__deps: [], - $enumReadValueFromPointer: (name, width, signed) => { - switch (width) { - case 1: return signed ? - function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'i8') }}}) } : - function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'u8') }}}) }; - case 2: return signed ? - function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'i16') }}}) } : - function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'u16') }}}) }; - case 4: return signed ? - function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'i32') }}}) } : - function(pointer) { return this['fromWireType']({{{ makeGetValue('pointer', 0, 'u32') }}}) }; - default: - throw new TypeError(`invalid integer width (${width}): ${name}`); - } - }, - - $floatReadValueFromPointer__deps: [], - $floatReadValueFromPointer: (name, width) => { - switch (width) { - case 4: return function(pointer) { - return this['fromWireType']({{{ makeGetValue('pointer', 0, 'float') }}}); - }; - case 8: return function(pointer) { - return this['fromWireType']({{{ makeGetValue('pointer', 0, 'double') }}}); - }; - default: - throw new TypeError(`invalid float width (${width}): ${name}`); - } - }, - - _embind_register_integer__docs: '/** @suppress {globalThis} */', - // When converting a number from JS to C++ side, the valid range of the number is - // [minRange, maxRange], inclusive. - _embind_register_integer__deps: [ - '$embindRepr', '$integerReadValueFromPointer', - '$readLatin1String', '$registerType'], - _embind_register_integer: (primitiveType, name, size, minRange, maxRange) => { - name = readLatin1String(name); - // LLVM doesn't have signed and unsigned 32-bit types, so u32 literals come - // out as 'i32 -1'. Always treat those as max u32. - if (maxRange === -1) { - maxRange = 4294967295; - } - - var fromWireType = (value) => value; - - if (minRange === 0) { - var bitshift = 32 - 8*size; - fromWireType = (value) => (value << bitshift) >>> bitshift; - } - - var isUnsignedType = (name.includes('unsigned')); - var checkAssertions = (value, toTypeName) => { -#if ASSERTIONS - if (typeof value != "number" && typeof value != "boolean") { - throw new TypeError(`Cannot convert "${embindRepr(value)}" to ${toTypeName}`); - } - if (value < minRange || value > maxRange) { - throw new TypeError(`Passing a number "${embindRepr(value)}" from JS side to C/C++ side to an argument of type "${name}", which is outside the valid range [${minRange}, ${maxRange}]!`); - } -#endif - } - var toWireType; - if (isUnsignedType) { - toWireType = function(destructors, value) { - checkAssertions(value, this.name); - return value >>> 0; - } - } else { - toWireType = function(destructors, value) { - checkAssertions(value, this.name); - // The VM will perform JS to Wasm value conversion, according to the spec: - // https://www.w3.org/TR/wasm-js-api-1/#towebassemblyvalue - return value; - } - } - registerType(primitiveType, { - name, - 'fromWireType': fromWireType, - 'toWireType': toWireType, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': integerReadValueFromPointer(name, size, minRange !== 0), - destructorFunction: null, // This type does not need a destructor - }); - }, - -#if WASM_BIGINT - _embind_register_bigint__docs: '/** @suppress {globalThis} */', - _embind_register_bigint__deps: [ - '$embindRepr', '$readLatin1String', '$registerType', '$integerReadValueFromPointer'], - _embind_register_bigint: (primitiveType, name, size, minRange, maxRange) => { - name = readLatin1String(name); - - var isUnsignedType = (name.indexOf('u') != -1); - - // maxRange comes through as -1 for uint64_t (see issue 13902). Work around that temporarily - if (isUnsignedType) { - maxRange = (1n << 64n) - 1n; - } - - registerType(primitiveType, { - name, - 'fromWireType': (value) => value, - 'toWireType': function(destructors, value) { - if (typeof value != "bigint" && typeof value != "number") { - throw new TypeError(`Cannot convert "${embindRepr(value)}" to ${this.name}`); - } - if (typeof value == "number") { - value = BigInt(value); - } -#if ASSERTIONS - if (value < minRange || value > maxRange) { - throw new TypeError(`Passing a number "${embindRepr(value)}" from JS side to C/C++ side to an argument of type "${name}", which is outside the valid range [${minRange}, ${maxRange}]!`); - } -#endif - return value; - }, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': integerReadValueFromPointer(name, size, !isUnsignedType), - destructorFunction: null, // This type does not need a destructor - }); - }, -#else - _embind_register_bigint__deps: [], - _embind_register_bigint: (primitiveType, name, size, minRange, maxRange) => {}, -#endif - - _embind_register_float__deps: [ - '$embindRepr', '$floatReadValueFromPointer', - '$readLatin1String', '$registerType'], - _embind_register_float: (rawType, name, size) => { - name = readLatin1String(name); - registerType(rawType, { - name, - 'fromWireType': (value) => value, - 'toWireType': (destructors, value) => { -#if ASSERTIONS - if (typeof value != "number" && typeof value != "boolean") { - throw new TypeError(`Cannot convert ${embindRepr(value)} to ${this.name}`); - } -#endif - // The VM will perform JS to Wasm value conversion, according to the spec: - // https://www.w3.org/TR/wasm-js-api-1/#towebassemblyvalue - return value; - }, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': floatReadValueFromPointer(name, size), - destructorFunction: null, // This type does not need a destructor - }); - }, - - $readPointer__docs: '/** @suppress {globalThis} */', - $readPointer: function(pointer) { - return this['fromWireType']({{{ makeGetValue('pointer', '0', '*') }}}); - }, - - _embind_register_std_string__deps: [ - '$readLatin1String', '$registerType', - '$readPointer', '$throwBindingError', - '$stringToUTF8', '$lengthBytesUTF8', 'malloc', 'free'], - _embind_register_std_string: (rawType, name) => { - name = readLatin1String(name); - var stdStringIsUTF8 -#if EMBIND_STD_STRING_IS_UTF8 - //process only std::string bindings with UTF8 support, in contrast to e.g. std::basic_string - = (name === "std::string"); -#else - = false; -#endif - - registerType(rawType, { - name, - // For some method names we use string keys here since they are part of - // the public/external API and/or used by the runtime-generated code. - 'fromWireType'(value) { - var length = {{{ makeGetValue('value', '0', SIZE_TYPE) }}}; - var payload = value + {{{ POINTER_SIZE }}}; - - var str; - if (stdStringIsUTF8) { - var decodeStartPtr = payload; - // Looping here to support possible embedded '0' bytes - for (var i = 0; i <= length; ++i) { - var currentBytePtr = payload + i; - if (i == length || HEAPU8[currentBytePtr] == 0) { - var maxRead = currentBytePtr - decodeStartPtr; - var stringSegment = UTF8ToString(decodeStartPtr, maxRead); - if (str === undefined) { - str = stringSegment; - } else { - str += String.fromCharCode(0); - str += stringSegment; - } - decodeStartPtr = currentBytePtr + 1; - } - } - } else { - var a = new Array(length); - for (var i = 0; i < length; ++i) { - a[i] = String.fromCharCode(HEAPU8[payload + i]); - } - str = a.join(''); - } - - _free(value); - - return str; - }, - 'toWireType'(destructors, value) { - if (value instanceof ArrayBuffer) { - value = new Uint8Array(value); - } - - var length; - var valueIsOfTypeString = (typeof value == 'string'); - - if (!(valueIsOfTypeString || value instanceof Uint8Array || value instanceof Uint8ClampedArray || value instanceof Int8Array)) { - throwBindingError('Cannot pass non-string to std::string'); - } - if (stdStringIsUTF8 && valueIsOfTypeString) { - length = lengthBytesUTF8(value); - } else { - length = value.length; - } - - // assumes POINTER_SIZE alignment - var base = _malloc({{{ POINTER_SIZE }}} + length + 1); - var ptr = base + {{{ POINTER_SIZE }}}; - {{{ makeSetValue('base', '0', 'length', SIZE_TYPE) }}}; - if (stdStringIsUTF8 && valueIsOfTypeString) { - stringToUTF8(value, ptr, length + 1); - } else { - if (valueIsOfTypeString) { - for (var i = 0; i < length; ++i) { - var charCode = value.charCodeAt(i); - if (charCode > 255) { - _free(ptr); - throwBindingError('String has UTF-16 code units that do not fit in 8 bits'); - } - HEAPU8[ptr + i] = charCode; - } - } else { - for (var i = 0; i < length; ++i) { - HEAPU8[ptr + i] = value[i]; - } - } - } - - if (destructors !== null) { - destructors.push(_free, base); - } - return base; - }, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': readPointer, - destructorFunction(ptr) { - _free(ptr); - }, - }); - }, - - _embind_register_std_wstring__deps: [ - '$readLatin1String', '$registerType', '$readPointer', - '$UTF16ToString', '$stringToUTF16', '$lengthBytesUTF16', - '$UTF32ToString', '$stringToUTF32', '$lengthBytesUTF32', - ], - _embind_register_std_wstring: (rawType, charSize, name) => { - name = readLatin1String(name); - var decodeString, encodeString, readCharAt, lengthBytesUTF; - if (charSize === 2) { - decodeString = UTF16ToString; - encodeString = stringToUTF16; - lengthBytesUTF = lengthBytesUTF16; - readCharAt = (pointer) => {{{ makeGetValue('pointer', 0, 'u16') }}}; - } else if (charSize === 4) { - decodeString = UTF32ToString; - encodeString = stringToUTF32; - lengthBytesUTF = lengthBytesUTF32; - readCharAt = (pointer) => {{{ makeGetValue('pointer', 0, 'u32') }}}; - } - registerType(rawType, { - name, - 'fromWireType': (value) => { - // Code mostly taken from _embind_register_std_string fromWireType - var length = {{{ makeGetValue('value', 0, '*') }}}; - var str; - - var decodeStartPtr = value + {{{ POINTER_SIZE }}}; - // Looping here to support possible embedded '0' bytes - for (var i = 0; i <= length; ++i) { - var currentBytePtr = value + {{{ POINTER_SIZE }}} + i * charSize; - if (i == length || readCharAt(currentBytePtr) == 0) { - var maxReadBytes = currentBytePtr - decodeStartPtr; - var stringSegment = decodeString(decodeStartPtr, maxReadBytes); - if (str === undefined) { - str = stringSegment; - } else { - str += String.fromCharCode(0); - str += stringSegment; - } - decodeStartPtr = currentBytePtr + charSize; - } - } - - _free(value); - - return str; - }, - 'toWireType': (destructors, value) => { - if (!(typeof value == 'string')) { - throwBindingError(`Cannot pass non-string to C++ string type ${name}`); - } - - // assumes POINTER_SIZE alignment - var length = lengthBytesUTF(value); - var ptr = _malloc({{{ POINTER_SIZE }}} + length + charSize); - {{{ makeSetValue('ptr', '0', 'length / charSize', SIZE_TYPE) }}}; - - encodeString(value, ptr + {{{ POINTER_SIZE }}}, length + charSize); - - if (destructors !== null) { - destructors.push(_free, ptr); - } - return ptr; - }, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': readPointer, - destructorFunction(ptr) { - _free(ptr); - } - }); - }, - - _embind_register_emval__deps: [ - '$registerType', '$EmValType'], - _embind_register_emval: (rawType) => registerType(rawType, EmValType), - - _embind_register_user_type__deps: ['_embind_register_emval'], - _embind_register_user_type: (rawType, name) => { - __embind_register_emval(rawType); - }, - - _embind_register_optional__deps: ['_embind_register_emval'], - _embind_register_optional: (rawOptionalType, rawType) => { - __embind_register_emval(rawOptionalType); - }, - - _embind_register_memory_view__deps: ['$readLatin1String', '$registerType'], - _embind_register_memory_view: (rawType, dataTypeIndex, name) => { - var typeMapping = [ - Int8Array, - Uint8Array, - Int16Array, - Uint16Array, - Int32Array, - Uint32Array, - Float32Array, - Float64Array, -#if WASM_BIGINT - BigInt64Array, - BigUint64Array, -#endif - ]; - - var TA = typeMapping[dataTypeIndex]; - - function decodeMemoryView(handle) { - var size = {{{ makeGetValue('handle', 0, '*') }}}; - var data = {{{ makeGetValue('handle', POINTER_SIZE, '*') }}}; - return new TA(HEAP8.buffer, data, size); - } - - name = readLatin1String(name); - registerType(rawType, { - name, - 'fromWireType': decodeMemoryView, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': decodeMemoryView, - }, { - ignoreDuplicateRegistrations: true, - }); - }, - - $runDestructors: (destructors) => { - while (destructors.length) { - var ptr = destructors.pop(); - var del = destructors.pop(); - del(ptr); - } - }, - -#if DYNAMIC_EXECUTION - $newFunc__deps: ['$createNamedFunction'], - $newFunc: function(constructor, argumentList) { - if (!(constructor instanceof Function)) { - throw new TypeError(`new_ called with constructor type ${typeof(constructor)} which is not a function`); - } - /* - * Previously, the following line was just: - * function dummy() {}; - * Unfortunately, Chrome was preserving 'dummy' as the object's name, even - * though at creation, the 'dummy' has the correct constructor name. Thus, - * objects created with IMVU.new would show up in the debugger as 'dummy', - * which isn't very helpful. Using IMVU.createNamedFunction addresses the - * issue. Doubly-unfortunately, there's no way to write a test for this - * behavior. -NRD 2013.02.22 - */ - var dummy = createNamedFunction(constructor.name || 'unknownFunctionName', function(){}); - dummy.prototype = constructor.prototype; - var obj = new dummy; - - var r = constructor.apply(obj, argumentList); - return (r instanceof Object) ? r : obj; - }, -#endif - - // The path to interop from JS code to C++ code: - // (hand-written JS code) -> (autogenerated JS invoker) -> (template-generated C++ invoker) -> (target C++ function) - // craftInvokerFunction generates the JS invoker function for each function exposed to JS through embind. - $craftInvokerFunction__deps: [ - '$createNamedFunction', '$runDestructors', '$throwBindingError', '$usesDestructorStack', -#if DYNAMIC_EXECUTION - '$newFunc', -#if !EMBIND_AOT - '$createJsInvoker', -#endif -#endif -#if EMBIND_AOT - '$InvokerFunctions', - '$createJsInvokerSignature', -#endif -#if ASYNCIFY - '$Asyncify', -#endif - ], - $craftInvokerFunction: function(humanName, argTypes, classType, cppInvokerFunc, cppTargetFunc, /** boolean= */ isAsync) { - // humanName: a human-readable string name for the function to be generated. - // argTypes: An array that contains the embind type objects for all types in the function signature. - // argTypes[0] is the type object for the function return value. - // argTypes[1] is the type object for function this object/class type, or null if not crafting an invoker for a class method. - // argTypes[2...] are the actual function parameters. - // classType: The embind type object for the class to be bound, or null if this is not a method of a class. - // cppInvokerFunc: JS Function object to the C++-side function that interops into C++ code. - // cppTargetFunc: Function pointer (an integer to FUNCTION_TABLE) to the target C++ function the cppInvokerFunc will end up calling. - // isAsync: Optional. If true, returns an async function. Async bindings are only supported with JSPI. - var argCount = argTypes.length; - - if (argCount < 2) { - throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!"); - } - -#if ASSERTIONS && ASYNCIFY != 2 - assert(!isAsync, 'Async bindings are only supported with JSPI.'); -#endif - -#if ASYNCIFY == 2 - if (isAsync) { - cppInvokerFunc = Asyncify.makeAsyncFunction(cppInvokerFunc); - } -#endif - - var isClassMethodFunc = (argTypes[1] !== null && classType !== null); - - // Free functions with signature "void function()" do not need an invoker that marshalls between wire types. -// TODO: This omits argument count check - enable only at -O3 or similar. -// if (ENABLE_UNSAFE_OPTS && argCount == 2 && argTypes[0].name == "void" && !isClassMethodFunc) { -// return FUNCTION_TABLE[fn]; -// } - - - // Determine if we need to use a dynamic stack to store the destructors for the function parameters. - // TODO: Remove this completely once all function invokers are being dynamically generated. - var needsDestructorStack = usesDestructorStack(argTypes); - - var returns = (argTypes[0].name !== "void"); - -#if DYNAMIC_EXECUTION == 0 && !EMBIND_AOT - var expectedArgCount = argCount - 2; - var argsWired = new Array(expectedArgCount); - var invokerFuncArgs = []; - var destructors = []; - var invokerFn = function(...args) { - if (args.length !== expectedArgCount) { - throwBindingError(`function ${humanName} called with ${args.length} arguments, expected ${expectedArgCount}`); - } -#if EMSCRIPTEN_TRACING - Module.emscripten_trace_enter_context(`embind::${humanName}`); -#endif - destructors.length = 0; - var thisWired; - invokerFuncArgs.length = isClassMethodFunc ? 2 : 1; - invokerFuncArgs[0] = cppTargetFunc; - if (isClassMethodFunc) { - thisWired = argTypes[1]['toWireType'](destructors, this); - invokerFuncArgs[1] = thisWired; - } - for (var i = 0; i < expectedArgCount; ++i) { - argsWired[i] = argTypes[i + 2]['toWireType'](destructors, args[i]); - invokerFuncArgs.push(argsWired[i]); - } - - var rv = cppInvokerFunc(...invokerFuncArgs); - - function onDone(rv) { - if (needsDestructorStack) { - runDestructors(destructors); - } else { - for (var i = isClassMethodFunc ? 1 : 2; i < argTypes.length; i++) { - var param = i === 1 ? thisWired : argsWired[i - 2]; - if (argTypes[i].destructorFunction !== null) { - argTypes[i].destructorFunction(param); - } - } - } - - #if EMSCRIPTEN_TRACING - Module.emscripten_trace_exit_context(); - #endif - - if (returns) { - return argTypes[0]['fromWireType'](rv); - } - } - -#if ASYNCIFY == 1 - if (Asyncify.currData) { - return Asyncify.whenDone().then(onDone); - } -#elif ASYNCIFY == 2 - if (isAsync) { - return rv.then(onDone); - } -#endif - - return onDone(rv); - }; -#else - // Builld the arguments that will be passed into the closure around the invoker - // function. - var closureArgs = [humanName, throwBindingError, cppInvokerFunc, cppTargetFunc, runDestructors, argTypes[0], argTypes[1]]; -#if EMSCRIPTEN_TRACING - closureArgs.push(Module); -#endif - for (var i = 0; i < argCount - 2; ++i) { - closureArgs.push(argTypes[i+2]); - } -#if ASYNCIFY == 1 - closureArgs.push(Asyncify); -#endif - if (!needsDestructorStack) { - for (var i = isClassMethodFunc?1:2; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here. Also skip class type if not a method. - if (argTypes[i].destructorFunction !== null) { - closureArgs.push(argTypes[i].destructorFunction); - } - } - } - -#if EMBIND_AOT - var signature = createJsInvokerSignature(argTypes, isClassMethodFunc, returns, isAsync); - var invokerFn = InvokerFunctions[signature](...closureArgs); -#else - let [args, invokerFnBody] = createJsInvoker(argTypes, isClassMethodFunc, returns, isAsync); - args.push(invokerFnBody); - var invokerFn = newFunc(Function, args)(...closureArgs); -#endif -#endif - return createNamedFunction(humanName, invokerFn); - }, - - $embind__requireFunction__deps: ['$readLatin1String', '$throwBindingError' -#if DYNCALLS || !WASM_BIGINT || MEMORY64 - , '$getDynCaller' -#endif - ], - $embind__requireFunction: (signature, rawFunction) => { - signature = readLatin1String(signature); - - function makeDynCaller() { -#if DYNCALLS - return getDynCaller(signature, rawFunction); -#else -#if !WASM_BIGINT - if (signature.includes('j')) { - return getDynCaller(signature, rawFunction); - } -#endif -#if MEMORY64 || CAN_ADDRESS_2GB - if (signature.includes('p')) { - return getDynCaller(signature, rawFunction); - } -#endif - return getWasmTableEntry(rawFunction); -#endif - } - - var fp = makeDynCaller(); - if (typeof fp != "function") { - throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`); - } - return fp; - }, - - _embind_register_function__deps: [ - '$craftInvokerFunction', '$exposePublicSymbol', '$heap32VectorToArray', - '$readLatin1String', '$replacePublicSymbol', '$embind__requireFunction', - '$throwUnboundTypeError', '$whenDependentTypesAreResolved', '$getFunctionName'], - _embind_register_function: (name, argCount, rawArgTypesAddr, signature, rawInvoker, fn, isAsync) => { - var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); - name = readLatin1String(name); - name = getFunctionName(name); - - rawInvoker = embind__requireFunction(signature, rawInvoker); - - exposePublicSymbol(name, function() { - throwUnboundTypeError(`Cannot call ${name} due to unbound types`, argTypes); - }, argCount - 1); - - whenDependentTypesAreResolved([], argTypes, (argTypes) => { - var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); - replacePublicSymbol(name, craftInvokerFunction(name, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn, isAsync), argCount - 1); - return []; - }); - }, - - _embind_register_value_array__deps: [ - '$tupleRegistrations', '$readLatin1String', '$embind__requireFunction'], - _embind_register_value_array: ( - rawType, - name, - constructorSignature, - rawConstructor, - destructorSignature, - rawDestructor - ) => { - tupleRegistrations[rawType] = { - name: readLatin1String(name), - rawConstructor: embind__requireFunction(constructorSignature, rawConstructor), - rawDestructor: embind__requireFunction(destructorSignature, rawDestructor), - elements: [], - }; - }, - - _embind_register_value_array_element__deps: [ - '$tupleRegistrations', '$embind__requireFunction'], - _embind_register_value_array_element: ( - rawTupleType, - getterReturnType, - getterSignature, - getter, - getterContext, - setterArgumentType, - setterSignature, - setter, - setterContext - ) => { - tupleRegistrations[rawTupleType].elements.push({ - getterReturnType, - getter: embind__requireFunction(getterSignature, getter), - getterContext, - setterArgumentType, - setter: embind__requireFunction(setterSignature, setter), - setterContext, - }); - }, - - _embind_finalize_value_array__deps: [ - '$tupleRegistrations', '$runDestructors', - '$readPointer', '$whenDependentTypesAreResolved'], - _embind_finalize_value_array: (rawTupleType) => { - var reg = tupleRegistrations[rawTupleType]; - delete tupleRegistrations[rawTupleType]; - var elements = reg.elements; - var elementsLength = elements.length; - var elementTypes = elements.map((elt) => elt.getterReturnType). - concat(elements.map((elt) => elt.setterArgumentType)); - - var rawConstructor = reg.rawConstructor; - var rawDestructor = reg.rawDestructor; - - whenDependentTypesAreResolved([rawTupleType], elementTypes, (elementTypes) => { - elements.forEach((elt, i) => { - var getterReturnType = elementTypes[i]; - var getter = elt.getter; - var getterContext = elt.getterContext; - var setterArgumentType = elementTypes[i + elementsLength]; - var setter = elt.setter; - var setterContext = elt.setterContext; - elt.read = (ptr) => getterReturnType['fromWireType'](getter(getterContext, ptr)); - elt.write = (ptr, o) => { - var destructors = []; - setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, o)); - runDestructors(destructors); - }; - }); - - return [{ - name: reg.name, - 'fromWireType': (ptr) => { - var rv = new Array(elementsLength); - for (var i = 0; i < elementsLength; ++i) { - rv[i] = elements[i].read(ptr); - } - rawDestructor(ptr); - return rv; - }, - 'toWireType': (destructors, o) => { - if (elementsLength !== o.length) { - throw new TypeError(`Incorrect number of tuple elements for ${reg.name}: expected=${elementsLength}, actual=${o.length}`); - } - var ptr = rawConstructor(); - for (var i = 0; i < elementsLength; ++i) { - elements[i].write(ptr, o[i]); - } - if (destructors !== null) { - destructors.push(rawDestructor, ptr); - } - return ptr; - }, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': readPointer, - destructorFunction: rawDestructor, - }]; - }); - }, - - _embind_register_value_object__deps: [ - '$structRegistrations', '$readLatin1String', '$embind__requireFunction'], - _embind_register_value_object: ( - rawType, - name, - constructorSignature, - rawConstructor, - destructorSignature, - rawDestructor - ) => { - structRegistrations[rawType] = { - name: readLatin1String(name), - rawConstructor: embind__requireFunction(constructorSignature, rawConstructor), - rawDestructor: embind__requireFunction(destructorSignature, rawDestructor), - fields: [], - }; - }, - - _embind_register_value_object_field__deps: [ - '$structRegistrations', '$readLatin1String', '$embind__requireFunction'], - _embind_register_value_object_field: ( - structType, - fieldName, - getterReturnType, - getterSignature, - getter, - getterContext, - setterArgumentType, - setterSignature, - setter, - setterContext - ) => { - structRegistrations[structType].fields.push({ - fieldName: readLatin1String(fieldName), - getterReturnType, - getter: embind__requireFunction(getterSignature, getter), - getterContext, - setterArgumentType, - setter: embind__requireFunction(setterSignature, setter), - setterContext, - }); - }, - - _embind_finalize_value_object__deps: [ - '$structRegistrations', '$runDestructors', - '$readPointer', '$whenDependentTypesAreResolved'], - _embind_finalize_value_object: (structType) => { - var reg = structRegistrations[structType]; - delete structRegistrations[structType]; - - var rawConstructor = reg.rawConstructor; - var rawDestructor = reg.rawDestructor; - var fieldRecords = reg.fields; - var fieldTypes = fieldRecords.map((field) => field.getterReturnType). - concat(fieldRecords.map((field) => field.setterArgumentType)); - whenDependentTypesAreResolved([structType], fieldTypes, (fieldTypes) => { - var fields = {}; - fieldRecords.forEach((field, i) => { - var fieldName = field.fieldName; - var getterReturnType = fieldTypes[i]; - var getter = field.getter; - var getterContext = field.getterContext; - var setterArgumentType = fieldTypes[i + fieldRecords.length]; - var setter = field.setter; - var setterContext = field.setterContext; - fields[fieldName] = { - read: (ptr) => getterReturnType['fromWireType'](getter(getterContext, ptr)), - write: (ptr, o) => { - var destructors = []; - setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, o)); - runDestructors(destructors); - } - }; - }); - - return [{ - name: reg.name, - 'fromWireType': (ptr) => { - var rv = {}; - for (var i in fields) { - rv[i] = fields[i].read(ptr); - } - rawDestructor(ptr); - return rv; - }, - 'toWireType': (destructors, o) => { - // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: - // assume all fields are present without checking. - for (var fieldName in fields) { - if (!(fieldName in o)) { - throw new TypeError(`Missing field: "${fieldName}"`); - } - } - var ptr = rawConstructor(); - for (fieldName in fields) { - fields[fieldName].write(ptr, o[fieldName]); - } - if (destructors !== null) { - destructors.push(rawDestructor, ptr); - } - return ptr; - }, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': readPointer, - destructorFunction: rawDestructor, - }]; - }); - }, - - $genericPointerToWireType__docs: '/** @suppress {globalThis} */', - $genericPointerToWireType__deps: ['$throwBindingError', '$upcastPointer'], - $genericPointerToWireType: function(destructors, handle) { - var ptr; - if (handle === null) { - if (this.isReference) { - throwBindingError(`null is not a valid ${this.name}`); - } - - if (this.isSmartPointer) { - ptr = this.rawConstructor(); - if (destructors !== null) { - destructors.push(this.rawDestructor, ptr); - } - return ptr; - } else { - return 0; - } - } - - if (!handle || !handle.$$) { - throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`); - } - if (!handle.$$.ptr) { - throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`); - } - if (!this.isConst && handle.$$.ptrType.isConst) { - throwBindingError(`Cannot convert argument of type ${(handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name)} to parameter type ${this.name}`); - } - var handleClass = handle.$$.ptrType.registeredClass; - ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); - - if (this.isSmartPointer) { - // TODO: this is not strictly true - // We could support BY_EMVAL conversions from raw pointers to smart pointers - // because the smart pointer can hold a reference to the handle - if (undefined === handle.$$.smartPtr) { - throwBindingError('Passing raw pointer to smart pointer is illegal'); - } - - switch (this.sharingPolicy) { - case 0: // NONE - // no upcasting - if (handle.$$.smartPtrType === this) { - ptr = handle.$$.smartPtr; - } else { - throwBindingError(`Cannot convert argument of type ${(handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name)} to parameter type ${this.name}`); - } - break; - - case 1: // INTRUSIVE - ptr = handle.$$.smartPtr; - break; - - case 2: // BY_EMVAL - if (handle.$$.smartPtrType === this) { - ptr = handle.$$.smartPtr; - } else { - var clonedHandle = handle['clone'](); - ptr = this.rawShare( - ptr, - Emval.toHandle(() => clonedHandle['delete']()) - ); - if (destructors !== null) { - destructors.push(this.rawDestructor, ptr); - } - } - break; - - default: - throwBindingError('Unsupporting sharing policy'); - } - } - return ptr; - }, - - $constNoSmartPtrRawPointerToWireType__docs: '/** @suppress {globalThis} */', - // If we know a pointer type is not going to have SmartPtr logic in it, we can - // special-case optimize it a bit (compare to genericPointerToWireType) - $constNoSmartPtrRawPointerToWireType__deps: ['$throwBindingError', '$upcastPointer'], - $constNoSmartPtrRawPointerToWireType: function(destructors, handle) { - if (handle === null) { - if (this.isReference) { - throwBindingError(`null is not a valid ${this.name}`); - } - return 0; - } - - if (!handle.$$) { - throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`); - } - if (!handle.$$.ptr) { - throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`); - } - var handleClass = handle.$$.ptrType.registeredClass; - var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); - return ptr; - }, - - $nonConstNoSmartPtrRawPointerToWireType__docs: '/** @suppress {globalThis} */', - // An optimized version for non-const method accesses - there we must additionally restrict that - // the pointer is not a const-pointer. - $nonConstNoSmartPtrRawPointerToWireType__deps: ['$throwBindingError', '$upcastPointer'], - $nonConstNoSmartPtrRawPointerToWireType: function(destructors, handle) { - if (handle === null) { - if (this.isReference) { - throwBindingError(`null is not a valid ${this.name}`); - } - return 0; - } - - if (!handle.$$) { - throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`); - } - if (!handle.$$.ptr) { - throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`); - } - if (handle.$$.ptrType.isConst) { - throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`); - } - var handleClass = handle.$$.ptrType.registeredClass; - var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); - return ptr; - }, - - $init_RegisteredPointer__deps: [ - '$RegisteredPointer', - '$readPointer', - '$RegisteredPointer_fromWireType', - '$GenericWireTypeSize', - ], - $init_RegisteredPointer: () => { - Object.assign(RegisteredPointer.prototype, { - getPointee(ptr) { - if (this.rawGetPointee) { - ptr = this.rawGetPointee(ptr); - } - return ptr; - }, - destructor(ptr) { - this.rawDestructor?.(ptr); - }, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': readPointer, - 'fromWireType': RegisteredPointer_fromWireType, - }); - }, - - $RegisteredPointer__docs: `/** @constructor - @param {*=} pointeeType, - @param {*=} sharingPolicy, - @param {*=} rawGetPointee, - @param {*=} rawConstructor, - @param {*=} rawShare, - @param {*=} rawDestructor, - */`, - $RegisteredPointer__deps: [ - '$constNoSmartPtrRawPointerToWireType', '$genericPointerToWireType', - '$nonConstNoSmartPtrRawPointerToWireType', '$init_RegisteredPointer'], - $RegisteredPointer__postset: 'init_RegisteredPointer()', - $RegisteredPointer: function( - name, - registeredClass, - isReference, - isConst, - - // smart pointer properties - isSmartPointer, - pointeeType, - sharingPolicy, - rawGetPointee, - rawConstructor, - rawShare, - rawDestructor - ) { - this.name = name; - this.registeredClass = registeredClass; - this.isReference = isReference; - this.isConst = isConst; - - // smart pointer properties - this.isSmartPointer = isSmartPointer; - this.pointeeType = pointeeType; - this.sharingPolicy = sharingPolicy; - this.rawGetPointee = rawGetPointee; - this.rawConstructor = rawConstructor; - this.rawShare = rawShare; - this.rawDestructor = rawDestructor; - - if (!isSmartPointer && registeredClass.baseClass === undefined) { - if (isConst) { - this['toWireType'] = constNoSmartPtrRawPointerToWireType; - this.destructorFunction = null; - } else { - this['toWireType'] = nonConstNoSmartPtrRawPointerToWireType; - this.destructorFunction = null; - } - } else { - this['toWireType'] = genericPointerToWireType; - // Here we must leave this.destructorFunction undefined, since whether genericPointerToWireType returns - // a pointer that needs to be freed up is runtime-dependent, and cannot be evaluated at registration time. - // TODO: Create an alternative mechanism that allows removing the use of var destructors = []; array in - // craftInvokerFunction altogether. - } - }, - - $RegisteredPointer_fromWireType__docs: '/** @suppress {globalThis} */', - $RegisteredPointer_fromWireType__deps: [ - '$downcastPointer', '$registeredPointers', - '$getInheritedInstance', '$makeClassHandle', -#if MEMORY64 - '$bigintToI53Checked' -#endif - ], - $RegisteredPointer_fromWireType: function(ptr) { - // ptr is a raw pointer (or a raw smartpointer) -#if MEMORY64 - ptr = bigintToI53Checked(ptr); -#if ASSERTIONS - assert(Number.isSafeInteger(ptr)); -#endif -#endif - - // rawPointer is a maybe-null raw pointer - var rawPointer = this.getPointee(ptr); - if (!rawPointer) { - this.destructor(ptr); - return null; - } - - var registeredInstance = getInheritedInstance(this.registeredClass, rawPointer); - if (undefined !== registeredInstance) { - // JS object has been neutered, time to repopulate it - if (0 === registeredInstance.$$.count.value) { - registeredInstance.$$.ptr = rawPointer; - registeredInstance.$$.smartPtr = ptr; - return registeredInstance['clone'](); - } else { - // else, just increment reference count on existing object - // it already has a reference to the smart pointer - var rv = registeredInstance['clone'](); - this.destructor(ptr); - return rv; - } - } - - function makeDefaultHandle() { - if (this.isSmartPointer) { - return makeClassHandle(this.registeredClass.instancePrototype, { - ptrType: this.pointeeType, - ptr: rawPointer, - smartPtrType: this, - smartPtr: ptr, - }); - } else { - return makeClassHandle(this.registeredClass.instancePrototype, { - ptrType: this, - ptr, - }); - } - } - - var actualType = this.registeredClass.getActualType(rawPointer); - var registeredPointerRecord = registeredPointers[actualType]; - if (!registeredPointerRecord) { - return makeDefaultHandle.call(this); - } - - var toType; - if (this.isConst) { - toType = registeredPointerRecord.constPointerType; - } else { - toType = registeredPointerRecord.pointerType; - } - var dp = downcastPointer( - rawPointer, - this.registeredClass, - toType.registeredClass); - if (dp === null) { - return makeDefaultHandle.call(this); - } - if (this.isSmartPointer) { - return makeClassHandle(toType.registeredClass.instancePrototype, { - ptrType: toType, - ptr: dp, - smartPtrType: this, - smartPtr: ptr, - }); - } else { - return makeClassHandle(toType.registeredClass.instancePrototype, { - ptrType: toType, - ptr: dp, - }); - } - }, - - $runDestructor: ($$) => { - if ($$.smartPtr) { - $$.smartPtrType.rawDestructor($$.smartPtr); - } else { - $$.ptrType.registeredClass.rawDestructor($$.ptr); - } - }, - - $releaseClassHandle__deps: ['$runDestructor'], - $releaseClassHandle: ($$) => { - $$.count.value -= 1; - var toDelete = 0 === $$.count.value; - if (toDelete) { - runDestructor($$); - } - }, - - $finalizationRegistry: false, - - $detachFinalizer_deps: ['$finalizationRegistry'], - $detachFinalizer: (handle) => {}, - - $attachFinalizer__deps: ['$finalizationRegistry', '$detachFinalizer', - '$releaseClassHandle', '$RegisteredPointer_fromWireType'], - $attachFinalizer: (handle) => { - if ('undefined' === typeof FinalizationRegistry) { - attachFinalizer = (handle) => handle; - return handle; - } - // If the running environment has a FinalizationRegistry (see - // https://github.com/tc39/proposal-weakrefs), then attach finalizers - // for class handles. We check for the presence of FinalizationRegistry - // at run-time, not build-time. - finalizationRegistry = new FinalizationRegistry((info) => { -#if ASSERTIONS - console.warn(info.leakWarning.stack.replace(/^Error: /, '')); -#endif - releaseClassHandle(info.$$); - }); - attachFinalizer = (handle) => { - var $$ = handle.$$; - var hasSmartPtr = !!$$.smartPtr; - if (hasSmartPtr) { - // We should not call the destructor on raw pointers in case other code expects the pointee to live - var info = { $$: $$ }; -#if ASSERTIONS - // Create a warning as an Error instance in advance so that we can store - // the current stacktrace and point to it when / if a leak is detected. - // This is more useful than the empty stacktrace of `FinalizationRegistry` - // callback. - var cls = $$.ptrType.registeredClass; - info.leakWarning = new Error(`Embind found a leaked C++ instance ${cls.name} <${ptrToString($$.ptr)}>.\n` + - "We'll free it automatically in this case, but this functionality is not reliable across various environments.\n" + - "Make sure to invoke .delete() manually once you're done with the instance instead.\n" + - "Originally allocated"); // `.stack` will add "at ..." after this sentence - if ('captureStackTrace' in Error) { - Error.captureStackTrace(info.leakWarning, RegisteredPointer_fromWireType); - } -#endif - finalizationRegistry.register(handle, info, handle); - } - return handle; - }; - detachFinalizer = (handle) => finalizationRegistry.unregister(handle); - return attachFinalizer(handle); - }, - - $makeClassHandle__deps: ['$throwInternalError', '$attachFinalizer'], - $makeClassHandle: (prototype, record) => { - if (!record.ptrType || !record.ptr) { - throwInternalError('makeClassHandle requires ptr and ptrType'); - } - var hasSmartPtrType = !!record.smartPtrType; - var hasSmartPtr = !!record.smartPtr; - if (hasSmartPtrType !== hasSmartPtr) { - throwInternalError('Both smartPtrType and smartPtr must be specified'); - } - record.count = { value: 1 }; - return attachFinalizer(Object.create(prototype, { - $$: { - value: record, - writable: true, - }, - })); - }, - - $init_ClassHandle__deps: [ - '$ClassHandle', - '$shallowCopyInternalPointer', - '$throwInstanceAlreadyDeleted', - '$attachFinalizer', - '$releaseClassHandle', - '$throwBindingError', - '$detachFinalizer', - ], - $init_ClassHandle: () => { - Object.assign(ClassHandle.prototype, { - "isAliasOf"(other) { - if (!(this instanceof ClassHandle)) { - return false; - } - if (!(other instanceof ClassHandle)) { - return false; - } - - var leftClass = this.$$.ptrType.registeredClass; - var left = this.$$.ptr; - other.$$ = /** @type {Object} */ (other.$$); - var rightClass = other.$$.ptrType.registeredClass; - var right = other.$$.ptr; - - while (leftClass.baseClass) { - left = leftClass.upcast(left); - leftClass = leftClass.baseClass; - } - - while (rightClass.baseClass) { - right = rightClass.upcast(right); - rightClass = rightClass.baseClass; - } - - return leftClass === rightClass && left === right; - }, - - "clone"() { - if (!this.$$.ptr) { - throwInstanceAlreadyDeleted(this); - } - - if (this.$$.preservePointerOnDelete) { - this.$$.count.value += 1; - return this; - } else { - var clone = attachFinalizer(Object.create(Object.getPrototypeOf(this), { - $$: { - value: shallowCopyInternalPointer(this.$$), - } - })); - - clone.$$.count.value += 1; - clone.$$.deleteScheduled = false; - return clone; - } - }, - - "delete"() { - if (!this.$$.ptr) { - throwInstanceAlreadyDeleted(this); - } - - if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) { - throwBindingError('Object already scheduled for deletion'); - } - - detachFinalizer(this); - releaseClassHandle(this.$$); - - if (!this.$$.preservePointerOnDelete) { - this.$$.smartPtr = undefined; - this.$$.ptr = undefined; - } - }, - - "isDeleted"() { - return !this.$$.ptr; - }, - - "deleteLater"() { - if (!this.$$.ptr) { - throwInstanceAlreadyDeleted(this); - } - if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) { - throwBindingError('Object already scheduled for deletion'); - } - deletionQueue.push(this); - if (deletionQueue.length === 1 && delayFunction) { - delayFunction(flushPendingDeletes); - } - this.$$.deleteScheduled = true; - return this; - }, - }); - }, - - $ClassHandle__docs: '/** @constructor */', - $ClassHandle__deps: ['$init_ClassHandle'], - $ClassHandle__postset: 'init_ClassHandle()', - // root of all pointer and smart pointer handles in embind - $ClassHandle: function() { - }, - - $throwInstanceAlreadyDeleted__deps: ['$throwBindingError'], - $throwInstanceAlreadyDeleted: (obj) => { - function getInstanceTypeName(handle) { - return handle.$$.ptrType.registeredClass.name; - } - throwBindingError(getInstanceTypeName(obj) + ' instance already deleted'); - }, - - $deletionQueue: [], - - $flushPendingDeletes__deps: ['$deletionQueue'], - $flushPendingDeletes: () => { - while (deletionQueue.length) { - var obj = deletionQueue.pop(); - obj.$$.deleteScheduled = false; - obj['delete'](); - } - }, - - $delayFunction: undefined, - - $setDelayFunction__deps: ['$delayFunction', '$deletionQueue', '$flushPendingDeletes'], - $setDelayFunction: (fn) => { - delayFunction = fn; - if (deletionQueue.length && delayFunction) { - delayFunction(flushPendingDeletes); - } - }, - - $RegisteredClass__docs: '/** @constructor */', - $RegisteredClass: function(name, - constructor, - instancePrototype, - rawDestructor, - baseClass, - getActualType, - upcast, - downcast) { - this.name = name; - this.constructor = constructor; - this.instancePrototype = instancePrototype; - this.rawDestructor = rawDestructor; - this.baseClass = baseClass; - this.getActualType = getActualType; - this.upcast = upcast; - this.downcast = downcast; - this.pureVirtualFunctions = []; - }, - - $shallowCopyInternalPointer: (o) => { - return { - count: o.count, - deleteScheduled: o.deleteScheduled, - preservePointerOnDelete: o.preservePointerOnDelete, - ptr: o.ptr, - ptrType: o.ptrType, - smartPtr: o.smartPtr, - smartPtrType: o.smartPtrType, - }; - }, - - _embind_register_class__deps: [ - '$BindingError', '$ClassHandle', '$createNamedFunction', - '$registeredPointers', '$exposePublicSymbol', - '$makeLegalFunctionName', '$readLatin1String', - '$RegisteredClass', '$RegisteredPointer', '$replacePublicSymbol', - '$embind__requireFunction', '$throwUnboundTypeError', - '$whenDependentTypesAreResolved'], - _embind_register_class: (rawType, - rawPointerType, - rawConstPointerType, - baseClassRawType, - getActualTypeSignature, - getActualType, - upcastSignature, - upcast, - downcastSignature, - downcast, - name, - destructorSignature, - rawDestructor) => { - name = readLatin1String(name); - getActualType = embind__requireFunction(getActualTypeSignature, getActualType); - upcast &&= embind__requireFunction(upcastSignature, upcast); - downcast &&= embind__requireFunction(downcastSignature, downcast); - rawDestructor = embind__requireFunction(destructorSignature, rawDestructor); - var legalFunctionName = makeLegalFunctionName(name); - - exposePublicSymbol(legalFunctionName, function() { - // this code cannot run if baseClassRawType is zero - throwUnboundTypeError(`Cannot construct ${name} due to unbound types`, [baseClassRawType]); - }); - - whenDependentTypesAreResolved( - [rawType, rawPointerType, rawConstPointerType], - baseClassRawType ? [baseClassRawType] : [], - (base) => { - base = base[0]; - - var baseClass; - var basePrototype; - if (baseClassRawType) { - baseClass = base.registeredClass; - basePrototype = baseClass.instancePrototype; - } else { - basePrototype = ClassHandle.prototype; - } - - var constructor = createNamedFunction(name, function(...args) { - if (Object.getPrototypeOf(this) !== instancePrototype) { - throw new BindingError("Use 'new' to construct " + name); - } - if (undefined === registeredClass.constructor_body) { - throw new BindingError(name + " has no accessible constructor"); - } - var body = registeredClass.constructor_body[args.length]; - if (undefined === body) { - throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`); - } - return body.apply(this, args); - }); - - var instancePrototype = Object.create(basePrototype, { - constructor: { value: constructor }, - }); - - constructor.prototype = instancePrototype; - - var registeredClass = new RegisteredClass(name, - constructor, - instancePrototype, - rawDestructor, - baseClass, - getActualType, - upcast, - downcast); - - if (registeredClass.baseClass) { - // Keep track of class hierarchy. Used to allow sub-classes to inherit class functions. - registeredClass.baseClass.__derivedClasses ??= []; - - registeredClass.baseClass.__derivedClasses.push(registeredClass); - } - - var referenceConverter = new RegisteredPointer(name, - registeredClass, - true, - false, - false); - - var pointerConverter = new RegisteredPointer(name + '*', - registeredClass, - false, - false, - false); - - var constPointerConverter = new RegisteredPointer(name + ' const*', - registeredClass, - false, - true, - false); - - registeredPointers[rawType] = { - pointerType: pointerConverter, - constPointerType: constPointerConverter - }; - - replacePublicSymbol(legalFunctionName, constructor); - - return [referenceConverter, pointerConverter, constPointerConverter]; - } - ); - }, - - _embind_register_class_constructor__deps: [ - '$heap32VectorToArray', '$embind__requireFunction', '$runDestructors', - '$throwBindingError', '$whenDependentTypesAreResolved', '$registeredTypes', - '$craftInvokerFunction'], - _embind_register_class_constructor: ( - rawClassType, - argCount, - rawArgTypesAddr, - invokerSignature, - invoker, - rawConstructor - ) => { -#if ASSERTIONS - assert(argCount > 0); -#endif - var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); - invoker = embind__requireFunction(invokerSignature, invoker); - var args = [rawConstructor]; - var destructors = []; - - whenDependentTypesAreResolved([], [rawClassType], (classType) => { - classType = classType[0]; - var humanName = `constructor ${classType.name}`; - - if (undefined === classType.registeredClass.constructor_body) { - classType.registeredClass.constructor_body = []; - } - if (undefined !== classType.registeredClass.constructor_body[argCount - 1]) { - throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class '${classType.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`); - } - classType.registeredClass.constructor_body[argCount - 1] = () => { - throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`, rawArgTypes); - }; - - whenDependentTypesAreResolved([], rawArgTypes, (argTypes) => { - // Insert empty slot for context type (argTypes[1]). - argTypes.splice(1, 0, null); - classType.registeredClass.constructor_body[argCount - 1] = craftInvokerFunction(humanName, argTypes, null, invoker, rawConstructor); - return []; - }); - return []; - }); - }, - - $downcastPointer: (ptr, ptrClass, desiredClass) => { - if (ptrClass === desiredClass) { - return ptr; - } - if (undefined === desiredClass.baseClass) { - return null; // no conversion - } - - var rv = downcastPointer(ptr, ptrClass, desiredClass.baseClass); - if (rv === null) { - return null; - } - return desiredClass.downcast(rv); - }, - - $upcastPointer__deps: ['$throwBindingError'], - $upcastPointer: (ptr, ptrClass, desiredClass) => { - while (ptrClass !== desiredClass) { - if (!ptrClass.upcast) { - throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`); - } - ptr = ptrClass.upcast(ptr); - ptrClass = ptrClass.baseClass; - } - return ptr; - }, - - $validateThis__deps: ['$throwBindingError', '$upcastPointer'], - $validateThis: (this_, classType, humanName) => { - if (!(this_ instanceof Object)) { - throwBindingError(`${humanName} with invalid "this": ${this_}`); - } - if (!(this_ instanceof classType.registeredClass.constructor)) { - throwBindingError(`${humanName} incompatible with "this" of type ${this_.constructor.name}`); - } - if (!this_.$$.ptr) { - throwBindingError(`cannot call emscripten binding method ${humanName} on deleted object`); - } - - // todo: kill this - return upcastPointer(this_.$$.ptr, - this_.$$.ptrType.registeredClass, - classType.registeredClass); - }, - - _embind_register_class_function__deps: [ - '$craftInvokerFunction', '$heap32VectorToArray', '$readLatin1String', - '$embind__requireFunction', '$throwUnboundTypeError', - '$whenDependentTypesAreResolved', '$getFunctionName'], - _embind_register_class_function: (rawClassType, - methodName, - argCount, - rawArgTypesAddr, // [ReturnType, ThisType, Args...] - invokerSignature, - rawInvoker, - context, - isPureVirtual, - isAsync) => { - var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); - methodName = readLatin1String(methodName); - methodName = getFunctionName(methodName); - rawInvoker = embind__requireFunction(invokerSignature, rawInvoker); - - whenDependentTypesAreResolved([], [rawClassType], (classType) => { - classType = classType[0]; - var humanName = `${classType.name}.${methodName}`; - - if (methodName.startsWith("@@")) { - methodName = Symbol[methodName.substring(2)]; - } - - if (isPureVirtual) { - classType.registeredClass.pureVirtualFunctions.push(methodName); - } - - function unboundTypesHandler() { - throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`, rawArgTypes); - } - - var proto = classType.registeredClass.instancePrototype; - var method = proto[methodName]; - if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount - 2)) { - // This is the first overload to be registered, OR we are replacing a - // function in the base class with a function in the derived class. - unboundTypesHandler.argCount = argCount - 2; - unboundTypesHandler.className = classType.name; - proto[methodName] = unboundTypesHandler; - } else { - // There was an existing function with the same name registered. Set up - // a function overload routing table. - ensureOverloadTable(proto, methodName, humanName); - proto[methodName].overloadTable[argCount - 2] = unboundTypesHandler; - } - - whenDependentTypesAreResolved([], rawArgTypes, (argTypes) => { - var memberFunction = craftInvokerFunction(humanName, argTypes, classType, rawInvoker, context, isAsync); - - // Replace the initial unbound-handler-stub function with the - // appropriate member function, now that all types are resolved. If - // multiple overloads are registered for this function, the function - // goes into an overload table. - if (undefined === proto[methodName].overloadTable) { - // Set argCount in case an overload is registered later - memberFunction.argCount = argCount - 2; - proto[methodName] = memberFunction; - } else { - proto[methodName].overloadTable[argCount - 2] = memberFunction; - } - - return []; - }); - return []; - }); - }, - - _embind_register_class_property__deps: [ - '$readLatin1String', '$embind__requireFunction', '$runDestructors', - '$throwBindingError', '$throwUnboundTypeError', - '$whenDependentTypesAreResolved', '$validateThis'], - _embind_register_class_property: (classType, - fieldName, - getterReturnType, - getterSignature, - getter, - getterContext, - setterArgumentType, - setterSignature, - setter, - setterContext) => { - fieldName = readLatin1String(fieldName); - getter = embind__requireFunction(getterSignature, getter); - - whenDependentTypesAreResolved([], [classType], (classType) => { - classType = classType[0]; - var humanName = `${classType.name}.${fieldName}`; - var desc = { - get() { - throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`, [getterReturnType, setterArgumentType]); - }, - enumerable: true, - configurable: true - }; - if (setter) { - desc.set = () => throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`, [getterReturnType, setterArgumentType]); - } else { - desc.set = (v) => throwBindingError(humanName + ' is a read-only property'); - } - - Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, desc); - - whenDependentTypesAreResolved( - [], - (setter ? [getterReturnType, setterArgumentType] : [getterReturnType]), - (types) => { - var getterReturnType = types[0]; - var desc = { - get() { - var ptr = validateThis(this, classType, humanName + ' getter'); - return getterReturnType['fromWireType'](getter(getterContext, ptr)); - }, - enumerable: true - }; - - if (setter) { - setter = embind__requireFunction(setterSignature, setter); - var setterArgumentType = types[1]; - desc.set = function(v) { - var ptr = validateThis(this, classType, humanName + ' setter'); - var destructors = []; - setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, v)); - runDestructors(destructors); - }; - } - - Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, desc); - return []; - }); - - return []; - }); - }, - - _embind_register_class_class_function__deps: [ - '$craftInvokerFunction', '$ensureOverloadTable', '$heap32VectorToArray', - '$readLatin1String', '$embind__requireFunction', '$throwUnboundTypeError', - '$whenDependentTypesAreResolved', '$getFunctionName'], - _embind_register_class_class_function: (rawClassType, - methodName, - argCount, - rawArgTypesAddr, - invokerSignature, - rawInvoker, - fn, - isAsync) => { - var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); - methodName = readLatin1String(methodName); - methodName = getFunctionName(methodName); - rawInvoker = embind__requireFunction(invokerSignature, rawInvoker); - whenDependentTypesAreResolved([], [rawClassType], (classType) => { - classType = classType[0]; - var humanName = `${classType.name}.${methodName}`; - - function unboundTypesHandler() { - throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`, rawArgTypes); - } - - if (methodName.startsWith("@@")) { - methodName = Symbol[methodName.substring(2)]; - } - - var proto = classType.registeredClass.constructor; - if (undefined === proto[methodName]) { - // This is the first function to be registered with this name. - unboundTypesHandler.argCount = argCount-1; - proto[methodName] = unboundTypesHandler; - } else { - // There was an existing function with the same name registered. Set up - // a function overload routing table. - ensureOverloadTable(proto, methodName, humanName); - proto[methodName].overloadTable[argCount-1] = unboundTypesHandler; - } - - whenDependentTypesAreResolved([], rawArgTypes, (argTypes) => { - // Replace the initial unbound-types-handler stub with the proper - // function. If multiple overloads are registered, the function handlers - // go into an overload table. - var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); - var func = craftInvokerFunction(humanName, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn, isAsync); - if (undefined === proto[methodName].overloadTable) { - func.argCount = argCount-1; - proto[methodName] = func; - } else { - proto[methodName].overloadTable[argCount-1] = func; - } - - if (classType.registeredClass.__derivedClasses) { - for (const derivedClass of classType.registeredClass.__derivedClasses) { - if (!derivedClass.constructor.hasOwnProperty(methodName)) { - // TODO: Add support for overloads - derivedClass.constructor[methodName] = func; - } - } - } - - return []; - }); - return []; - }); - }, - - _embind_register_class_class_property__deps: [ - '$readLatin1String', '$embind__requireFunction', '$runDestructors', - '$throwBindingError', '$throwUnboundTypeError', - '$whenDependentTypesAreResolved', '$validateThis'], - _embind_register_class_class_property: (rawClassType, - fieldName, - rawFieldType, - rawFieldPtr, - getterSignature, - getter, - setterSignature, - setter) => { - fieldName = readLatin1String(fieldName); - getter = embind__requireFunction(getterSignature, getter); - - whenDependentTypesAreResolved([], [rawClassType], (classType) => { - classType = classType[0]; - var humanName = `${classType.name}.${fieldName}`; - var desc = { - get() { - throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`, [rawFieldType]); - }, - enumerable: true, - configurable: true - }; - if (setter) { - desc.set = () => { - throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`, [rawFieldType]); - }; - } else { - desc.set = (v) => { - throwBindingError(`${humanName} is a read-only property`); - }; - } - - Object.defineProperty(classType.registeredClass.constructor, fieldName, desc); - - whenDependentTypesAreResolved([], [rawFieldType], (fieldType) => { - fieldType = fieldType[0]; - var desc = { - get() { - return fieldType['fromWireType'](getter(rawFieldPtr)); - }, - enumerable: true - }; - - if (setter) { - setter = embind__requireFunction(setterSignature, setter); - desc.set = (v) => { - var destructors = []; - setter(rawFieldPtr, fieldType['toWireType'](destructors, v)); - runDestructors(destructors); - }; - } - - Object.defineProperty(classType.registeredClass.constructor, fieldName, desc); - return []; - }); - - return []; - }); - }, - - _embind_create_inheriting_constructor__deps: [ - '$createNamedFunction', '$Emval', - '$PureVirtualError', '$readLatin1String', - '$registerInheritedInstance', - '$requireRegisteredType', '$throwBindingError', - '$unregisterInheritedInstance', '$detachFinalizer', '$attachFinalizer'], - _embind_create_inheriting_constructor: (constructorName, wrapperType, properties) => { - constructorName = readLatin1String(constructorName); - wrapperType = requireRegisteredType(wrapperType, 'wrapper'); - properties = Emval.toValue(properties); - - var registeredClass = wrapperType.registeredClass; - var wrapperPrototype = registeredClass.instancePrototype; - var baseClass = registeredClass.baseClass; - var baseClassPrototype = baseClass.instancePrototype; - var baseConstructor = registeredClass.baseClass.constructor; - var ctor = createNamedFunction(constructorName, function(...args) { - registeredClass.baseClass.pureVirtualFunctions.forEach(function(name) { - if (this[name] === baseClassPrototype[name]) { - throw new PureVirtualError(`Pure virtual function ${name} must be implemented in JavaScript`); - } - }.bind(this)); - - Object.defineProperty(this, '__parent', { - value: wrapperPrototype - }); - this["__construct"](...args); - }); - - // It's a little nasty that we're modifying the wrapper prototype here. - - wrapperPrototype["__construct"] = function __construct(...args) { - if (this === wrapperPrototype) { - throwBindingError("Pass correct 'this' to __construct"); - } - - var inner = baseConstructor["implement"](this, ...args); - detachFinalizer(inner); - var $$ = inner.$$; - inner["notifyOnDestruction"](); - $$.preservePointerOnDelete = true; - Object.defineProperties(this, { $$: { - value: $$ - }}); - attachFinalizer(this); - registerInheritedInstance(registeredClass, $$.ptr, this); - }; - - wrapperPrototype["__destruct"] = function __destruct() { - if (this === wrapperPrototype) { - throwBindingError("Pass correct 'this' to __destruct"); - } - - detachFinalizer(this); - unregisterInheritedInstance(registeredClass, this.$$.ptr); - }; - - ctor.prototype = Object.create(wrapperPrototype); - Object.assign(ctor.prototype, properties); - return Emval.toHandle(ctor); - }, - - $char_0: '0'.charCodeAt(0), - $char_9: '9'.charCodeAt(0), - $makeLegalFunctionName__deps: ['$char_0', '$char_9'], - $makeLegalFunctionName: (name) => { - if (undefined === name) { - return '_unknown'; - } - name = name.replace(/[^a-zA-Z0-9_]/g, '$'); - var f = name.charCodeAt(0); - if (f >= char_0 && f <= char_9) { - return `_${name}`; - } - return name; - }, - - _embind_register_smart_ptr__deps: ['$RegisteredPointer', '$embind__requireFunction', '$whenDependentTypesAreResolved'], - _embind_register_smart_ptr: (rawType, - rawPointeeType, - name, - sharingPolicy, - getPointeeSignature, - rawGetPointee, - constructorSignature, - rawConstructor, - shareSignature, - rawShare, - destructorSignature, - rawDestructor) => { - name = readLatin1String(name); - rawGetPointee = embind__requireFunction(getPointeeSignature, rawGetPointee); - rawConstructor = embind__requireFunction(constructorSignature, rawConstructor); - rawShare = embind__requireFunction(shareSignature, rawShare); - rawDestructor = embind__requireFunction(destructorSignature, rawDestructor); - - whenDependentTypesAreResolved([rawType], [rawPointeeType], (pointeeType) => { - pointeeType = pointeeType[0]; - - var registeredPointer = new RegisteredPointer(name, - pointeeType.registeredClass, - false, - false, - // smart pointer properties - true, - pointeeType, - sharingPolicy, - rawGetPointee, - rawConstructor, - rawShare, - rawDestructor); - return [registeredPointer]; - }); - }, - - _embind_register_enum__docs: '/** @suppress {globalThis} */', - _embind_register_enum__deps: ['$exposePublicSymbol', '$enumReadValueFromPointer', - '$readLatin1String', '$registerType'], - _embind_register_enum: (rawType, name, size, isSigned) => { - name = readLatin1String(name); - - function ctor() {} - ctor.values = {}; - - registerType(rawType, { - name, - constructor: ctor, - 'fromWireType': function(c) { - return this.constructor.values[c]; - }, - 'toWireType': (destructors, c) => c.value, - 'argPackAdvance': GenericWireTypeSize, - 'readValueFromPointer': enumReadValueFromPointer(name, size, isSigned), - destructorFunction: null, - }); - exposePublicSymbol(name, ctor); - }, - - _embind_register_enum_value__deps: ['$createNamedFunction', '$readLatin1String', '$requireRegisteredType'], - _embind_register_enum_value: (rawEnumType, name, enumValue) => { - var enumType = requireRegisteredType(rawEnumType, 'enum'); - name = readLatin1String(name); - - var Enum = enumType.constructor; - - var Value = Object.create(enumType.constructor.prototype, { - value: {value: enumValue}, - constructor: {value: createNamedFunction(`${enumType.name}_${name}`, function() {})}, - }); - Enum.values[enumValue] = Value; - Enum[name] = Value; - }, - - _embind_register_constant__deps: ['$readLatin1String', '$whenDependentTypesAreResolved'], - _embind_register_constant: (name, type, value) => { - name = readLatin1String(name); - whenDependentTypesAreResolved([], [type], (type) => { - type = type[0]; - Module[name] = type['fromWireType'](value); - return []; - }); - }, -}; - -addToLibrary(LibraryEmbind); diff --git a/src/embind/embind_gen.js b/src/embind/embind_gen.js deleted file mode 100644 index 125d8596d2bbe..0000000000000 --- a/src/embind/embind_gen.js +++ /dev/null @@ -1,819 +0,0 @@ -// Copyright 2023 The Emscripten Authors. All rights reserved. -// Emscripten is available under two separate licenses, the MIT license and the -// University of Illinois/NCSA Open Source License. Both these licenses can be -// found in the LICENSE file. -#include "embind/embind_shared.js" - -var LibraryEmbind = { - - $moduleDefinitions: [], - // Function signatures that have already been generated for JS generation. - $emittedFunctions: 'new Set()', - - $PrimitiveType: class { - constructor(typeId, name, destructorType) { - this.typeId = typeId; - this.name = name; - this.destructorType = destructorType; - } - }, - $IntegerType: class { - constructor(typeId) { - this.typeId = typeId; - this.destructorType = 'none'; - } - }, - $Argument: class { - constructor(name, type) { - this.name = name; - this.type = type; - } - }, - $UserType: class { - constructor(typeId, name) { - this.typeId = typeId; - this.name = name; - this.destructorType = 'none'; // Same as emval. - } - }, - $OptionalType: class { - constructor(type) { - this.type = type; - this.destructorType = 'none'; // Same as emval. - } - }, - $FunctionDefinition__deps: ['$createJsInvoker', '$createJsInvokerSignature', '$emittedFunctions'], - $FunctionDefinition: class { - constructor(name, returnType, argumentTypes, functionIndex, thisType = null, isConstructor = false, isAsync = false) { - this.name = name; - this.returnType = returnType; - this.argumentTypes = argumentTypes; - this.functionIndex = functionIndex; - this.thisType = thisType; - this.isConstructor = isConstructor; - this.isAsync = isAsync; - } - - printSignature(nameMap, out) { - out.push('('); - const argOut = []; - // Work backwards on the arguments, so optional types can be replaced - // with TS optional params until we see the first non-optional argument. - let seenNonOptional = false; - for (let i = this.argumentTypes.length - 1; i >= 0; i--) { - const arg = this.argumentTypes[i]; - let argType; - let argName; - if (arg.type instanceof OptionalType && !seenNonOptional) { - argType = nameMap(arg.type.type); - argName = arg.name + '?'; - } else { - seenNonOptional = true; - argType = nameMap(arg.type); - argName = arg.name; - } - argOut.unshift(`${argName}: ${argType}`); - } - - out.push(argOut.join(', ')); - let returnType = this.returnType; - // Constructors can return a pointer, but it will be a non-null pointer. - // Change the return type to the class type so the TS output doesn't - // have `| null`. - if (this.isConstructor && this.returnType instanceof PointerDefinition) { - returnType = this.returnType.classType; - } - out.push(`): ${nameMap(returnType, true)}`); - } - - printFunction(nameMap, out) { - out.push(`${this.name}`); - this.printSignature(nameMap, out); - } - - printModuleEntry(nameMap, out) { - out.push(' '); - this.printFunction(nameMap, out); - out.push(';\n'); - } - - // Convert a type definition in this file to something that matches the type - // object in embind.js `registerType()`. - convertToEmbindType(type) { - const ret = { - name: type.name, - }; - switch (type.destructorType) { - case 'none': - ret.destructorFunction = null; - break; - case 'function': - ret.destructorFunction = true; - break; - case 'stack': - // Intentionally empty since embind uses `undefined` for this type. - break; - default: - throw new Error(`Bad destructor type '${type.destructorType}'`); - } - return ret; - } - - printJs(out) { - const argTypes = [this.convertToEmbindType(this.returnType)]; - if (this.thisType) { - argTypes.push(this.convertToEmbindType(this.thisType)); - } else { - argTypes.push(null); - } - for (const argType of this.argumentTypes) { - argTypes.push(this.convertToEmbindType(argType.type)); - } - const signature = createJsInvokerSignature(argTypes, !!this.thisType, this.returnType.name !== 'void', this.isAsync) - if (emittedFunctions.has(signature)) { - return; - } - emittedFunctions.add(signature); - let [args, body] = createJsInvoker(argTypes, !!this.thisType, this.returnType.name !== 'void', this.isAsync); - out.push( - // The ${""} is hack to workaround the preprocessor replacing "function". - `'${signature}': f${""}unction(${args.join(',')}) {\n${body}},` - ); - } - }, - $PointerDefinition: class { - constructor(classType, isConst, isSmartPointer) { - this.classType = classType; - this.isConst = isConst; - this.isSmartPointer = isSmartPointer; - this.destructorType = 'none'; - if (isSmartPointer || classType.base) { - this.destructorType = 'stack'; - } - } - }, - $ClassDefinition: class { - constructor(typeId, name, base = null) { - this.typeId = typeId; - this.name = name; - this.methods = []; - this.staticMethods = []; - this.staticProperties = []; - this.constructors = []; - this.base = base; - this.properties = []; - this.destructorType = 'none'; - if (base) { - this.destructorType = 'stack'; - } - } - - print(nameMap, out) { - out.push(`export interface ${this.name}`); - if (this.base) { - out.push(` extends ${this.base.name}`); - } - out.push(' {\n'); - for (const property of this.properties) { - out.push(' '); - property.print(nameMap, out); - out.push(';\n'); - } - for (const method of this.methods) { - out.push(' '); - method.printFunction(nameMap, out); - out.push(';\n'); - } - out.push(' delete(): void;\n'); - out.push('}\n\n'); - } - - printModuleEntry(nameMap, out) { - out.push(` ${this.name}: {`); - const entries = []; - for (const construct of this.constructors) { - const entry = []; - entry.push('new'); - construct.printSignature(nameMap, entry); - entries.push(entry.join('')); - } - for (const method of this.staticMethods) { - const entry = []; - method.printFunction(nameMap, entry); - entries.push(entry.join('')); - } - for (const prop of this.staticProperties) { - const entry = []; - prop.print(nameMap, entry); - entries.push(entry.join('')); - } - out.push(entries.join('; ')); - out.push('};\n'); - } - - printJs(out) { - out.push(`// class ${this.name}\n`); - if (this.constructors.length) { - out.push(`// constructors\n`); - for (const construct of this.constructors) { - construct.printJs(out); - } - } - if (this.staticMethods.length) { - out.push(`// static methods\n`); - for (const method of this.staticMethods) { - method.printJs(out); - } - } - if (this.methods.length) { - out.push(`// methods\n`); - for (const method of this.methods) { - method.printJs(out); - } - } - out.push('\n'); - } - - }, - $ClassProperty: class { - constructor(type, name, readonly) { - this.type = type; - this.name = name; - this.readonly = readonly; - } - - print(nameMap, out) { - out.push(`${this.readonly ? 'readonly ' : ''}${this.name}: ${nameMap(this.type)}`); - } - }, - $ConstantDefinition: class { - constructor(type, name) { - this.type = type; - this.name = name; - } - - printModuleEntry(nameMap, out) { - out.push(` ${this.name}: ${nameMap(this.type)};\n`); - } - }, - $EnumDefinition: class { - constructor(typeId, name) { - this.typeId = typeId; - this.name = name; - this.items = []; - this.destructorType = 'none'; - } - - print(nameMap, out) { - out.push(`export interface ${this.name}Value {\n`); - out.push(' value: T;\n}\n'); - out.push(`export type ${this.name} = `); - if (this.items.length === 0) { - out.push('never/* Empty Enumerator */'); - } else { - const outItems = []; - for (const [name, value] of this.items) { - outItems.push(`${this.name}Value<${value}>`); - } - out.push(outItems.join('|')); - } - out.push(';\n\n'); - } - - printModuleEntry(nameMap, out) { - out.push(` ${this.name}: {`); - const outItems = []; - for (const [name, value] of this.items) { - outItems.push(`${name}: ${this.name}Value<${value}>`); - } - out.push(outItems.join(', ')); - out.push('};\n'); - } - }, - $ValueArrayDefinition: class { - constructor(typeId, name) { - this.typeId = typeId; - this.name = name; - this.elementTypeIds = []; - this.elements = []; - this.destructorType = 'function'; - } - - print(nameMap, out) { - out.push(`export type ${this.name} = [ `); - const outElements = []; - for (const type of this.elements) { - outElements.push(nameMap(type)); - } - out.push(outElements.join(', ')) - out.push(' ];\n\n'); - } - }, - $ValueObjectDefinition: class { - constructor(typeId, name) { - this.typeId = typeId; - this.name = name; - this.fieldTypeIds = []; - this.fieldNames = []; - this.fields = []; - this.destructorType = 'function'; - } - - print(nameMap, out) { - out.push(`export type ${this.name} = {\n`); - const outFields = []; - for (const {name, type} of this.fields) { - outFields.push(` ${name}: ${nameMap(type)}`); - } - out.push(outFields.join(',\n')) - out.push('\n};\n\n'); - } - }, - $TsPrinter__deps: ['$OptionalType'], - $TsPrinter: class { - constructor(definitions) { - this.definitions = definitions; - const jsString = 'EmbindString'; // Type alias for multiple types. - // The mapping is in the format of '' => ['toWireType', 'fromWireType'] - // or if the to/from wire types are the same use a single element. - this.builtInToJsName = new Map([ - ['bool', ['boolean']], - ['float', ['number']], - ['double', ['number']], -#if MEMORY64 - ['long', ['bigint']], - ['unsigned long', ['bigint']], -#endif -#if WASM_BIGINT - ['int64_t', ['bigint']], - ['uint64_t', ['bigint']], -#endif - ['void', ['void']], - ['std::string', [jsString, 'string']], - ['std::basic_string', [jsString, 'string']], - ['std::wstring', ['string']], - ['std::u16string', ['string']], - ['std::u32string', ['string']], - ['emscripten::val', ['any']], - ]); - // Signal that the type alias for EmbindString is needed. - this.usedEmbindString = false; - } - - typeToJsName(type, isFromWireType = false) { - if (type instanceof IntegerType) { - return 'number'; - } - if (type instanceof PrimitiveType) { - if (!this.builtInToJsName.has(type.name)) { - throw new Error(`Missing primitive type to TS type for '${type.name}'`); - } - const [toWireType, fromWireType = toWireType] = this.builtInToJsName.get(type.name); - const tsName = isFromWireType ? fromWireType : toWireType; - if (tsName === 'EmbindString') { - this.usedEmbindString = true; - } - return tsName; - } - if (type instanceof PointerDefinition) { - return `${this.typeToJsName(type.classType)} | null`; - } - if (type instanceof OptionalType) { - return `${this.typeToJsName(type.type)} | undefined`; - } - return type.name; - } - - print() { - const out = []; - for (const def of this.definitions) { - if (!def.print) { - continue; - } - def.print(this.typeToJsName.bind(this), out); - } - // Print module definitions - out.push('interface EmbindModule {\n'); - for (const def of this.definitions) { - if (!def.printModuleEntry) { - continue; - } - def.printModuleEntry(this.typeToJsName.bind(this), out); - } - out.push('}\n'); - if (this.usedEmbindString) { - out.unshift('type EmbindString = ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string;\n'); - } - console.log(out.join('')); - } - }, - - $JsPrinter: class { - constructor(definitions) { - this.definitions = definitions; - } - - print() { - const out = ['{\n']; - for (const def of this.definitions) { - if (!def.printJs) { - continue; - } - def.printJs(out); - } - out.push('}') - console.log(out.join('')); - } - }, - - $registerType__deps: ['$sharedRegisterType'], - $registerType: function(rawType, registeredInstance, options = {}) { - return sharedRegisterType(rawType, registeredInstance, options); - }, - $registerPrimitiveType__deps: ['$registerType', '$PrimitiveType'], - $registerPrimitiveType: (id, name, destructorType) => { - name = readLatin1String(name); - registerType(id, new PrimitiveType(id, name, destructorType)); - }, - $registerIntegerType__deps: ['$registerType', '$IntegerType'], - $registerIntegerType: (id) => { - registerType(id, new IntegerType(id)); - }, - $createFunctionDefinition__deps: ['$FunctionDefinition', '$heap32VectorToArray', '$readLatin1String', '$Argument', '$whenDependentTypesAreResolved', '$getFunctionName', '$getFunctionArgsName', '$PointerDefinition', '$ClassDefinition'], - $createFunctionDefinition: (name, argCount, rawArgTypesAddr, functionIndex, hasThis, isConstructor, isAsync, cb) => { - const argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); - name = typeof name === 'string' ? name : readLatin1String(name); - - whenDependentTypesAreResolved([], argTypes, function (argTypes) { - const argsName = getFunctionArgsName(name); - name = getFunctionName(name); - const returnType = argTypes[0]; - let thisType = null; - let argStart = 1; - if (hasThis) { - thisType = argTypes[1]; - if (thisType instanceof PointerDefinition) { - thisType = argTypes[1].classType; - } - if (!(thisType instanceof ClassDefinition)) { - throw new Error('This type must be class definition for: ' + name); - } - argStart = 2; - } - if (argsName.length) - assert(argsName.length == (argTypes.length - hasThis - 1), 'Argument names should match number of parameters.'); - const args = []; - for (let i = argStart, x = 0; i < argTypes.length; i++) { - if (x < argsName.length) { - args.push(new Argument(argsName[x++], argTypes[i])); - } else { - args.push(new Argument(`_${i - argStart}`, argTypes[i])); - } - } - const funcDef = new FunctionDefinition(name, returnType, args, functionIndex, thisType, isConstructor, isAsync); - cb(funcDef); - return []; - }); - }, - _embind_register_void__deps: ['$registerPrimitiveType'], - _embind_register_void: (rawType, name) => { - registerPrimitiveType(rawType, name, 'none'); - }, - _embind_register_bool__deps: ['$registerPrimitiveType'], - _embind_register_bool: (rawType, name, trueValue, falseValue) => { - registerPrimitiveType(rawType, name, 'none'); - }, - _embind_register_integer__deps: ['$registerIntegerType'], - _embind_register_integer: (primitiveType, name, size, minRange, maxRange) => { - registerIntegerType(primitiveType, name); - }, - _embind_register_bigint: (primitiveType, name, size, minRange, maxRange) => { - registerPrimitiveType(primitiveType, name, 'none'); - }, - _embind_register_float__deps: ['$registerPrimitiveType'], - _embind_register_float: (rawType, name, size) => { - registerPrimitiveType(rawType, name, 'none'); - }, - _embind_register_std_string__deps: ['$registerPrimitiveType'], - _embind_register_std_string: (rawType, name) => { - registerPrimitiveType(rawType, name, 'function'); - }, - _embind_register_std_wstring: (rawType, charSize, name) => { - registerPrimitiveType(rawType, name, 'function'); - }, - _embind_register_emval__deps: ['$registerType', '$PrimitiveType'], - _embind_register_emval: (rawType) => { - registerType(rawType, new PrimitiveType(rawType, 'emscripten::val', 'none')); - }, - _embind_register_user_type__deps: ['$registerType', '$readLatin1String', '$UserType'], - _embind_register_user_type: (rawType, name) => { - name = readLatin1String(name); - registerType(rawType, new UserType(rawType, name)); - }, - _embind_register_optional__deps: ['_embind_register_emval', '$OptionalType'], - _embind_register_optional: (rawOptionalType, rawType) => { - whenDependentTypesAreResolved([rawOptionalType], [rawType], function(type) { - type = type[0]; - return [new OptionalType(type)]; - }); - }, - _embind_register_memory_view: (rawType, dataTypeIndex, name) => { - // TODO - }, - _embind_register_function__deps: ['$moduleDefinitions', '$createFunctionDefinition'], - _embind_register_function: (name, argCount, rawArgTypesAddr, signature, rawInvoker, fn, isAsync) => { - createFunctionDefinition(name, argCount, rawArgTypesAddr, fn, false, false, isAsync, (funcDef) => { - moduleDefinitions.push(funcDef); - }); - }, - _embind_register_class__deps: ['$readLatin1String', '$ClassDefinition', '$whenDependentTypesAreResolved', '$moduleDefinitions', '$PointerDefinition'], - _embind_register_class: function(rawType, - rawPointerType, - rawConstPointerType, - baseClassRawType, - getActualTypeSignature, - getActualType, - upcastSignature, - upcast, - downcastSignature, - downcast, - name, - destructorSignature, - rawDestructor) { - name = readLatin1String(name); - whenDependentTypesAreResolved( - [rawType, rawPointerType, rawConstPointerType], - baseClassRawType ? [baseClassRawType] : [], - function(base) { - const hasBase = base.length; - const classDef = new ClassDefinition(rawType, name, hasBase ? base[0] : null); - moduleDefinitions.push(classDef); - - const pointer = new PointerDefinition(classDef, false, false); - const constPointer = new PointerDefinition(classDef, true, false); - return [classDef, pointer, constPointer]; - } - ); - - }, - _embind_register_class_constructor__deps: ['$whenDependentTypesAreResolved', '$createFunctionDefinition'], - _embind_register_class_constructor: function( - rawClassType, - argCount, - rawArgTypesAddr, - invokerSignature, - invoker, - rawConstructor - ) { - whenDependentTypesAreResolved([], [rawClassType], function(classType) { - classType = classType[0]; - createFunctionDefinition(`constructor ${classType.name}`, argCount, rawArgTypesAddr, rawConstructor, false, true, false, (funcDef) => { - classType.constructors.push(funcDef); - }); - return []; - }); - }, - _embind_register_class_function__deps: ['$createFunctionDefinition'], - _embind_register_class_function: function(rawClassType, - methodName, - argCount, - rawArgTypesAddr, // [ReturnType, ThisType, Args...] - invokerSignature, - rawInvoker, - context, - isPureVirtual, - isAsync) { - createFunctionDefinition(methodName, argCount, rawArgTypesAddr, context, true, false, isAsync, (funcDef) => { - const classDef = funcDef.thisType; - classDef.methods.push(funcDef); - }); - }, - _embind_register_class_property__deps: [ - '$readLatin1String', '$whenDependentTypesAreResolved', '$ClassProperty'], - _embind_register_class_property: function(classType, - fieldName, - getterReturnType, - getterSignature, - getter, - getterContext, - setterArgumentType, - setterSignature, - setter, - setterContext) { - fieldName = readLatin1String(fieldName); - const readonly = setter === 0; - assert(readonly || getterReturnType === setterArgumentType, 'Mismatched getter and setter types are not supported.'); - whenDependentTypesAreResolved([], [classType], function(classType) { - classType = classType[0]; - whenDependentTypesAreResolved([], [getterReturnType], function(types) { - const prop = new ClassProperty(types[0], fieldName, readonly); - classType.properties.push(prop); - return []; - }); - return []; - }); - }, - _embind_register_class_class_function__deps: ['$createFunctionDefinition'], - _embind_register_class_class_function: function(rawClassType, - methodName, - argCount, - rawArgTypesAddr, - invokerSignature, - rawInvoker, - fn, - isAsync) { - whenDependentTypesAreResolved([], [rawClassType], function(classType) { - classType = classType[0]; - createFunctionDefinition(methodName, argCount, rawArgTypesAddr, fn, false, false, isAsync, (funcDef) => { - classType.staticMethods.push(funcDef); - }); - return []; - }); - }, - _embind_register_class_class_property__deps: [ - '$readLatin1String', '$whenDependentTypesAreResolved', '$ClassProperty'], - _embind_register_class_class_property: (rawClassType, - fieldName, - rawFieldType, - rawFieldPtr, - getterSignature, - getter, - setterSignature, - setter) => { - fieldName = readLatin1String(fieldName); - whenDependentTypesAreResolved([], [rawClassType], function(classType) { - classType = classType[0]; - whenDependentTypesAreResolved([], [rawFieldType], function(types) { - const prop = new ClassProperty(types[0], fieldName); - classType.staticProperties.push(prop); - return []; - }); - return []; - }); - }, - // Stub function. This is called a when extending an object and not needed for TS generation. - _embind_create_inheriting_constructor: (constructorName, wrapperType, properties) => {}, - _embind_register_enum__deps: ['$readLatin1String', '$EnumDefinition', '$moduleDefinitions'], - _embind_register_enum: function(rawType, name, size, isSigned) { - name = readLatin1String(name); - const enumDef = new EnumDefinition(rawType, name); - registerType(rawType, enumDef); - moduleDefinitions.push(enumDef); - }, - _embind_register_enum_value__deps: ['$readLatin1String', '$requireRegisteredType'], - _embind_register_enum_value: function(rawEnumType, name, enumValue) { - name = readLatin1String(name); - const enumDef = requireRegisteredType(rawEnumType, name); - enumDef.items.push([name, enumValue]); - }, - _embind_register_constant__deps: ['$readLatin1String', '$ConstantDefinition', '$whenDependentTypesAreResolved', '$moduleDefinitions'], - _embind_register_constant: function(name, typeId, value) { - name = readLatin1String(name); - whenDependentTypesAreResolved([], [typeId], function(types) { - const def = new ConstantDefinition(types[0], name); - moduleDefinitions.push(def); - return []; - }); - }, - _embind_register_value_array__deps: [ - '$readLatin1String', '$ValueArrayDefinition', '$tupleRegistrations'], - _embind_register_value_array: function( - rawType, - name, - constructorSignature, - rawConstructor, - destructorSignature, - rawDestructor - ) { - name = readLatin1String(name); - const valueArray = new ValueArrayDefinition(rawType, name); - tupleRegistrations[rawType] = valueArray; - }, - _embind_register_value_array_element__deps: ['$tupleRegistrations'], - _embind_register_value_array_element: function( - rawTupleType, - getterReturnType, - getterSignature, - getter, - getterContext, - setterArgumentType, - setterSignature, - setter, - setterContext - ) { - const valueArray = tupleRegistrations[rawTupleType]; - assert(getterReturnType === setterArgumentType, 'Mismatched getter and setter types are not supported.'); - valueArray.elementTypeIds.push(getterReturnType); - }, - _embind_finalize_value_array__deps: ['$whenDependentTypesAreResolved', '$moduleDefinitions', '$tupleRegistrations'], - _embind_finalize_value_array: function(rawTupleType) { - const valueArray = tupleRegistrations[rawTupleType]; - delete tupleRegistrations[rawTupleType]; - whenDependentTypesAreResolved([rawTupleType], valueArray.elementTypeIds, function(types) { - moduleDefinitions.push(valueArray); - valueArray.elements = types; - return [valueArray]; - }); - }, - _embind_register_value_object__deps: ['$readLatin1String', '$ValueObjectDefinition', '$structRegistrations'], - _embind_register_value_object: function( - rawType, - name, - constructorSignature, - rawConstructor, - destructorSignature, - rawDestructor - ) { - name = readLatin1String(name); - const valueObject = new ValueObjectDefinition(rawType, name); - structRegistrations[rawType] = valueObject; - }, - _embind_register_value_object_field__deps: [ - '$readLatin1String', '$structRegistrations'], - _embind_register_value_object_field: function( - structType, - fieldName, - getterReturnType, - getterSignature, - getter, - getterContext, - setterArgumentType, - setterSignature, - setter, - setterContext - ) { - const valueObject = structRegistrations[structType]; - assert(getterReturnType === setterArgumentType, 'Mismatched getter and setter types are not supported.'); - valueObject.fieldTypeIds.push(getterReturnType); - valueObject.fieldNames.push(readLatin1String(fieldName)); - }, - _embind_finalize_value_object__deps: ['$moduleDefinitions', '$whenDependentTypesAreResolved', '$structRegistrations'], - _embind_finalize_value_object: function(structType) { - const valueObject = structRegistrations[structType]; - delete structRegistrations[structType]; - whenDependentTypesAreResolved([structType], valueObject.fieldTypeIds, function(types) { - moduleDefinitions.push(valueObject); - for (let i = 0; i < types.length; i++) { - valueObject.fields.push({ - name: valueObject.fieldNames[i], - type: types[i], - }); - } - return [valueObject]; - }); - }, - _embind_register_smart_ptr__deps: ['$whenDependentTypesAreResolved'], - _embind_register_smart_ptr: function(rawType, - rawPointeeType, - name, - sharingPolicy, - getPointeeSignature, - rawGetPointee, - constructorSignature, - rawConstructor, - shareSignature, - rawShare, - destructorSignature, - rawDestructor) { - whenDependentTypesAreResolved([rawType], [rawPointeeType], function(pointeeType) { - const smartPointer = new PointerDefinition(pointeeType[0], false, true); - return [smartPointer]; - }); - }, - -#if EMBIND_AOT - $embindEmitAotJs__deps: ['$awaitingDependencies', '$throwBindingError', '$getTypeName', '$moduleDefinitions', '$JsPrinter'], - $embindEmitAotJs__postset: 'addOnInit(embindEmitAotJs);', - $embindEmitAotJs: () => { - for (const typeId in awaitingDependencies) { - throwBindingError(`Missing binding for type: '${getTypeName(typeId)}' typeId: ${typeId}`); - } - const printer = new JsPrinter(moduleDefinitions); - printer.print(); - }, -#else // EMBIND_AOT - $embindEmitTypes__deps: ['$awaitingDependencies', '$throwBindingError', '$getTypeName', '$moduleDefinitions', '$TsPrinter'], - $embindEmitTypes__postset: 'addOnInit(embindEmitTypes);', - $embindEmitTypes: () => { - for (const typeId in awaitingDependencies) { - throwBindingError(`Missing binding for type: '${getTypeName(typeId)}' typeId: ${typeId}`); - } - const printer = new TsPrinter(moduleDefinitions); - printer.print(); - }, -#endif - - // Stub functions used by eval, but not needed for TS generation: - $makeLegalFunctionName: () => assert(false, 'stub function should not be called'), - $newFunc: () => assert(false, 'stub function should not be called'), - $runDestructors: () => assert(false, 'stub function should not be called'), - $createNamedFunction: () => assert(false, 'stub function should not be called'), -}; - -#if EMBIND_AOT -extraLibraryFuncs.push('$embindEmitAotJs'); -#else -extraLibraryFuncs.push('$embindEmitTypes'); -#endif - -addToLibrary(LibraryEmbind); diff --git a/src/embind/embind_shared.js b/src/embind/embind_shared.js deleted file mode 100644 index 3e48a6fdc68d1..0000000000000 --- a/src/embind/embind_shared.js +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2023 The Emscripten Authors. All rights reserved. -// Emscripten is available under two separate licenses, the MIT license and the -// University of Illinois/NCSA Open Source License. Both these licenses can be -// found in the LICENSE file. -var LibraryEmbindShared = { - $InternalError__postset: "InternalError = Module['InternalError'] = class InternalError extends Error { constructor(message) { super(message); this.name = 'InternalError'; }}", - $InternalError: undefined, - $BindingError__postset: "BindingError = Module['BindingError'] = class BindingError extends Error { constructor(message) { super(message); this.name = 'BindingError'; }}", - $BindingError: undefined, - - $throwInternalError__deps: ['$InternalError'], - $throwInternalError: (message) => { throw new InternalError(message); }, - - $throwBindingError__deps: ['$BindingError'], - $throwBindingError: (message) => { throw new BindingError(message); }, - - // typeID -> { toWireType: ..., fromWireType: ... } - $registeredTypes: {}, - - // typeID -> [callback] - $awaitingDependencies: {}, - - // typeID -> [dependentTypes] - $typeDependencies: {}, - - $tupleRegistrations: {}, - - $structRegistrations: {}, - - $sharedRegisterType__deps: [ - '$awaitingDependencies', '$registeredTypes', - '$typeDependencies', '$throwBindingError', - '$whenDependentTypesAreResolved'], - $sharedRegisterType__docs: '/** @param {Object=} options */', - $sharedRegisterType: function(rawType, registeredInstance, options = {}) { - var name = registeredInstance.name; - if (!rawType) { - throwBindingError(`type "${name}" must have a positive integer typeid pointer`); - } - if (registeredTypes.hasOwnProperty(rawType)) { - if (options.ignoreDuplicateRegistrations) { - return; - } else { - throwBindingError(`Cannot register type '${name}' twice`); - } - } - - registeredTypes[rawType] = registeredInstance; - delete typeDependencies[rawType]; - - if (awaitingDependencies.hasOwnProperty(rawType)) { - var callbacks = awaitingDependencies[rawType]; - delete awaitingDependencies[rawType]; - callbacks.forEach((cb) => cb()); - } - }, - - $whenDependentTypesAreResolved__deps: [ - '$awaitingDependencies', '$registeredTypes', - '$typeDependencies', '$throwInternalError'], - $whenDependentTypesAreResolved: (myTypes, dependentTypes, getTypeConverters) => { - myTypes.forEach(function(type) { - typeDependencies[type] = dependentTypes; - }); - - function onComplete(typeConverters) { - var myTypeConverters = getTypeConverters(typeConverters); - if (myTypeConverters.length !== myTypes.length) { - throwInternalError('Mismatched type converter count'); - } - for (var i = 0; i < myTypes.length; ++i) { - registerType(myTypes[i], myTypeConverters[i]); - } - } - - var typeConverters = new Array(dependentTypes.length); - var unregisteredTypes = []; - var registered = 0; - dependentTypes.forEach((dt, i) => { - if (registeredTypes.hasOwnProperty(dt)) { - typeConverters[i] = registeredTypes[dt]; - } else { - unregisteredTypes.push(dt); - if (!awaitingDependencies.hasOwnProperty(dt)) { - awaitingDependencies[dt] = []; - } - awaitingDependencies[dt].push(() => { - typeConverters[i] = registeredTypes[dt]; - ++registered; - if (registered === unregisteredTypes.length) { - onComplete(typeConverters); - } - }); - } - }); - if (0 === unregisteredTypes.length) { - onComplete(typeConverters); - } - }, - - $embind_charCodes__deps: ['$embind_init_charCodes'], - $embind_charCodes__postset: "embind_init_charCodes()", - $embind_charCodes: undefined, - $embind_init_charCodes: () => { - var codes = new Array(256); - for (var i = 0; i < 256; ++i) { - codes[i] = String.fromCharCode(i); - } - embind_charCodes = codes; - }, - $readLatin1String__deps: ['$embind_charCodes'], - $readLatin1String: (ptr) => { - var ret = ""; - var c = ptr; - while (HEAPU8[c]) { - ret += embind_charCodes[HEAPU8[c++]]; - } - return ret; - }, - $getTypeName__deps: ['$readLatin1String', '__getTypeName', 'free'], - $getTypeName: (type) => { - var ptr = ___getTypeName(type); - var rv = readLatin1String(ptr); - _free(ptr); - return rv; - }, - $getFunctionName__deps: [], - $getFunctionName: (signature) => { - signature = signature.trim(); - const argsIndex = signature.indexOf("("); - if (argsIndex !== -1) { -#if ASSERTIONS - assert(signature[signature.length - 1] == ")", "Parentheses for argument names should match."); -#endif - return signature.substr(0, argsIndex); - } else { - return signature; - } - }, - $getFunctionArgsName__deps: [], - $getFunctionArgsName: (signature) => { - signature = signature.trim(); - const argsIndex = signature.indexOf("(") + 1; - if (argsIndex !== 0) { -#if ASSERTIONS - assert(signature[signature.length - 1] == ")", "Parentheses for argument names should match."); -#endif - return signature.substr(argsIndex, signature.length - argsIndex - 1).replaceAll(" ", "").split(",").filter(n => n.length); - } else { - return []; - } - }, - $heap32VectorToArray: (count, firstElement) => { - var array = []; - for (var i = 0; i < count; i++) { - // TODO(https://github.com/emscripten-core/emscripten/issues/17310): - // Find a way to hoist the `>> 2` or `>> 3` out of this loop. - array.push({{{ makeGetValue('firstElement', `i * ${POINTER_SIZE}`, '*') }}}); - } - return array; - }, - - $requireRegisteredType__deps: [ - '$registeredTypes', '$getTypeName', '$throwBindingError'], - $requireRegisteredType: (rawType, humanName) => { - var impl = registeredTypes[rawType]; - if (undefined === impl) { - throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`); - } - return impl; - }, - - $usesDestructorStack(argTypes) { - // Skip return value at index 0 - it's not deleted here. - for (var i = 1; i < argTypes.length; ++i) { - // The type does not define a destructor function - must use dynamic stack - if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) { - return true; - } - } - return false; - }, - - // Many of the JS invoker functions are generic and can be reused for multiple - // function bindings. This function needs to match createJsInvoker and create - // a unique signature for any inputs that will create different invoker - // function outputs. - $createJsInvokerSignature(argTypes, isClassMethodFunc, returns, isAsync) { - const signature = [ - isClassMethodFunc ? 't' : 'f', - returns ? 't' : 'f', - isAsync ? 't' : 'f' - ]; - for (let i = isClassMethodFunc ? 1 : 2; i < argTypes.length; ++i) { - const arg = argTypes[i]; - let destructorSig = ''; - if (arg.destructorFunction === undefined) { - destructorSig = 'u'; - } else if (arg.destructorFunction === null) { - destructorSig = 'n'; - } else { - destructorSig = 't'; - } - signature.push(destructorSig); - } - return signature.join(''); - }, - - $createJsInvoker__deps: ['$usesDestructorStack'], - $createJsInvoker(argTypes, isClassMethodFunc, returns, isAsync) { - var needsDestructorStack = usesDestructorStack(argTypes); - var argCount = argTypes.length; - var argsList = ""; - var argsListWired = ""; - for (var i = 0; i < argCount - 2; ++i) { - argsList += (i!==0?", ":"")+"arg"+i; - argsListWired += (i!==0?", ":"")+"arg"+i+"Wired"; - } - - var invokerFnBody = ` - return function (${argsList}) { - if (arguments.length !== ${argCount - 2}) { - throwBindingError('function ' + humanName + ' called with ' + arguments.length + ' arguments, expected ${argCount - 2}'); - }`; - -#if EMSCRIPTEN_TRACING - invokerFnBody += `Module.emscripten_trace_enter_context('embind::' + humanName );\n`; -#endif - - if (needsDestructorStack) { - invokerFnBody += "var destructors = [];\n"; - } - - var dtorStack = needsDestructorStack ? "destructors" : "null"; - var args1 = ["humanName", "throwBindingError", "invoker", "fn", "runDestructors", "retType", "classParam"]; - -#if EMSCRIPTEN_TRACING - args1.push("Module"); -#endif - - if (isClassMethodFunc) { - invokerFnBody += "var thisWired = classParam['toWireType']("+dtorStack+", this);\n"; - } - - for (var i = 0; i < argCount - 2; ++i) { - invokerFnBody += "var arg"+i+"Wired = argType"+i+"['toWireType']("+dtorStack+", arg"+i+");\n"; - args1.push("argType"+i); - } - - if (isClassMethodFunc) { - argsListWired = "thisWired" + (argsListWired.length > 0 ? ", " : "") + argsListWired; - } - - invokerFnBody += - (returns || isAsync ? "var rv = ":"") + "invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n"; - - var returnVal = returns ? "rv" : ""; -#if ASYNCIFY == 1 - args1.push("Asyncify"); -#endif -#if ASYNCIFY - invokerFnBody += `function onDone(${returnVal}) {\n`; -#endif - - if (needsDestructorStack) { - invokerFnBody += "runDestructors(destructors);\n"; - } else { - for (var i = isClassMethodFunc?1:2; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here. Also skip class type if not a method. - var paramName = (i === 1 ? "thisWired" : ("arg"+(i - 2)+"Wired")); - if (argTypes[i].destructorFunction !== null) { - invokerFnBody += `${paramName}_dtor(${paramName});\n`; - args1.push(`${paramName}_dtor`); - } - } - } - - if (returns) { - invokerFnBody += "var ret = retType['fromWireType'](rv);\n" + -#if EMSCRIPTEN_TRACING - "Module.emscripten_trace_exit_context();\n" + -#endif - "return ret;\n"; - } else { -#if EMSCRIPTEN_TRACING - invokerFnBody += "Module.emscripten_trace_exit_context();\n"; -#endif - } - -#if ASYNCIFY == 1 - invokerFnBody += "}\n"; - invokerFnBody += `return Asyncify.currData ? Asyncify.whenDone().then(onDone) : onDone(${returnVal});\n` -#elif ASYNCIFY == 2 - invokerFnBody += "}\n"; - invokerFnBody += "return " + (isAsync ? "rv.then(onDone)" : `onDone(${returnVal})`) + ";"; -#endif - - invokerFnBody += "}\n"; - -#if ASSERTIONS - invokerFnBody = `if (arguments.length !== ${args1.length}){ throw new Error(humanName + "Expected ${args1.length} closure arguments " + arguments.length + " given."); }\n${invokerFnBody}`; -#endif - return [args1, invokerFnBody]; - } -}; - -addToLibrary(LibraryEmbindShared); diff --git a/src/embind/emval.js b/src/embind/emval.js deleted file mode 100644 index c2217b1ff5a4c..0000000000000 --- a/src/embind/emval.js +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright 2012 The Emscripten Authors. All rights reserved. -// Emscripten is available under two separate licenses, the MIT license and the -// University of Illinois/NCSA Open Source License. Both these licenses can be -// found in the LICENSE file. - -/*global Module:true, Runtime*/ -/*global HEAP32*/ -/*global newFunc*/ -/*global createNamedFunction*/ -/*global readLatin1String, stringToUTF8*/ -/*global requireRegisteredType, throwBindingError, runDestructors*/ -/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ - -// -- jshint doesn't understand library syntax, so we need to mark the symbols exposed here -/*global getStringOrSymbol, emval_freelist, emval_handles, Emval, __emval_unregister, count_emval_handles, emval_symbols, __emval_decref*/ -/*global emval_addMethodCaller, emval_methodCallers, addToLibrary, global, emval_lookupTypes, makeLegalFunctionName*/ -/*global emval_get_global*/ - -// Number of handles reserved for non-use (0) or common values w/o refcount. -{{{ - globalThis.EMVAL_RESERVED_HANDLES = 5; - globalThis.EMVAL_LAST_RESERVED_HANDLE = globalThis.EMVAL_RESERVED_HANDLES * 2 - 1; - null; -}}} -var LibraryEmVal = { - // Stack of handles available for reuse. - $emval_freelist: [], - // Array of alternating pairs (value, refcount). - $emval_handles: [], - $emval_symbols: {}, // address -> string - - $init_emval__deps: ['$count_emval_handles', '$emval_handles'], - $init_emval__postset: 'init_emval();', - $init_emval: () => { - // reserve 0 and some special values. These never get de-allocated. - emval_handles.push( - 0, 1, - undefined, 1, - null, 1, - true, 1, - false, 1, - ); - #if ASSERTIONS - assert(emval_handles.length === {{{ EMVAL_RESERVED_HANDLES }}} * 2); - #endif - Module['count_emval_handles'] = count_emval_handles; - }, - - $count_emval_handles__deps: ['$emval_freelist', '$emval_handles'], - $count_emval_handles: () => { - return emval_handles.length / 2 - {{{ EMVAL_RESERVED_HANDLES }}} - emval_freelist.length; - }, - - _emval_register_symbol__deps: ['$emval_symbols', '$readLatin1String'], - _emval_register_symbol: (address) => { - emval_symbols[address] = readLatin1String(address); - }, - - $getStringOrSymbol__deps: ['$emval_symbols', '$readLatin1String'], - $getStringOrSymbol: (address) => { - var symbol = emval_symbols[address]; - if (symbol === undefined) { - return readLatin1String(address); - } - return symbol; - }, - - $Emval__deps: ['$emval_freelist', '$emval_handles', '$throwBindingError', '$init_emval'], - $Emval: { - toValue: (handle) => { - if (!handle) { - throwBindingError('Cannot use deleted val. handle = ' + handle); - } - #if ASSERTIONS - // handle 2 is supposed to be `undefined`. - assert(handle === 2 || emval_handles[handle] !== undefined && handle % 2 === 0, `invalid handle: ${handle}`); - #endif - return emval_handles[handle]; - }, - - toHandle: (value) => { - switch (value) { - case undefined: return 2; - case null: return 4; - case true: return 6; - case false: return 8; - default:{ - const handle = emval_freelist.pop() || emval_handles.length; - emval_handles[handle] = value; - emval_handles[handle + 1] = 1; - return handle; - } - } - } - }, - - _emval_incref__deps: ['$emval_handles'], - _emval_incref: (handle) => { - if (handle > {{{ EMVAL_LAST_RESERVED_HANDLE }}}) { - emval_handles[handle + 1] += 1; - } - }, - - _emval_decref__deps: ['$emval_freelist', '$emval_handles'], - _emval_decref: (handle) => { - if (handle > {{{ EMVAL_LAST_RESERVED_HANDLE }}} && 0 === --emval_handles[handle + 1]) { - #if ASSERTIONS - assert(emval_handles[handle] !== undefined, `Decref for unallocated handle.`); - #endif - emval_handles[handle] = undefined; - emval_freelist.push(handle); - } - }, - - _emval_run_destructors__deps: ['_emval_decref', '$Emval', '$runDestructors'], - _emval_run_destructors: (handle) => { - var destructors = Emval.toValue(handle); - runDestructors(destructors); - __emval_decref(handle); - }, - - _emval_new_array__deps: ['$Emval'], - _emval_new_array: () => Emval.toHandle([]), - - _emval_new_array_from_memory_view__deps: ['$Emval'], - _emval_new_array_from_memory_view: (view) => { - view = Emval.toValue(view); - // using for..loop is faster than Array.from - var a = new Array(view.length); - for (var i = 0; i < view.length; i++) a[i] = view[i]; - return Emval.toHandle(a); - }, - - _emval_new_object__deps: ['$Emval'], - _emval_new_object: () => Emval.toHandle({}), - - _emval_new_cstring__deps: ['$getStringOrSymbol', '$Emval'], - _emval_new_cstring: (v) => Emval.toHandle(getStringOrSymbol(v)), - - _emval_new_u8string__deps: ['$Emval'], - _emval_new_u8string: (v) => Emval.toHandle(UTF8ToString(v)), - - _emval_new_u16string__deps: ['$Emval'], - _emval_new_u16string: (v) => Emval.toHandle(UTF16ToString(v)), - - _emval_take_value__deps: ['$Emval', '$requireRegisteredType'], - _emval_take_value: (type, arg) => { - type = requireRegisteredType(type, '_emval_take_value'); - var v = type['readValueFromPointer'](arg); - return Emval.toHandle(v); - }, - -#if !DYNAMIC_EXECUTION - $emval_get_global: () => { - if (typeof globalThis == 'object') { - return globalThis; - } - function testGlobal(obj) { - obj['$$$embind_global$$$'] = obj; - var success = typeof $$$embind_global$$$ == 'object' && obj['$$$embind_global$$$'] == obj; - if (!success) { - delete obj['$$$embind_global$$$']; - } - return success; - } - if (typeof $$$embind_global$$$ == 'object') { - return $$$embind_global$$$; - } - if (typeof global == 'object' && testGlobal(global)) { - $$$embind_global$$$ = global; - } else if (typeof self == 'object' && testGlobal(self)) { - $$$embind_global$$$ = self; // This works for both "window" and "self" (Web Workers) global objects - } - if (typeof $$$embind_global$$$ == 'object') { - return $$$embind_global$$$; - } - throw Error('unable to get global object.'); - }, -#else - // appease jshint (technically this code uses eval) - $emval_get_global: () => { - if (typeof globalThis == 'object') { - return globalThis; - } - return (function(){ - return Function; - })()('return this')(); - }, -#endif - _emval_get_global__deps: ['$Emval', '$getStringOrSymbol', '$emval_get_global'], - _emval_get_global: (name) => { - if (name===0) { - return Emval.toHandle(emval_get_global()); - } else { - name = getStringOrSymbol(name); - return Emval.toHandle(emval_get_global()[name]); - } - }, - - _emval_get_module_property__deps: ['$getStringOrSymbol', '$Emval'], - _emval_get_module_property: (name) => { - name = getStringOrSymbol(name); - return Emval.toHandle(Module[name]); - }, - - _emval_get_property__deps: ['$Emval'], - _emval_get_property: (handle, key) => { - handle = Emval.toValue(handle); - key = Emval.toValue(key); - return Emval.toHandle(handle[key]); - }, - - _emval_set_property__deps: ['$Emval'], - _emval_set_property: (handle, key, value) => { - handle = Emval.toValue(handle); - key = Emval.toValue(key); - value = Emval.toValue(value); - handle[key] = value; - }, - - $emval_returnValue__deps: ['$Emval'], - $emval_returnValue: (returnType, destructorsRef, handle) => { - var destructors = []; - var result = returnType['toWireType'](destructors, handle); - if (destructors.length) { - // void, primitives and any other types w/o destructors don't need to allocate a handle - {{{ makeSetValue('destructorsRef', '0', 'Emval.toHandle(destructors)', '*') }}}; - } - return result; - }, - - _emval_as__deps: ['$Emval', '$requireRegisteredType', '$emval_returnValue'], - _emval_as: (handle, returnType, destructorsRef) => { - handle = Emval.toValue(handle); - returnType = requireRegisteredType(returnType, 'emval::as'); - return emval_returnValue(returnType, destructorsRef, handle); - }, - - _emval_as_int64__deps: ['$Emval', '$requireRegisteredType'], - _emval_as_int64: (handle, returnType) => { - handle = Emval.toValue(handle); - returnType = requireRegisteredType(returnType, 'emval::as'); - return returnType['toWireType'](null, handle); - }, - - _emval_as_uint64__deps: ['$Emval', '$requireRegisteredType'], - _emval_as_uint64: (handle, returnType) => { - handle = Emval.toValue(handle); - returnType = requireRegisteredType(returnType, 'emval::as'); - return returnType['toWireType'](null, handle); - }, - - _emval_equals__deps: ['$Emval'], - _emval_equals: (first, second) => { - first = Emval.toValue(first); - second = Emval.toValue(second); - return first == second; - }, - - _emval_strictly_equals__deps: ['$Emval'], - _emval_strictly_equals: (first, second) => { - first = Emval.toValue(first); - second = Emval.toValue(second); - return first === second; - }, - - _emval_greater_than__deps: ['$Emval'], - _emval_greater_than: (first, second) => { - first = Emval.toValue(first); - second = Emval.toValue(second); - return first > second; - }, - - _emval_less_than__deps: ['$Emval'], - _emval_less_than: (first, second) => { - first = Emval.toValue(first); - second = Emval.toValue(second); - return first < second; - }, - - _emval_not__deps: ['$Emval'], - _emval_not: (object) => { - object = Emval.toValue(object); - return !object; - }, - - _emval_call__deps: ['$emval_methodCallers', '$Emval'], - _emval_call: (caller, handle, destructorsRef, args) => { - caller = emval_methodCallers[caller]; - handle = Emval.toValue(handle); - return caller(null, handle, destructorsRef, args); - }, - - $emval_lookupTypes__deps: ['$requireRegisteredType'], - $emval_lookupTypes: (argCount, argTypes) => { - var a = new Array(argCount); - for (var i = 0; i < argCount; ++i) { - a[i] = requireRegisteredType({{{ makeGetValue('argTypes', 'i * ' + POINTER_SIZE, '*') }}}, - "parameter " + i); - } - return a; - }, - - // Leave id 0 undefined. It's not a big deal, but might be confusing - // to have null be a valid method caller. - $emval_methodCallers: [undefined], - - $emval_addMethodCaller__deps: ['$emval_methodCallers'], - $emval_addMethodCaller: (caller) => { - var id = emval_methodCallers.length; - emval_methodCallers.push(caller); - return id; - }, - -#if MIN_CHROME_VERSION < 49 || MIN_FIREFOX_VERSION < 42 || MIN_SAFARI_VERSION < 100101 - $reflectConstruct: null, - $reflectConstruct__postset: ` - if (typeof Reflect != 'undefined') { - reflectConstruct = Reflect.construct; - } else { - reflectConstruct = function(target, args) { - // limited polyfill for Reflect.construct that handles variadic args and native objects, but not new.target - return new (target.bind.apply(target, [null].concat(args)))(); - }; - } - `, -#else - $reflectConstruct: 'Reflect.construct', -#endif - - _emval_get_method_caller__deps: [ - '$emval_addMethodCaller', '$emval_lookupTypes', - '$createNamedFunction', - '$reflectConstruct', '$emval_returnValue', -#if DYNAMIC_EXECUTION - '$newFunc', -#endif - ], - _emval_get_method_caller: (argCount, argTypes, kind) => { - var types = emval_lookupTypes(argCount, argTypes); - var retType = types.shift(); - argCount--; // remove the shifted off return type - -#if !DYNAMIC_EXECUTION - var argN = new Array(argCount); - var invokerFunction = (obj, func, destructorsRef, args) => { - var offset = 0; - for (var i = 0; i < argCount; ++i) { - argN[i] = types[i]['readValueFromPointer'](args + offset); - offset += types[i]['argPackAdvance']; - } - var rv = kind === /* CONSTRUCTOR */ 1 ? reflectConstruct(func, argN) : func.apply(obj, argN); - return emval_returnValue(retType, destructorsRef, rv); - }; -#else - var functionBody = - `return function (obj, func, destructorsRef, args) {\n`; - - var offset = 0; - var argsList = []; // 'obj?, arg0, arg1, arg2, ... , argN' - if (kind === /* FUNCTION */ 0) { - argsList.push("obj"); - } - var params = ["retType"]; - var args = [retType]; - for (var i = 0; i < argCount; ++i) { - argsList.push("arg" + i); - params.push("argType" + i); - args.push(types[i]); - functionBody += - ` var arg${i} = argType${i}.readValueFromPointer(args${offset ? "+" + offset : ""});\n`; - offset += types[i]['argPackAdvance']; - } - var invoker = kind === /* CONSTRUCTOR */ 1 ? 'new func' : 'func.call'; - functionBody += - ` var rv = ${invoker}(${argsList.join(", ")});\n`; - if (!retType.isVoid) { - params.push("emval_returnValue"); - args.push(emval_returnValue); - functionBody += - " return emval_returnValue(retType, destructorsRef, rv);\n"; - } - functionBody += - "};\n"; - - params.push(functionBody); - var invokerFunction = newFunc(Function, params)(...args); -#endif - var functionName = `methodCaller<(${types.map(t => t.name).join(', ')}) => ${retType.name}>`; - return emval_addMethodCaller(createNamedFunction(functionName, invokerFunction)); - }, - - _emval_call_method__deps: ['$getStringOrSymbol', '$emval_methodCallers', '$Emval'], - _emval_call_method: (caller, objHandle, methodName, destructorsRef, args) => { - caller = emval_methodCallers[caller]; - objHandle = Emval.toValue(objHandle); - methodName = getStringOrSymbol(methodName); - return caller(objHandle, objHandle[methodName], destructorsRef, args); - }, - - _emval_typeof__deps: ['$Emval'], - _emval_typeof: (handle) => { - handle = Emval.toValue(handle); - return Emval.toHandle(typeof handle); - }, - - _emval_instanceof__deps: ['$Emval'], - _emval_instanceof: (object, constructor) => { - object = Emval.toValue(object); - constructor = Emval.toValue(constructor); - return object instanceof constructor; - }, - - _emval_is_number__deps: ['$Emval'], - _emval_is_number: (handle) => { - handle = Emval.toValue(handle); - return typeof handle == 'number'; - }, - - _emval_is_string__deps: ['$Emval'], - _emval_is_string: (handle) => { - handle = Emval.toValue(handle); - return typeof handle == 'string'; - }, - - _emval_in__deps: ['$Emval'], - _emval_in: (item, object) => { - item = Emval.toValue(item); - object = Emval.toValue(object); - return item in object; - }, - - _emval_delete__deps: ['$Emval'], - _emval_delete: (object, property) => { - object = Emval.toValue(object); - property = Emval.toValue(property); - return delete object[property]; - }, - - _emval_throw__deps: ['$Emval'], - _emval_throw: (object) => { - object = Emval.toValue(object); - throw object; - }, - -#if ASYNCIFY - _emval_await__deps: ['$Emval', '$Asyncify'], - _emval_await__async: true, - _emval_await: (promise) => { - return Asyncify.handleAsync(() => { - promise = Emval.toValue(promise); - return promise.then(Emval.toHandle); - }); - }, -#endif - - _emval_iter_begin__deps: ['$Emval'], - _emval_iter_begin: (iterable) => { - iterable = Emval.toValue(iterable); - return Emval.toHandle(iterable[Symbol.iterator]()); - }, - - _emval_iter_next__deps: ['$Emval'], - _emval_iter_next: (iterator) => { - iterator = Emval.toValue(iterator); - var result = iterator.next(); - return result.done ? 0 : Emval.toHandle(result.value); - }, - - _emval_coro_suspend__deps: ['$Emval', '_emval_coro_resume'], - _emval_coro_suspend: (promiseHandle, awaiterPtr) => { - Emval.toValue(promiseHandle).then(result => { - __emval_coro_resume(awaiterPtr, Emval.toHandle(result)); - }); - }, - - _emval_coro_make_promise__deps: ['$Emval', '__cxa_rethrow'], - _emval_coro_make_promise: (resolveHandlePtr, rejectHandlePtr) => { - return Emval.toHandle(new Promise((resolve, reject) => { - const rejectWithCurrentException = () => { - try { - // Use __cxa_rethrow which already has mechanism for generating - // user-friendly error message and stacktrace from C++ exception - // if EXCEPTION_STACK_TRACES is enabled and numeric exception - // with metadata optimised out otherwise. - ___cxa_rethrow(); - } catch (e) { - // But catch it so that it rejects the promise instead of throwing - // in an unpredictable place during async execution. - reject(e); - } - }; - - {{{ makeSetValue('resolveHandlePtr', '0', 'Emval.toHandle(resolve)', '*') }}}; - {{{ makeSetValue('rejectHandlePtr', '0', 'Emval.toHandle(rejectWithCurrentException)', '*') }}}; - })); - }, -}; - -addToLibrary(LibraryEmVal); diff --git a/src/emrun_postjs.js b/src/emrun_postjs.js index 5aecc5e932a26..97e1ad116be9e 100644 --- a/src/emrun_postjs.js +++ b/src/emrun_postjs.js @@ -7,7 +7,7 @@ * emcc is run with `--emrun` */ -if (typeof window == "object" && (typeof ENVIRONMENT_IS_PTHREAD == 'undefined' || !ENVIRONMENT_IS_PTHREAD)) { +if (globalThis.window && (typeof ENVIRONMENT_IS_PTHREAD == 'undefined' || !ENVIRONMENT_IS_PTHREAD)) { var emrun_register_handlers = () => { // When C code exit()s, we may still have remaining stdout and stderr // messages in flight. In that case, we can't close the browser until all @@ -89,7 +89,7 @@ if (typeof window == "object" && (typeof ENVIRONMENT_IS_PTHREAD == 'undefined' | http.send(data); // XXX this does not work in workers, for some odd reason (issue #2681) }; - if (typeof document != 'undefined') { + if (globalThis.document) { emrun_register_handlers(); } } diff --git a/src/emrun_prejs.js b/src/emrun_prejs.js index 3ff49965ad7b4..1637ff420b499 100644 --- a/src/emrun_prejs.js +++ b/src/emrun_prejs.js @@ -8,8 +8,8 @@ */ // Route URL GET parameters to argc+argv -if (typeof window == 'object') { - Module['arguments'] = window.location.search.substr(1).trim().split('&'); +if (globalThis.window) { + Module['arguments'] = window.location.search.slice(1).trim().split('&'); for (let i = 0; i < Module['arguments'].length; ++i) { Module['arguments'][i] = decodeURI(Module['arguments'][i]); } diff --git a/src/emscripten-source-map.min.js b/src/emscripten-source-map.min.js deleted file mode 100644 index ba10d77f65903..0000000000000 --- a/src/emscripten-source-map.min.js +++ /dev/null @@ -1,32 +0,0 @@ -function define(e,t,n){if(typeof e!="string")throw new TypeError("Expected string, got: "+e);arguments.length==2&&(n=t);if(e in define.modules)throw new Error("Module already defined: "+e);define.modules[e]=n}function Domain(){this.modules={},this._currentModule=null}define.modules={},function(){function e(e){var t=e.split("/"),n=1;while(nt)-(e0&&t.column>=0&&!n&&!r&&!i)return;if(t&&"line"in t&&"column"in t&&n&&"line"in n&&"column"in n&&t.line>0&&t.column>=0&&n.line>0&&n.column>=0&&r)return;throw new Error("Invalid mapping.")},o.prototype._serializeMappings=function(){var t=0,n=1,i=0,s=0,o=0,u=0,a="",l;this._mappings.sort(f);for(var c=0,h=this._mappings.length;c0){if(!f(l,this._mappings[c-1]))continue;a+=","}a+=r.encode(l.generated.column-t),t=l.generated.column,l.source&&l.original&&(a+=r.encode(this._sources.indexOf(l.source)-u),u=this._sources.indexOf(l.source),a+=r.encode(l.original.line-1-s),s=l.original.line-1,a+=r.encode(l.original.column-i),i=l.original.column,l.name&&(a+=r.encode(this._names.indexOf(l.name)-o),o=this._names.indexOf(l.name)))}return a},o.prototype.toJSON=function(){var t={version:this._version,file:this._file,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};return this._sourceRoot&&(t.sourceRoot=this._sourceRoot),this._sourcesContents&&(t.sourcesContent=t.sources.map(function(e){return t.sourceRoot&&(e=i.relative(t.sourceRoot,e)),Object.prototype.hasOwnProperty.call(this._sourcesContents,i.toSetString(e))?this._sourcesContents[i.toSetString(e)]:null},this)),t},o.prototype.toString=function(){return JSON.stringify(this)},t.SourceMapGenerator=o}),define("source-map/base64-vlq",["require","exports","module","source-map/base64"],function(e,t,n){function a(e){return e<0?(-e<<1)+1:(e<<1)+0}function f(e){var t=(e&1)===1,n=e>>1;return t?-n:n}var r=e("./base64"),i=5,s=1<>>=i,f>0&&(s|=u),n+=r.encode(s);while(f>0);return n},t.decode=function(t){var n=0,s=t.length,a=0,l=0,c,h;do{if(n>=s)throw new Error("Expected more digits in base 64 VLQ value.");h=r.decode(t.charAt(n++)),c=!!(h&u),h&=o,a+=h<=0&&t0)if(c.charAt(0)===";")r++,c=c.slice(1),i=0;else if(c.charAt(0)===",")c=c.slice(1);else{h={},h.generatedLine=r,p=o.decode(c),h.generatedColumn=i+p.value,i=h.generatedColumn,c=p.rest;if(c.length>0&&!l.test(c.charAt(0))){p=o.decode(c),h.source=this._sources.at(a+p.value),a+=p.value,c=p.rest;if(c.length===0||l.test(c.charAt(0)))throw new Error("Found a source, but no line and column");p=o.decode(c),h.originalLine=s+p.value,s=h.originalLine,h.originalLine+=1,c=p.rest;if(c.length===0||l.test(c.charAt(0)))throw new Error("Found a source and line, but no column");p=o.decode(c),h.originalColumn=u+p.value,u=h.originalColumn,c=p.rest,c.length>0&&!l.test(c.charAt(0))&&(p=o.decode(c),h.name=this._names.at(f+p.value),f+=p.value,c=p.rest)}this._generatedMappings.push(h),typeof h.originalLine=="number"&&this._originalMappings.push(h)}this._originalMappings.sort(this._compareOriginalPositions)},u.prototype._compareOriginalPositions=function(t,n){if(t.source>n.source)return 1;if(t.source0?t-o>1?r(o,t,n,i,s):i[o]:o-e>1?r(e,o,n,i,s):e<0?null:i[e]}t.search=function(t,n,i){return n.length>0?r(-1,n.length,t,n,i):null}}),define("source-map/source-node",["require","exports","module","source-map/source-map-generator","source-map/util"],function(e,t,n){function s(e,t,n,r,i){this.children=[],this.sourceContents={},this.line=e===undefined?null:e,this.column=t===undefined?null:t,this.source=n===undefined?null:n,this.name=i===undefined?null:i,r!=null&&this.add(r)}var r=e("./source-map-generator").SourceMapGenerator,i=e("./util");s.fromStringWithSourceMap=function(t,n){function f(e,t){e===null||e.source===undefined?r.add(t):r.add(new s(e.originalLine,e.originalColumn,e.source,t,e.name))}var r=new s,i=t.split("\n"),o=1,u=0,a=null;return n.eachMapping(function(e){if(a===null){while(o=0;n--)this.prepend(t[n]);else{if(!(t instanceof s||typeof t=="string"))throw new TypeError("Expected a SourceNode, string, or an array of SourceNodes and strings. Got "+t);this.children.unshift(t)}return this},s.prototype.walk=function(t){this.children.forEach(function(e){e instanceof s?e.walk(t):e!==""&&t(e,{source:this.source,line:this.line,column:this.column,name:this.name})},this)},s.prototype.join=function(t){var n,r,i=this.children.length;if(i>0){n=[];for(r=0;r { /** * @fileoverview gl-matrix - High performance matrix and vector operations for WebGL diff --git a/src/growableHeap.js b/src/growableHeap.js deleted file mode 100644 index 6169ab5909ef6..0000000000000 --- a/src/growableHeap.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @license - * Copyright 2019 The Emscripten Authors - * SPDX-License-Identifier: MIT - */ - -// Support for growable heap + pthreads, where the buffer may change, so JS views -// must be updated. -function GROWABLE_HEAP_I8() { - if (wasmMemory.buffer != HEAP8.buffer) { - updateMemoryViews(); - } - return HEAP8; -} -function GROWABLE_HEAP_U8() { - if (wasmMemory.buffer != HEAP8.buffer) { - updateMemoryViews(); - } - return HEAPU8; -} -function GROWABLE_HEAP_I16() { - if (wasmMemory.buffer != HEAP8.buffer) { - updateMemoryViews(); - } - return HEAP16; -} -function GROWABLE_HEAP_U16() { - if (wasmMemory.buffer != HEAP8.buffer) { - updateMemoryViews(); - } - return HEAPU16; -} -function GROWABLE_HEAP_I32() { - if (wasmMemory.buffer != HEAP8.buffer) { - updateMemoryViews(); - } - return HEAP32; -} -function GROWABLE_HEAP_U32() { - if (wasmMemory.buffer != HEAP8.buffer) { - updateMemoryViews(); - } - return HEAPU32; -} -function GROWABLE_HEAP_F32() { - if (wasmMemory.buffer != HEAP8.buffer) { - updateMemoryViews(); - } - return HEAPF32; -} -function GROWABLE_HEAP_F64() { - if (wasmMemory.buffer != HEAP8.buffer) { - updateMemoryViews(); - } - return HEAPF64; -} diff --git a/src/headless.js b/src/headless.js deleted file mode 100644 index e1b0697684ab3..0000000000000 --- a/src/headless.js +++ /dev/null @@ -1,290 +0,0 @@ -/** - * @license - * Copyright 2012 The Emscripten Authors - * SPDX-License-Identifier: MIT - */ - -var headlessPrint = (x) => { - //print(x); -}; - -var window = { - // adjustable parameters - location: { - toString() { - return 'http://emscripten.org'; - }, - search: '', - pathname: null, - }, - onIdle() { - headlessPrint('triggering click'); - document.querySelector('.fullscreen-button.low-res').callEventListeners('click'); - window.onIdle = null; - }, - dirsToDrop: 0, // go back to root dir if first_js is in a subdir - - headless: true, - - stopped: false, - fakeNow: 0, // we don't use Date.now() - rafs: [], - timeouts: [], - uid: 0, - requestAnimationFrame(func) { - func.uid = window.uid++; - headlessPrint(`adding raf ${func.uid}`); - window.rafs.push(func); - }, - setTimeout(func, ms) { - func.uid = window.uid++; - headlessPrint(`adding timeout ${func.uid}`); - window.timeouts.push({ - func, - when: window.fakeNow + (ms || 0) - }); - window.timeouts.sort((x, y) => { return y.when - x.when }); - }, - runEventLoop() { - // run forever until an exception stops this replay - var iter = 0; - while (!this.stopped) { - var start = Date.realNow(); - headlessPrint(`event loop: ${(iter++)}`); - if (window.rafs.length == 0 && window.timeouts.length == 0) { - if (window.onIdle) { - window.onIdle(); - } else { - throw 'main loop is idle!'; - } - } - // rafs - var currRafs = window.rafs; - window.rafs = []; - for (var i = 0; i < currRafs.length; i++) { - var raf = currRafs[i]; - headlessPrint(`calling raf: ${raf.uid}`);// + ': ' + raf.toString().substring(0, 50)); - raf(); - } - // timeouts - var now = window.fakeNow; - var timeouts = window.timeouts; - window.timeouts = []; - while (timeouts.length && timeouts[timeouts.length-1].when <= now) { - var timeout = timeouts.pop(); - headlessPrint(`calling timeout: ${timeout.func.uid}`);// + ': ' + timeout.func.toString().substring(0, 50)); - timeout.func(); - } - // increment 'time' - window.fakeNow += 16.666; - headlessPrint(`main event loop iteration took ${Date.realNow() - start} ms`); - } - }, - eventListeners: {}, - addEventListener(id, func) { - var listeners = this.eventListeners[id]; - listeners ||= this.eventListeners[id] = []; - listeners.push(func); - }, - removeEventListener(id, func) { - var listeners = this.eventListeners[id]; - if (!listeners) return; - for (var i = 0; i < listeners.length; i++) { - if (listeners[i] === func) { - listeners.splice(i, 1); - return; - } - } - }, - callEventListeners(id) { - var listeners = this.eventListeners[id]; - listeners?.forEach((listener) => listener()); - }, - URL: { - createObjectURL(x) { - return x; // the blob itself is returned - }, - revokeObjectURL(x) {}, - }, - encodeURIComponent(x) { return x }, -}; -var setTimeout = window.setTimeout; -var document = { - headless: true, - eventListeners: {}, - addEventListener: window.addEventListener, - removeEventListener: window.removeEventListener, - callEventListeners: window.callEventListeners, - getElementById(id) { - switch (id) { - case 'canvas': { - if (this.canvas) return this.canvas; - return this.canvas = headlessCanvas(); - } - case 'status-text': case 'progress': { - return {}; - } - default: throw 'getElementById: ' + id; - } - }, - createElement(what) { - switch (what) { - case 'canvas': return document.getElementById(what); - case 'script': { - var ret = {}; - window.setTimeout(() => { - headlessPrint(`loading script: ${ret.src}`); - load(ret.src); - headlessPrint(' script loaded.'); - if (ret.onload) { - window.setTimeout(() => { - ret.onload(); // yeah yeah this might vanish - }); - } - }); - return ret; - } - case 'div': { - return { - appendChild() {}, - requestFullscreen() { - return document.getElementById('canvas').requestFullscreen(); - }, - }; - } - default: throw `createElement ${what}${new Error().stack}`; - } - }, - elements: {}, - querySelector(id) { - document.elements[id] ||= { - classList: { - add() {}, - remove() {}, - }, - eventListeners: {}, - addEventListener: document.addEventListener, - removeEventListener: document.removeEventListener, - callEventListeners: document.callEventListeners, - }; - return document.elements[id]; - }, - styleSheets: [{ - cssRules: [], - insertRule() {}, - }], - body: { - appendChild() {}, - }, - exitPointerLock() {}, - exitFullscreen() {}, -}; -var alert = function(x) { - print(x); -}; -var performance = { - now() { - return Date.now(); - }, -}; -function fixPath(path) { - if (path[0] == '/') path = path.substring(1); - for (var i = 0; i < window.dirsToDrop; i++) { - path = '../' + path; - } - return path -} -var XMLHttpRequest = function() { - return { - open(mode, path, async) { - path = fixPath(path); - this.mode = mode; - this.path = path; - this.async = async; - }, - send() { - if (!this.async) { - this.doSend(); - } else { - window.setTimeout(() => { - this.doSend(); - this.onload?.(); - }, 0); - } - }, - doSend() { - if (this.responseType == 'arraybuffer') { - this.response = read(this.path, 'binary'); - } else { - this.responseText = read(this.path); - } - }, - }; -}; -var Audio = () => ({ - play() {}, - pause() {}, - cloneNode() { - return this; - }, -}); -var Image = () => { - window.setTimeout(function() { - this.complete = true; - this.width = 64; - this.height = 64; - this.onload?.(); - }); -}; -var Worker = (workerPath) => { - workerPath = fixPath(workerPath); - var workerCode = read(workerPath); - workerCode = workerCode.replace(/Module/g, 'zzModuleyy' + (Worker.id++)). // prevent collision with the global Module object. Note that this becomes global, so we need unique ids - replace(/\nonmessage = /, '\nvar onmessage = '); // workers commonly do "onmessage = ", we need to verify that to sandbox - headlessPrint(`loading worker ${workerPath} : ${workerCode.substring(0, 50)}`); - eval(workerCode); // will implement onmessage() - - function duplicateJSON(json) { - function handleTypedArrays(key, value) { - if (value?.toString && value.toString().substring(0, 8) == '[object ' && value.length && value.byteLength) { - return Array.prototype.slice.call(value); - } - return value; - } - return JSON.parse(JSON.stringify(json, handleTypedArrays)) - } - this.terminate = () => {}; - this.postMessage = (msg) => { - msg.messageId = Worker.messageId++; - headlessPrint(`main thread sending message ${msg.messageId} to worker ${workerPath}`); - window.setTimeout(() => { - headlessPrint(`worker ${workerPath} receiving message ${msg.messageId}`); - onmessage({ data: duplicateJSON(msg) }); - }); - }; - var thisWorker = this; - var postMessage = (msg) => { - msg.messageId = Worker.messageId++; - headlessPrint(`worker ${workerPath} sending message ${msg.messageId}`); - window.setTimeout(() => { - headlessPrint(`main thread receiving message ${msg.messageId} from ${workerPath}`); - thisWorker.onmessage({ data: duplicateJSON(msg) }); - }); - }; -}; -Worker.id = 0; -Worker.messageId = 0; -var screen = { // XXX these values may need to be adjusted - width: 2100, - height: 1313, - availWidth: 2100, - availHeight: 1283, -}; -if (typeof console == 'undefined') { - console = { - log(x) { print(x); }, - }; -} - -// additional setup -Module['canvas'] ||= document.getElementById('canvas'); diff --git a/src/headlessCanvas.js b/src/headlessCanvas.js deleted file mode 100644 index a14f34e6a4f71..0000000000000 --- a/src/headlessCanvas.js +++ /dev/null @@ -1,633 +0,0 @@ -/** - * @license - * Copyright 2013 The Emscripten Authors - * SPDX-License-Identifier: MIT - */ - -function headlessCanvas() { - var ret = { - headless: true, - getContext: function(which) { - switch (which) { - case 'webgl': - case 'experimental-webgl': { - return { - /* ClearBufferMask */ - DEPTH_BUFFER_BIT : 0x00000100, - STENCIL_BUFFER_BIT : 0x00000400, - COLOR_BUFFER_BIT : 0x00004000, - - /* BeginMode */ - POINTS : 0x0000, - LINES : 0x0001, - LINE_LOOP : 0x0002, - LINE_STRIP : 0x0003, - TRIANGLES : 0x0004, - TRIANGLE_STRIP : 0x0005, - TRIANGLE_FAN : 0x0006, - - /* AlphaFunction (not supported in ES20) */ - /* NEVER */ - /* LESS */ - /* EQUAL */ - /* LEQUAL */ - /* GREATER */ - /* NOTEQUAL */ - /* GEQUAL */ - /* ALWAYS */ - - /* BlendingFactorDest */ - ZERO : 0, - ONE : 1, - SRC_COLOR : 0x0300, - ONE_MINUS_SRC_COLOR : 0x0301, - SRC_ALPHA : 0x0302, - ONE_MINUS_SRC_ALPHA : 0x0303, - DST_ALPHA : 0x0304, - ONE_MINUS_DST_ALPHA : 0x0305, - - /* BlendingFactorSrc */ - /* ZERO */ - /* ONE */ - DST_COLOR : 0x0306, - ONE_MINUS_DST_COLOR : 0x0307, - SRC_ALPHA_SATURATE : 0x0308, - /* SRC_ALPHA */ - /* ONE_MINUS_SRC_ALPHA */ - /* DST_ALPHA */ - /* ONE_MINUS_DST_ALPHA */ - - /* BlendEquationSeparate */ - FUNC_ADD : 0x8006, - BLEND_EQUATION : 0x8009, - BLEND_EQUATION_RGB : 0x8009, /* same as BLEND_EQUATION */ - BLEND_EQUATION_ALPHA : 0x883D, - - /* BlendSubtract */ - FUNC_SUBTRACT : 0x800A, - FUNC_REVERSE_SUBTRACT : 0x800B, - - /* Separate Blend Functions */ - BLEND_DST_RGB : 0x80C8, - BLEND_SRC_RGB : 0x80C9, - BLEND_DST_ALPHA : 0x80CA, - BLEND_SRC_ALPHA : 0x80CB, - CONSTANT_COLOR : 0x8001, - ONE_MINUS_CONSTANT_COLOR : 0x8002, - CONSTANT_ALPHA : 0x8003, - ONE_MINUS_CONSTANT_ALPHA : 0x8004, - BLEND_COLOR : 0x8005, - - /* Buffer Objects */ - ARRAY_BUFFER : 0x8892, - ELEMENT_ARRAY_BUFFER : 0x8893, - ARRAY_BUFFER_BINDING : 0x8894, - ELEMENT_ARRAY_BUFFER_BINDING : 0x8895, - - STREAM_DRAW : 0x88E0, - STATIC_DRAW : 0x88E4, - DYNAMIC_DRAW : 0x88E8, - - BUFFER_SIZE : 0x8764, - BUFFER_USAGE : 0x8765, - - CURRENT_VERTEX_ATTRIB : 0x8626, - - /* CullFaceMode */ - FRONT : 0x0404, - BACK : 0x0405, - FRONT_AND_BACK : 0x0408, - - /* DepthFunction */ - /* NEVER */ - /* LESS */ - /* EQUAL */ - /* LEQUAL */ - /* GREATER */ - /* NOTEQUAL */ - /* GEQUAL */ - /* ALWAYS */ - - /* EnableCap */ - /* TEXTURE_2D */ - CULL_FACE : 0x0B44, - BLEND : 0x0BE2, - DITHER : 0x0BD0, - STENCIL_TEST : 0x0B90, - DEPTH_TEST : 0x0B71, - SCISSOR_TEST : 0x0C11, - POLYGON_OFFSET_FILL : 0x8037, - SAMPLE_ALPHA_TO_COVERAGE : 0x809E, - SAMPLE_COVERAGE : 0x80A0, - - /* ErrorCode */ - NO_ERROR : 0, - INVALID_ENUM : 0x0500, - INVALID_VALUE : 0x0501, - INVALID_OPERATION : 0x0502, - OUT_OF_MEMORY : 0x0505, - - /* FrontFaceDirection */ - CW : 0x0900, - CCW : 0x0901, - - /* GetPName */ - LINE_WIDTH : 0x0B21, - ALIASED_POINT_SIZE_RANGE : 0x846D, - ALIASED_LINE_WIDTH_RANGE : 0x846E, - CULL_FACE_MODE : 0x0B45, - FRONT_FACE : 0x0B46, - DEPTH_RANGE : 0x0B70, - DEPTH_WRITEMASK : 0x0B72, - DEPTH_CLEAR_VALUE : 0x0B73, - DEPTH_FUNC : 0x0B74, - STENCIL_CLEAR_VALUE : 0x0B91, - STENCIL_FUNC : 0x0B92, - STENCIL_FAIL : 0x0B94, - STENCIL_PASS_DEPTH_FAIL : 0x0B95, - STENCIL_PASS_DEPTH_PASS : 0x0B96, - STENCIL_REF : 0x0B97, - STENCIL_VALUE_MASK : 0x0B93, - STENCIL_WRITEMASK : 0x0B98, - STENCIL_BACK_FUNC : 0x8800, - STENCIL_BACK_FAIL : 0x8801, - STENCIL_BACK_PASS_DEPTH_FAIL : 0x8802, - STENCIL_BACK_PASS_DEPTH_PASS : 0x8803, - STENCIL_BACK_REF : 0x8CA3, - STENCIL_BACK_VALUE_MASK : 0x8CA4, - STENCIL_BACK_WRITEMASK : 0x8CA5, - VIEWPORT : 0x0BA2, - SCISSOR_BOX : 0x0C10, - /* SCISSOR_TEST */ - COLOR_CLEAR_VALUE : 0x0C22, - COLOR_WRITEMASK : 0x0C23, - UNPACK_ALIGNMENT : 0x0CF5, - PACK_ALIGNMENT : 0x0D05, - MAX_TEXTURE_SIZE : 0x0D33, - MAX_VIEWPORT_DIMS : 0x0D3A, - SUBPIXEL_BITS : 0x0D50, - RED_BITS : 0x0D52, - GREEN_BITS : 0x0D53, - BLUE_BITS : 0x0D54, - ALPHA_BITS : 0x0D55, - DEPTH_BITS : 0x0D56, - STENCIL_BITS : 0x0D57, - POLYGON_OFFSET_UNITS : 0x2A00, - /* POLYGON_OFFSET_FILL */ - POLYGON_OFFSET_FACTOR : 0x8038, - TEXTURE_BINDING_2D : 0x8069, - SAMPLE_BUFFERS : 0x80A8, - SAMPLES : 0x80A9, - SAMPLE_COVERAGE_VALUE : 0x80AA, - SAMPLE_COVERAGE_INVERT : 0x80AB, - - /* GetTextureParameter */ - /* TEXTURE_MAG_FILTER */ - /* TEXTURE_MIN_FILTER */ - /* TEXTURE_WRAP_S */ - /* TEXTURE_WRAP_T */ - - COMPRESSED_TEXTURE_FORMATS : 0x86A3, - - /* HintMode */ - DONT_CARE : 0x1100, - FASTEST : 0x1101, - NICEST : 0x1102, - - /* HintTarget */ - GENERATE_MIPMAP_HINT : 0x8192, - - /* DataType */ - BYTE : 0x1400, - UNSIGNED_BYTE : 0x1401, - SHORT : 0x1402, - UNSIGNED_SHORT : 0x1403, - INT : 0x1404, - UNSIGNED_INT : 0x1405, - FLOAT : 0x1406, - - /* PixelFormat */ - DEPTH_COMPONENT : 0x1902, - ALPHA : 0x1906, - RGB : 0x1907, - RGBA : 0x1908, - LUMINANCE : 0x1909, - LUMINANCE_ALPHA : 0x190A, - - /* PixelType */ - /* UNSIGNED_BYTE */ - UNSIGNED_SHORT_4_4_4_4 : 0x8033, - UNSIGNED_SHORT_5_5_5_1 : 0x8034, - UNSIGNED_SHORT_5_6_5 : 0x8363, - - /* Shaders */ - FRAGMENT_SHADER : 0x8B30, - VERTEX_SHADER : 0x8B31, - MAX_VERTEX_ATTRIBS : 0x8869, - MAX_VERTEX_UNIFORM_VECTORS : 0x8DFB, - MAX_VARYING_VECTORS : 0x8DFC, - MAX_COMBINED_TEXTURE_IMAGE_UNITS : 0x8B4D, - MAX_VERTEX_TEXTURE_IMAGE_UNITS : 0x8B4C, - MAX_TEXTURE_IMAGE_UNITS : 0x8872, - MAX_FRAGMENT_UNIFORM_VECTORS : 0x8DFD, - SHADER_TYPE : 0x8B4F, - DELETE_STATUS : 0x8B80, - LINK_STATUS : 0x8B82, - VALIDATE_STATUS : 0x8B83, - ATTACHED_SHADERS : 0x8B85, - ACTIVE_UNIFORMS : 0x8B86, - ACTIVE_ATTRIBUTES : 0x8B89, - SHADING_LANGUAGE_VERSION : 0x8B8C, - CURRENT_PROGRAM : 0x8B8D, - - /* StencilFunction */ - NEVER : 0x0200, - LESS : 0x0201, - EQUAL : 0x0202, - LEQUAL : 0x0203, - GREATER : 0x0204, - NOTEQUAL : 0x0205, - GEQUAL : 0x0206, - ALWAYS : 0x0207, - - /* StencilOp */ - /* ZERO */ - KEEP : 0x1E00, - REPLACE : 0x1E01, - INCR : 0x1E02, - DECR : 0x1E03, - INVERT : 0x150A, - INCR_WRAP : 0x8507, - DECR_WRAP : 0x8508, - - /* StringName */ - VENDOR : 0x1F00, - RENDERER : 0x1F01, - VERSION : 0x1F02, - - /* TextureMagFilter */ - NEAREST : 0x2600, - LINEAR : 0x2601, - - /* TextureMinFilter */ - /* NEAREST */ - /* LINEAR */ - NEAREST_MIPMAP_NEAREST : 0x2700, - LINEAR_MIPMAP_NEAREST : 0x2701, - NEAREST_MIPMAP_LINEAR : 0x2702, - LINEAR_MIPMAP_LINEAR : 0x2703, - - /* TextureParameterName */ - TEXTURE_MAG_FILTER : 0x2800, - TEXTURE_MIN_FILTER : 0x2801, - TEXTURE_WRAP_S : 0x2802, - TEXTURE_WRAP_T : 0x2803, - - /* TextureTarget */ - TEXTURE_2D : 0x0DE1, - TEXTURE : 0x1702, - - TEXTURE_CUBE_MAP : 0x8513, - TEXTURE_BINDING_CUBE_MAP : 0x8514, - TEXTURE_CUBE_MAP_POSITIVE_X : 0x8515, - TEXTURE_CUBE_MAP_NEGATIVE_X : 0x8516, - TEXTURE_CUBE_MAP_POSITIVE_Y : 0x8517, - TEXTURE_CUBE_MAP_NEGATIVE_Y : 0x8518, - TEXTURE_CUBE_MAP_POSITIVE_Z : 0x8519, - TEXTURE_CUBE_MAP_NEGATIVE_Z : 0x851A, - MAX_CUBE_MAP_TEXTURE_SIZE : 0x851C, - - /* TextureUnit */ - TEXTURE0 : 0x84C0, - TEXTURE1 : 0x84C1, - TEXTURE2 : 0x84C2, - TEXTURE3 : 0x84C3, - TEXTURE4 : 0x84C4, - TEXTURE5 : 0x84C5, - TEXTURE6 : 0x84C6, - TEXTURE7 : 0x84C7, - TEXTURE8 : 0x84C8, - TEXTURE9 : 0x84C9, - TEXTURE10 : 0x84CA, - TEXTURE11 : 0x84CB, - TEXTURE12 : 0x84CC, - TEXTURE13 : 0x84CD, - TEXTURE14 : 0x84CE, - TEXTURE15 : 0x84CF, - TEXTURE16 : 0x84D0, - TEXTURE17 : 0x84D1, - TEXTURE18 : 0x84D2, - TEXTURE19 : 0x84D3, - TEXTURE20 : 0x84D4, - TEXTURE21 : 0x84D5, - TEXTURE22 : 0x84D6, - TEXTURE23 : 0x84D7, - TEXTURE24 : 0x84D8, - TEXTURE25 : 0x84D9, - TEXTURE26 : 0x84DA, - TEXTURE27 : 0x84DB, - TEXTURE28 : 0x84DC, - TEXTURE29 : 0x84DD, - TEXTURE30 : 0x84DE, - TEXTURE31 : 0x84DF, - ACTIVE_TEXTURE : 0x84E0, - - /* TextureWrapMode */ - REPEAT : 0x2901, - CLAMP_TO_EDGE : 0x812F, - MIRRORED_REPEAT : 0x8370, - - /* Uniform Types */ - FLOAT_VEC2 : 0x8B50, - FLOAT_VEC3 : 0x8B51, - FLOAT_VEC4 : 0x8B52, - INT_VEC2 : 0x8B53, - INT_VEC3 : 0x8B54, - INT_VEC4 : 0x8B55, - BOOL : 0x8B56, - BOOL_VEC2 : 0x8B57, - BOOL_VEC3 : 0x8B58, - BOOL_VEC4 : 0x8B59, - FLOAT_MAT2 : 0x8B5A, - FLOAT_MAT3 : 0x8B5B, - FLOAT_MAT4 : 0x8B5C, - SAMPLER_2D : 0x8B5E, - SAMPLER_CUBE : 0x8B60, - - /* Vertex Arrays */ - VERTEX_ATTRIB_ARRAY_ENABLED : 0x8622, - VERTEX_ATTRIB_ARRAY_SIZE : 0x8623, - VERTEX_ATTRIB_ARRAY_STRIDE : 0x8624, - VERTEX_ATTRIB_ARRAY_TYPE : 0x8625, - VERTEX_ATTRIB_ARRAY_NORMALIZED : 0x886A, - VERTEX_ATTRIB_ARRAY_POINTER : 0x8645, - VERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 0x889F, - - /* Shader Source */ - COMPILE_STATUS : 0x8B81, - - /* Shader Precision-Specified Types */ - LOW_FLOAT : 0x8DF0, - MEDIUM_FLOAT : 0x8DF1, - HIGH_FLOAT : 0x8DF2, - LOW_INT : 0x8DF3, - MEDIUM_INT : 0x8DF4, - HIGH_INT : 0x8DF5, - - /* Framebuffer Object. */ - FRAMEBUFFER : 0x8D40, - RENDERBUFFER : 0x8D41, - - RGBA4 : 0x8056, - RGB5_A1 : 0x8057, - RGB565 : 0x8D62, - DEPTH_COMPONENT16 : 0x81A5, - STENCIL_INDEX : 0x1901, - STENCIL_INDEX8 : 0x8D48, - DEPTH_STENCIL : 0x84F9, - - RENDERBUFFER_WIDTH : 0x8D42, - RENDERBUFFER_HEIGHT : 0x8D43, - RENDERBUFFER_INTERNAL_FORMAT : 0x8D44, - RENDERBUFFER_RED_SIZE : 0x8D50, - RENDERBUFFER_GREEN_SIZE : 0x8D51, - RENDERBUFFER_BLUE_SIZE : 0x8D52, - RENDERBUFFER_ALPHA_SIZE : 0x8D53, - RENDERBUFFER_DEPTH_SIZE : 0x8D54, - RENDERBUFFER_STENCIL_SIZE : 0x8D55, - - FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE : 0x8CD0, - FRAMEBUFFER_ATTACHMENT_OBJECT_NAME : 0x8CD1, - FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL : 0x8CD2, - FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 0x8CD3, - - COLOR_ATTACHMENT0 : 0x8CE0, - DEPTH_ATTACHMENT : 0x8D00, - STENCIL_ATTACHMENT : 0x8D20, - DEPTH_STENCIL_ATTACHMENT : 0x821A, - - NONE : 0, - - FRAMEBUFFER_COMPLETE : 0x8CD5, - FRAMEBUFFER_INCOMPLETE_ATTACHMENT : 0x8CD6, - FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : 0x8CD7, - FRAMEBUFFER_INCOMPLETE_DIMENSIONS : 0x8CD9, - FRAMEBUFFER_UNSUPPORTED : 0x8CDD, - - FRAMEBUFFER_BINDING : 0x8CA6, - RENDERBUFFER_BINDING : 0x8CA7, - MAX_RENDERBUFFER_SIZE : 0x84E8, - - INVALID_FRAMEBUFFER_OPERATION : 0x0506, - - /* WebGL-specific enums */ - UNPACK_FLIP_Y_WEBGL : 0x9240, - UNPACK_PREMULTIPLY_ALPHA_WEBGL : 0x9241, - CONTEXT_LOST_WEBGL : 0x9242, - UNPACK_COLORSPACE_CONVERSION_WEBGL : 0x9243, - BROWSER_DEFAULT_WEBGL : 0x9244, - - items: {}, - id: 0, - getExtension: function() { return 1 }, - createBuffer: function() { - var id = this.id++; - this.items[id] = { - which: 'buffer', - }; - return id; - }, - deleteBuffer: function(){}, - bindBuffer: function(){}, - bufferData: function(){}, - getParameter: function(pname) { - switch (pname) { - case /* GL_VENDOR */ 0x1F00: return 'FakeShellGLVendor'; - case /* GL_RENDERER */ 0x1F01: return 'FakeShellGLRenderer'; - case /* GL_VERSION */ 0x1F02: return '0.0.1'; - case /* GL_MAX_TEXTURE_SIZE */ 0x0D33: return 16384; - case /* GL_MAX_CUBE_MAP_TEXTURE_SIZE */ 0x851C: return 16384; - case /* GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT */ 0x84FF: return 16; - case /* GL_MAX_TEXTURE_IMAGE_UNITS_NV */ 0x8872: return 16; - case /* GL_MAX_VERTEX_UNIFORM_VECTORS */ 0x8DFB: return 4096; - case /* GL_MAX_FRAGMENT_UNIFORM_VECTORS */ 0x8DFD: return 4096; - case /* GL_MAX_VARYING_VECTORS */ 0x8DFC: return 32; - case /* GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS */ 0x8B4D: return 32; - case /* GL_ARRAY_BUFFER_BINDING */ 0x8894: return 0; - default: console.log('getParameter ' + pname + '?'); return 0; - } - }, - getSupportedExtensions: function() { - return ["OES_texture_float", "OES_standard_derivatives", "EXT_texture_filter_anisotropic", "MOZ_EXT_texture_filter_anisotropic", "MOZ_WEBGL_lose_context", "MOZ_WEBGL_compressed_texture_s3tc", "MOZ_WEBGL_depth_texture"]; - }, - createShader: function(type) { - var id = this.id++; - this.items[id] = { - which: 'shader', - type, - }; - return id; - }, - getShaderParameter: function(shader, pname) { - switch (pname) { - case /* GL_SHADER_TYPE */ 0x8B4F: return this.items[shader].type; - case /* GL_COMPILE_STATUS */ 0x8B81: return true; - default: throw 'getShaderParameter ' + pname; - } - }, - shaderSource: function(){}, - compileShader: function(){}, - createProgram: function() { - var id = this.id++; - this.items[id] = { - which: 'program', - shaders: [], - }; - return id; - }, - attachShader: function(program, shader) { - this.items[program].shaders.push(shader); - }, - bindAttribLocation: function(){}, - linkProgram: function(){}, - getProgramParameter: function(program, pname) { - switch (pname) { - case /* LINK_STATUS */ 0x8B82: return true; - case /* ACTIVE_UNIFORMS */ 0x8B86: return 4; - default: throw 'getProgramParameter ' + pname; - } - }, - deleteShader: function(){}, - deleteProgram: function(){}, - viewport: function(){}, - clearColor: function(){}, - clearDepth: function(){}, - depthFunc: function(){}, - enable: function(){}, - disable: function(){}, - frontFace: function(){}, - cullFace: function(){}, - activeTexture: function(){}, - createTexture: function() { - var id = this.id++; - this.items[id] = { - which: 'texture', - }; - return id; - }, - deleteTexture: function(){}, - boundTextures: {}, - bindTexture: function(target, texture) { - this.boundTextures[target] = texture; - }, - texParameteri: function(){}, - pixelStorei: function(){}, - texImage2D: function(){}, - compressedTexImage2D: function(){}, - useProgram: function(){}, - getUniformLocation: function() { - return null; - }, - getActiveUniform: function(program, index) { - return { - size: 1, - type: /* INT_VEC3 */ 0x8B54, - name: 'activeUniform' + index, - }; - }, - clear: function(){}, - uniform4fv: function(){}, - uniform1i: function(){}, - getAttribLocation: function() { return 1 }, - vertexAttribPointer: function(){}, - enableVertexAttribArray: function(){}, - disableVertexAttribArray: function(){}, - drawElements: function(){}, - drawArrays: function(){}, - depthMask: function(){}, - depthRange: function(){}, - bufferSubData: function(){}, - blendFunc: function(){}, - createFramebuffer: function() { - var id = this.id++; - this.items[id] = { - which: 'framebuffer', - shaders: [], - }; - return id; - }, - bindFramebuffer: function(){}, - framebufferTexture2D: function(){}, - checkFramebufferStatus: function() { - return /* FRAMEBUFFER_COMPLETE */ 0x8CD5; - }, - createRenderbuffer: function() { - var id = this.id++; - this.items[id] = { - which: 'renderbuffer', - shaders: [], - }; - return id; - }, - bindRenderbuffer: function(){}, - renderbufferStorage: function(){}, - framebufferRenderbuffer: function(){}, - scissor: function(){}, - colorMask: function(){}, - lineWidth: function(){}, - vertexAttrib4fv: function(){}, - }; - } - case '2d': { - return { - drawImage: function(){}, - getImageData: function(x, y, w, h) { - return { - width: w, - height: h, - data: new Uint8ClampedArray(w*h), - }; - }, - save: function(){}, - restore: function(){}, - fillRect: function(){}, - measureText: function() { return 10 }, - fillText: function(){}, - }; - } - default: throw 'canvas.getContext: ' + which; - } - }, - requestPointerLock: function() { - document.pointerLockElement = document.getElementById('canvas'); - window.setTimeout(function() { - document.callEventListeners('pointerlockchange'); - }); - }, - exitPointerLock: function(){}, - style: { - setProperty: function() {}, - removeProperty: function() {}, - }, - eventListeners: {}, - addEventListener: function(){}, - removeEventListener: function(){}, - requestFullscreen: function() { - document.fullscreenElement = document.getElementById('canvas'); - window.setTimeout(function() { - document.callEventListeners('fullscreenchange'); - }); - }, - offsetTop: 0, - offsetLeft: 0, - // generics - classList: { - add: function(){}, - remove: function(){}, - }, - insertBefore: function(){}, - }; - ret.parentNode = ret; - return ret; -} - diff --git a/src/jsifier.mjs b/src/jsifier.mjs index 15270ce00511b..627c740fe5c1c 100644 --- a/src/jsifier.mjs +++ b/src/jsifier.mjs @@ -7,10 +7,16 @@ // Convert analyzed data to javascript. Everything has already been calculated // before this stage, which just does the final conversion to JavaScript. +import assert from 'node:assert'; +import * as fs from 'node:fs/promises'; import { + ATMODULES, ATEXITS, ATINITS, + ATPOSTCTORS, + ATPRERUNS, ATMAINS, + ATPOSTRUNS, defineI64Param, indentify, makeReturn64, @@ -21,25 +27,29 @@ import { } from './parseTools.mjs'; import { addToCompileTimeContext, - assert, + debugLog, error, errorOccured, isDecorator, isJsOnlySymbol, compileTimeContext, - print, - printErr, - read, + readFile, + runInMacroContext, warn, warnOnce, warningOccured, + localFile, } from './utility.mjs'; -import {LibraryManager, librarySymbols} from './modules.mjs'; +import {LibraryManager, librarySymbols, nativeAliases} from './modules.mjs'; const addedLibraryItems = {}; const extraLibraryFuncs = []; +// Experimental feature to check for invalid __deps entries. +// See `EMCC_CHECK_DEPS` in in the environment to try it out. +const CHECK_DEPS = process.env.EMCC_CHECK_DEPS; + // Some JS-implemented library functions are proxied to be called on the main // browser thread, if the Emscripten runtime is executing in a Web Worker. // Each such proxied function is identified via an ordinal number (this is not @@ -51,7 +61,7 @@ function mangleCSymbolName(f) { if (f === '__main_argc_argv') { f = 'main'; } - return f[0] == '$' ? f.substr(1) : '_' + f; + return f[0] == '$' ? f.slice(1) : '_' + f; } // Splits out items that pass filter. Returns also the original sans the filtered @@ -75,12 +85,25 @@ function stringifyWithFunctions(obj) { if (Array.isArray(obj)) { return '[' + obj.map(stringifyWithFunctions).join(',') + ']'; } + + // preserve the type of the object if it is one of [Map, Set, WeakMap, WeakSet]. + const builtinContainers = runInMacroContext('[Map, Set, WeakMap, WeakSet]', { + filename: '', + }); + for (const container of builtinContainers) { + if (obj instanceof container) { + const className = container.name; + assert(!obj.size, `cannot stringify ${className} with data`); + return `new ${className}`; + } + } + var rtn = '{\n'; for (const [key, value] of Object.entries(obj)) { var str = stringifyWithFunctions(value); // Handle JS method syntax where the function property starts with its own - // name. e.g. foo(a) {}, - if (typeof value === 'function' && str.startsWith(key)) { + // name. e.g. `foo(a) {}` (or `async foo(a) {}`) + if (typeof value === 'function' && (str.startsWith(key) || str.startsWith('async ' + key))) { rtn += str + ',\n'; } else { rtn += escapeJSONKey(key) + ':' + str + ',\n'; @@ -105,14 +128,18 @@ function isDefined(symName) { } function resolveAlias(symbol) { - var value = LibraryManager.library[symbol]; - if (typeof value == 'string' && value[0] != '=' && LibraryManager.library.hasOwnProperty(value)) { - return value; + while (true) { + var value = LibraryManager.library[symbol]; + if (typeof value == 'string' && value[0] != '=' && (LibraryManager.library.hasOwnProperty(value) || WASM_EXPORTS.has(value))) { + symbol = value; + } else { + break; + } } return symbol; } -function getTransitiveDeps(symbol, debug) { +function getTransitiveDeps(symbol) { // TODO(sbc): Use some kind of cache to avoid quadratic behaviour here. const transitiveDeps = new Set(); const seen = new Set(); @@ -124,8 +151,8 @@ function getTransitiveDeps(symbol, debug) { directDeps = directDeps.filter((d) => typeof d === 'string'); for (const dep of directDeps) { const resolved = resolveAlias(dep); - if (VERBOSE && !transitiveDeps.has(dep)) { - printErr(`adding dependency ${symbol} -> ${dep}`); + if (!transitiveDeps.has(dep)) { + debugLog(`adding dependency ${symbol} -> ${dep}`); } transitiveDeps.add(resolved); toVisit.push(resolved); @@ -137,131 +164,228 @@ function getTransitiveDeps(symbol, debug) { } function shouldPreprocess(fileName) { - var content = read(fileName).trim(); + var content = readFile(fileName).trim(); return content.startsWith('#preprocess\n') || content.startsWith('#preprocess\r\n'); } -function getIncludeFile(fileName, needsPreprocess) { - let result = `// include: ${fileName}\n`; - if (needsPreprocess) { +function getIncludeFile(fileName, alwaysPreprocess, shortName) { + shortName ??= fileName; + let result = `// include: ${shortName}\n`; + const doPreprocess = alwaysPreprocess || shouldPreprocess(fileName); + if (doPreprocess) { result += processMacros(preprocess(fileName), fileName); } else { - result += read(fileName); + result += readFile(fileName); } - result += `// end include: ${fileName}\n`; + result += `// end include: ${shortName}\n`; return result; } +function getSystemIncludeFile(fileName) { + return getIncludeFile(localFile(fileName), /*alwaysPreprocess=*/ true, /*shortName=*/ fileName); +} + function preJS() { let result = ''; for (const fileName of PRE_JS_FILES) { - result += getIncludeFile(fileName, shouldPreprocess(fileName)); + result += getIncludeFile(fileName); } return result; } -export function runJSify(symbolsOnly) { - const libraryItems = []; - const symbolDeps = {}; - const asyncFuncs = []; - let postSets = []; +// Certain library functions have specific indirect dependencies. See the +// comments alongside eaach of these. +const checkDependenciesSkip = new Set([ + '_mmap_js', + '_emscripten_throw_longjmp', + '_emscripten_receive_on_main_thread_js', + 'emscripten_start_fetch', + 'emscripten_start_wasm_audio_worklet_thread_async', +]); + +const checkDependenciesIgnore = new Set([ + // These are added in bulk to whole library files are so are not precise + '$PThread', + '$WebGPU', + '$SDL', + '$GLUT', + '$GLEW', + '$Browser', + '$AL', + '$GL', + '$IDBStore', + // These are added purely for their side effects + '$polyfillWaitAsync', + '$GLImmediateSetup', + '$emscriptenGetAudioObject', + // These get conservatively injected via i53ConversionDeps + '$bigintToI53Checked', + '$convertI32PairToI53Checked', + 'setTempRet0', +]); - LibraryManager.load(); - - const symbolsNeeded = DEFAULT_LIBRARY_FUNCS_TO_INCLUDE; - symbolsNeeded.push(...extraLibraryFuncs); - for (const sym of EXPORTED_RUNTIME_METHODS) { - if ('$' + sym in LibraryManager.library) { - symbolsNeeded.push('$' + sym); +/** + * Hacky attempt to find unused `__deps` entries. This is not enabled by default + * but can be enabled by setting CHECK_DEPS above. + * TODO: Use a more precise method such as tokenising using acorn. + */ +function checkDependencies(symbol, snippet, deps, postset) { + if (checkDependenciesSkip.has(symbol)) { + return; + } + for (const dep of deps) { + if (typeof dep === 'function') { + continue; + } + if (checkDependenciesIgnore.has(dep)) { + continue; + } + const mangled = mangleCSymbolName(dep); + if (!snippet.includes(mangled) && (!postset || !postset.includes(mangled))) { + error(`${symbol}: unused dependency: ${dep}`); } } +} - for (const key of Object.keys(LibraryManager.library)) { - if (!isDecorator(key)) { - if (INCLUDE_FULL_LIBRARY || EXPORTED_FUNCTIONS.has(mangleCSymbolName(key))) { - symbolsNeeded.push(key); - } +function addImplicitDeps(snippet, deps) { + // There are some common dependencies that we inject automatically by + // conservatively scanning the input functions for their usage. + // Specifically, these are dependencies that are very common and would be + // burdensome to add manually to all functions. + // The first four are deps that are automatically/conditionally added + // by the {{{ makeDynCall }}}, and {{{ runtimeKeepalivePush/Pop }}} macros. + const autoDeps = [ + 'getDynCaller', + 'getWasmTableEntry', + 'runtimeKeepalivePush', + 'runtimeKeepalivePop', + 'UTF8ToString', + ]; + for (const dep of autoDeps) { + if (snippet.includes(dep + '(')) { + deps.push('$' + dep); } } +} - function handleI64Signatures(symbol, snippet, sig, i53abi) { - // Handle i64 parameters and return values. - // - // When WASM_BIGINT is enabled these arrive as BigInt values which we - // convert to int53 JS numbers. If necessary, we also convert the return - // value back into a BigInt. - // - // When WASM_BIGINT is not enabled we receive i64 values as a pair of i32 - // numbers which is converted to single int53 number. In necessary, we also - // split the return value into a pair of i32 numbers. - return modifyJSFunction(snippet, (args, body, async_, oneliner) => { - let argLines = args.split('\n'); - argLines = argLines.map((line) => line.split('//')[0]); - const argNames = argLines - .join(' ') - .split(',') - .map((name) => name.trim()); - const newArgs = []; - let argConversions = ''; - if (sig.length > argNames.length + 1) { - error(`handleI64Signatures: signature too long for ${symbol}`); - return snippet; - } - for (let i = 0; i < argNames.length; i++) { - const name = argNames[i]; - // If sig is shorter than argNames list then argType will be undefined - // here, which will result in the default case below. - const argType = sig[i + 1]; - if (WASM_BIGINT && ((MEMORY64 && argType == 'p') || (i53abi && argType == 'j'))) { +function handleI64Signatures(symbol, snippet, sig, i53abi) { + // Handle i64 parameters and return values. + // + // When WASM_BIGINT is enabled these arrive as BigInt values which we + // convert to int53 JS numbers. If necessary, we also convert the return + // value back into a BigInt. + // + // When WASM_BIGINT is not enabled we receive i64 values as a pair of i32 + // numbers which is converted to single int53 number. In necessary, we also + // split the return value into a pair of i32 numbers. + return modifyJSFunction(snippet, (args, body, async_, oneliner) => { + let argLines = args.split('\n'); + argLines = argLines.map((line) => line.split('//')[0]); + const argNames = argLines + .join(' ') + .split(',') + .map((name) => name.trim()); + const newArgs = []; + let argConversions = ''; + if (sig.length > argNames.length + 1) { + error(`handleI64Signatures: signature too long for ${symbol}`); + return snippet; + } + for (let i = 0; i < argNames.length; i++) { + const name = argNames[i]; + // If sig is shorter than argNames list then argType will be undefined + // here, which will result in the default case below. + const argType = sig[i + 1]; + if (WASM_BIGINT && ((MEMORY64 && argType == 'p') || (i53abi && argType == 'j'))) { + argConversions += ` ${receiveI64ParamAsI53(name, undefined, false)}\n`; + } else { + if (argType == 'j' && i53abi) { argConversions += ` ${receiveI64ParamAsI53(name, undefined, false)}\n`; + newArgs.push(defineI64Param(name)); + } else if (argType == 'p' && CAN_ADDRESS_2GB) { + argConversions += ` ${name} >>>= 0;\n`; + newArgs.push(name); } else { - if (argType == 'j' && i53abi) { - argConversions += ` ${receiveI64ParamAsI53(name, undefined, false)}\n`; - newArgs.push(defineI64Param(name)); - } else if (argType == 'p' && CAN_ADDRESS_2GB) { - argConversions += ` ${name} >>>= 0;\n`; - newArgs.push(name); - } else { - newArgs.push(name); - } + newArgs.push(name); } } + } - if (!WASM_BIGINT) { - args = newArgs.join(','); - } + if (!WASM_BIGINT) { + args = newArgs.join(','); + } - if ((sig[0] == 'j' && i53abi) || (sig[0] == 'p' && MEMORY64)) { - // For functions that where we need to mutate the return value, we - // also need to wrap the body in an inner function. - if (oneliner) { - if (argConversions) { - return `${async_}(${args}) => { + if ((sig[0] == 'j' && i53abi) || (sig[0] == 'p' && MEMORY64)) { + const await_ = async_ ? 'await ' : ''; + // For functions that where we need to mutate the return value, we + // also need to wrap the body in an inner function. + if (oneliner) { + // Special case for abort(), this a noreturn function and but closure + // compiler doesn't have a way to express that, so it complains if we + // do `BigInt(abort(..))`. + if (body.startsWith('abort(')) { + return snippet; + } + if (argConversions) { + return `${async_}(${args}) => { ${argConversions} - return ${makeReturn64(body)}; +return ${makeReturn64(await_ + body)}; }`; - } - return `${async_}(${args}) => ${makeReturn64(body)};`; } - return `\ + return `${async_}(${args}) => ${makeReturn64(await_ + body)};`; + } + return `\ ${async_}function(${args}) { ${argConversions} - var ret = (() => { ${body} })(); - return ${makeReturn64('ret')}; +var ret = (() => { ${body} })(); +return ${makeReturn64(await_ + 'ret')}; }`; - } + } - // Otherwise no inner function is needed and we covert the arguments - // before executing the function body. - if (oneliner) { - body = `return ${body}`; - } - return `\ + // Otherwise no inner function is needed and we covert the arguments + // before executing the function body. + if (oneliner) { + body = `return ${body}`; + } + return `\ ${async_}function(${args}) { ${argConversions} - ${body}; +${body}; }`; - }); + }); +} + +export async function runJSify(outputFile, symbolsOnly) { + const libraryItems = []; + const symbolDeps = {}; + const asyncFuncs = []; + let postSets = []; + + LibraryManager.load(); + + let outputHandle = process.stdout; + if (outputFile) { + outputHandle = await fs.open(outputFile, 'w'); + } + + async function writeOutput(str) { + await outputHandle.write(str + '\n'); + } + + const symbolsNeeded = DEFAULT_LIBRARY_FUNCS_TO_INCLUDE; + symbolsNeeded.push(...extraLibraryFuncs); + for (const sym of EXPORTED_RUNTIME_METHODS) { + if ('$' + sym in LibraryManager.library) { + symbolsNeeded.push('$' + sym); + } + } + + for (const key of Object.keys(LibraryManager.library)) { + if (!isDecorator(key)) { + if (INCLUDE_FULL_LIBRARY || EXPORTED_FUNCTIONS.has(mangleCSymbolName(key))) { + symbolsNeeded.push(key); + } + } } function processLibraryFunction(snippet, symbol, mangled, deps, isStub) { @@ -284,21 +408,35 @@ ${argConversions} // apply LIBRARY_DEBUG if relevant if (LIBRARY_DEBUG && !isJsOnlySymbol(symbol)) { - snippet = modifyJSFunction( - snippet, - (args, body, async) => `\ + snippet = modifyJSFunction(snippet, (args, body, _async, oneliner) => { + var run_func; + if (oneliner) { + run_func = `var ret = ${body}`; + } else { + run_func = `var ret = (() => { ${body} })();`; + } + return `\ function(${args}) { - var ret = (() => { if (runtimeDebug) err("[library call:${mangled}: " + Array.prototype.slice.call(arguments).map(prettyPrint) + "]"); - ${body} - })(); - if (runtimeDebug && typeof ret != "undefined") err(" [ return:" + prettyPrint(ret)); + dbg("[library call:${mangled}: " + Array.prototype.slice.call(arguments).map(prettyPrint) + "]"); + ${run_func} + dbg(" [ return:" + prettyPrint(ret)); return ret; -}`, - ); +}`; + }); } const sig = LibraryManager.library[symbol + '__sig']; const i53abi = LibraryManager.library[symbol + '__i53abi']; + if (i53abi) { + if (!sig) { + error(`JS library error: '__i53abi' decorator requires '__sig' decorator: '${symbol}'`); + } + if (!sig.includes('j')) { + error( + `JS library error: '__i53abi' only makes sense when '__sig' includes 'j' (int64): '${symbol}'`, + ); + } + } if ( sig && ((i53abi && sig.includes('j')) || ((MEMORY64 || CAN_ADDRESS_2GB) && sig.includes('p'))) @@ -312,7 +450,7 @@ function(${args}) { if (proxyingMode !== 'sync' && proxyingMode !== 'async' && proxyingMode !== 'none') { throw new Error(`Invalid proxyingMode ${symbol}__proxy: '${proxyingMode}' specified!`); } - if (SHARED_MEMORY) { + if (SHARED_MEMORY && proxyingMode != 'none') { const sync = proxyingMode === 'sync'; if (PTHREADS) { snippet = modifyJSFunction(snippet, (args, body, async_, oneliner) => { @@ -324,7 +462,7 @@ function(${args}) { MEMORY64 && rtnType == 'p' ? 'proxyToMainThreadPtr' : 'proxyToMainThread'; deps.push('$' + proxyFunc); return ` -function(${args}) { +${async_}function(${args}) { if (ENVIRONMENT_IS_PTHREAD) return ${proxyFunc}(${proxiedFunctionTable.length}, 0, ${+sync}${args ? ', ' : ''}${args}); ${body} @@ -349,27 +487,6 @@ function(${args}) { return snippet; } - function addImplicitDeps(snippet, deps) { - // There are some common dependencies that we inject automatically by - // conservatively scanning the input functions for their usage. - // Specifically, these are dependencies that are very common and would be - // burdensome to add manually to all functions. - // The first four are deps that are automatically/conditionally added - // by the {{{ makeDynCall }}}, and {{{ runtimeKeepalivePush/Pop }}} macros. - const autoDeps = [ - 'getDynCaller', - 'getWasmTableEntry', - 'runtimeKeepalivePush', - 'runtimeKeepalivePop', - 'UTF8ToString', - ]; - for (const dep of autoDeps) { - if (snippet.includes(dep + '(')) { - deps.push('$' + dep); - } - } - } - function symbolHandler(symbol) { // In LLVM, exceptions generate a set of functions of form // __cxa_find_matching_catch_1(), __cxa_find_matching_catch_2(), etc. where @@ -379,7 +496,7 @@ function(${args}) { if (LINK_AS_CXX && !WASM_EXCEPTIONS && symbol.startsWith('__cxa_find_matching_catch_')) { if (DISABLE_EXCEPTION_THROWING) { error( - 'DISABLE_EXCEPTION_THROWING was set (likely due to -fno-exceptions), which means no C++ exception throwing support code is linked in, but exception catching code appears. Either do not set DISABLE_EXCEPTION_THROWING (if you do want exception throwing) or compile all source files with -fno-except (so that no exceptions support code is required); also make sure DISABLE_EXCEPTION_CATCHING is set to the right value - if you want exceptions, it should be off, and vice versa.', + 'DISABLE_EXCEPTION_THROWING was set (likely due to -fno-exceptions), which means no C++ exception throwing support code is linked in, but exception catching code appears. Either do not set DISABLE_EXCEPTION_THROWING (if you do want exception throwing) or compile all source files with -fno-exceptions (so that no exceptions support code is required); also make sure DISABLE_EXCEPTION_CATCHING is set to the right value - if you want exceptions, it should be off, and vice versa.', ); return; } @@ -392,7 +509,7 @@ function(${args}) { // what we just added to the library. } - function addFromLibrary(symbol, dependent, force = false) { + function addFromLibrary(symbol, dependent) { // don't process any special identifiers. These are looked up when // processing the base name of the identifier. if (isDecorator(symbol)) { @@ -401,7 +518,7 @@ function(${args}) { // if the function was implemented in compiled code, there is no need to // include the js version - if (WASM_EXPORTS.has(symbol) && !force) { + if (WASM_EXPORTS.has(symbol)) { return; } @@ -427,9 +544,7 @@ function(${args}) { if (ASYNCIFY) { const original = LibraryManager.library[symbol]; if (typeof original == 'function') { - isAsyncFunction = - LibraryManager.library[symbol + '__async'] || - original.constructor.name == 'AsyncFunction'; + isAsyncFunction = LibraryManager.library[symbol + '__async']; } if (isAsyncFunction) { asyncFuncs.push(symbol); @@ -477,22 +592,19 @@ function(${args}) { mangled + ' may need to be added to EXPORTED_FUNCTIONS if it arrives from a system library', ); - } else if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) { + } else if (WARN_ON_UNDEFINED_SYMBOLS) { warn(msg); + } else { + debugLog(msg); } if (symbol === '__main_argc_argv' && STANDALONE_WASM) { warn('To build in STANDALONE_WASM mode without a main(), use emcc --no-entry'); } } - if (!RELOCATABLE) { - // emit a stub that will fail at runtime - LibraryManager.library[symbol] = new Function(`abort('missing function: ${symbol}');`); - // We have already warned/errored about this function, so for the purposes of Closure use, mute all type checks - // regarding this function, marking ot a variadic function that can take in anything and return anything. - // (not useful to warn/error multiple times) - LibraryManager.library[symbol + '__docs'] = '/** @type {function(...*):?} */'; - isStub = true; - } else { + + // emit a stub that will fail at runtime + var stubFunctionBody = `abort('missing function: ${symbol}');` + if (RELOCATABLE) { // Create a stub for this symbol which can later be replaced by the // dynamic linker. If this stub is called before the symbol is // resolved assert in debug builds or trap in release builds. @@ -506,10 +618,10 @@ function(${args}) { if (ASSERTIONS) { assertion += `if (!${target} || ${target}.stub) abort("external symbol '${symbol}' is missing. perhaps a side module was not linked in? if this function was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment");\n`; } - const functionBody = assertion + `return ${target}(...args);`; - LibraryManager.library[symbol] = new Function('...args', functionBody); - isStub = true; + stubFunctionBody = assertion + `return ${target}(...args);`; } + isStub = true; + LibraryManager.library[symbol] = new Function('...args', stubFunctionBody); } librarySymbols.push(mangled); @@ -519,66 +631,73 @@ function(${args}) { // Check for dependencies on `__internal` symbols from user libraries. const isUserSymbol = LibraryManager.library[symbol + '__user']; - deps.forEach((dep) => { + for (const dep of deps) { if (isUserSymbol && LibraryManager.library[dep + '__internal']) { warn(`user library symbol '${symbol}' depends on internal symbol '${dep}'`); } - }); + } - let isFunction = false; - let aliasTarget; + const isFunction = typeof snippet == 'function'; + let isNativeAlias = false; + + const postsetId = symbol + '__postset'; + const postset = LibraryManager.library[postsetId]; + if (postset) { + // A postset is either code to run right now, or some text we should emit. + // If it's code, it may return some text to emit as well. + const postsetString = typeof postset == 'function' ? postset() : postset; + if (postsetString && !addedLibraryItems[postsetId]) { + addedLibraryItems[postsetId] = true; + postSets.push(postsetString + ';'); + } + } if (typeof snippet == 'string') { if (snippet[0] != '=') { - if (LibraryManager.library[snippet]) { + if (LibraryManager.library[snippet] || WASM_EXPORTS.has(snippet)) { // Redirection for aliases. We include the parent, and at runtime // make ourselves equal to it. This avoid having duplicate // functions with identical content. - aliasTarget = snippet; - snippet = mangleCSymbolName(aliasTarget); - deps.push(aliasTarget); + const aliasTarget = resolveAlias(snippet); + if (WASM_EXPORTS.has(aliasTarget)) { + //printErr(`native alias: ${mangled} -> ${snippet}`); + //console.error(WASM_EXPORTS); + nativeAliases[mangled] = aliasTarget; + snippet = undefined; + isNativeAlias = true; + } else { + //printErr(`js alias: ${mangled} -> ${snippet}`); + deps.push(aliasTarget); + snippet = mangleCSymbolName(aliasTarget); + } } } } else if (typeof snippet == 'object') { snippet = stringifyWithFunctions(snippet); addImplicitDeps(snippet, deps); - } else if (typeof snippet == 'function') { - isFunction = true; + } else if (isFunction) { snippet = processLibraryFunction(snippet, symbol, mangled, deps, isStub); addImplicitDeps(snippet, deps); - } - - const postsetId = symbol + '__postset'; - let postset = LibraryManager.library[postsetId]; - if (postset) { - // A postset is either code to run right now, or some text we should emit. - // If it's code, it may return some text to emit as well. - if (typeof postset == 'function') { - postset = postset(); - } - if (postset && !addedLibraryItems[postsetId]) { - addedLibraryItems[postsetId] = true; - postSets.push(postset + ';'); + if (CHECK_DEPS && !isUserSymbol) { + checkDependencies(symbol, snippet, deps, postset?.toString()); } } - if (VERBOSE) { - printErr(`adding ${symbol} (referenced by ${dependent})`); - } + debugLog(`adding ${symbol} (referenced by ${dependent})`); function addDependency(dep) { // dependencies can be JS functions, which we just run if (typeof dep == 'function') { return dep(); } // $noExitRuntime is special since there are conditional usages of it - // in library.js and library_pthread.js. These happen before deps are + // in libcore.js and libpthread.js. These happen before deps are // processed so depending on it via `__deps` doesn't work. if (dep === '$noExitRuntime') { error( 'noExitRuntime cannot be referenced via __deps mechanism. Use DEFAULT_LIBRARY_FUNCS_TO_INCLUDE or EXPORTED_RUNTIME_METHODS', ); } - return addFromLibrary(dep, `${symbol}, referenced by ${dependent}`, dep === aliasTarget); + return addFromLibrary(dep, `${symbol}, referenced by ${dependent}`); } let contentText; if (isFunction) { @@ -602,6 +721,7 @@ function(${args}) { // Handle arrow functions contentText = `var ${mangled} = ` + contentText + ';'; } else if (contentText.startsWith('class ')) { + // Handle class declarations (which also have typeof == 'function'.) contentText = contentText.replace(/^class /, `class ${mangled} `); } else { // Handle regular (non-arrow) functions @@ -613,25 +733,36 @@ function(${args}) { // emits // 'var foo;[code here verbatim];' contentText = 'var ' + mangled + snippet; - if (snippet[snippet.length - 1] != ';' && snippet[snippet.length - 1] != '}') + if (snippet[snippet.length - 1] != ';' && snippet[snippet.length - 1] != '}') { contentText += ';'; + } } else if (typeof snippet == 'undefined') { - contentText = `var ${mangled};`; + // For JS library functions that are simply aliases of native symbols, + // we don't need to generate anything here. Instead these get included + // and exported alongside native symbols. + // See `create_receiving` in `tools/emscripten.py`. + if (isNativeAlias) { + contentText = ''; + } else { + contentText = `var ${mangled};`; + } } else { // In JS libraries // foo: '=[value]' // emits // 'var foo = [value];' if (typeof snippet == 'string' && snippet[0] == '=') { - snippet = snippet.substr(1); + snippet = snippet.slice(1); } contentText = `var ${mangled} = ${snippet};`; } - // asm module exports are done in emscripten.py, after the asm module is ready. Here - // we also export library methods as necessary. - if ((EXPORT_ALL || EXPORTED_FUNCTIONS.has(mangled)) && !isStub) { - contentText += `\nModule['${mangled}'] = ${mangled};`; + + if (contentText && MODULARIZE == 'instance' && (EXPORT_ALL || EXPORTED_FUNCTIONS.has(mangled)) && !isStub) { + // In MODULARIZE=instance mode mark JS library symbols are exported at + // the point of declaration. + contentText = 'export ' + contentText; } + // Relocatable code needs signatures to create proper wrappers. if (sig && RELOCATABLE) { if (!WASM_BIGINT) { @@ -649,13 +780,11 @@ function(${args}) { } } - let commentText = ''; - if (force) { - commentText += '/** @suppress {duplicate } */\n'; - } - + // Add the docs if they exist and if we are actually emitting a declaration. + // See the TODO about wasmTable above. let docs = LibraryManager.library[symbol + '__docs']; - if (docs) { + let commentText = ''; + if (contentText != '' && docs) { commentText += docs + '\n'; } @@ -679,8 +808,12 @@ function(${args}) { libraryItems.push(JS); } - function includeFile(fileName, needsPreprocess = true) { - print(getIncludeFile(fileName, needsPreprocess)); + function includeSystemFile(fileName) { + writeOutput(getSystemIncludeFile(fileName)); + } + + function includeFile(fileName) { + writeOutput(getIncludeFile(fileName)); } function finalCombiner() { @@ -706,17 +839,23 @@ function(${args}) { postSets.push(...orderedPostSets); const shellFile = MINIMAL_RUNTIME ? 'shell_minimal.js' : 'shell.js'; - includeFile(shellFile); + includeSystemFile(shellFile); const preFile = MINIMAL_RUNTIME ? 'preamble_minimal.js' : 'preamble.js'; - includeFile(preFile); + includeSystemFile(preFile); + writeOutput('// Begin JS library code\n'); for (const item of libraryItems.concat(postSets)) { - print(indentify(item || '', 2)); + writeOutput(indentify(item || '', 2)); + } + writeOutput('// End JS library code\n'); + + if (!MINIMAL_RUNTIME) { + includeSystemFile('postlibrary.js'); } if (PTHREADS) { - print(` + writeOutput(` // proxiedFunctionTable specifies the list of functions that can be called // either synchronously or asynchronously from other threads in postMessage()d // or internally queued events. This way a pthread in a Worker can synchronously @@ -727,36 +866,41 @@ var proxiedFunctionTable = [ `); } - if (errorOccured()) { - throw Error('Aborting compilation due to previous errors'); - } - // This is the main 'post' pass. Print out the generated code // that we have here, together with the rest of the output // that we started to print out earlier (see comment on the // "Final shape that will be created"). - print('// EMSCRIPTEN_END_FUNCS\n'); + writeOutput('// EMSCRIPTEN_END_FUNCS\n'); const postFile = MINIMAL_RUNTIME ? 'postamble_minimal.js' : 'postamble.js'; - includeFile(postFile); + includeSystemFile(postFile); for (const fileName of POST_JS_FILES) { - includeFile(fileName, shouldPreprocess(fileName)); + includeFile(fileName); } - if (MODULARIZE) { - includeFile('postamble_modularize.js'); + if (MODULARIZE && MODULARIZE != 'instance') { + includeSystemFile('postamble_modularize.js'); } - print( + if (errorOccured()) { + throw Error('Aborting compilation due to previous errors'); + } + + writeOutput( '//FORWARDED_DATA:' + JSON.stringify({ librarySymbols, + nativeAliases, warnings: warningOccured(), asyncFuncs, libraryDefinitions: LibraryManager.libraryDefinitions, + ATPRERUNS: ATPRERUNS.join('\n'), + ATMODULES: ATMODULES.join('\n'), ATINITS: ATINITS.join('\n'), - ATMAINS: STRICT ? '' : ATMAINS.join('\n'), + ATPOSTCTORS: ATPOSTCTORS.join('\n'), + ATMAINS: ATMAINS.join('\n'), + ATPOSTRUNS: ATPOSTRUNS.join('\n'), ATEXITS: ATEXITS.join('\n'), }), ); @@ -767,7 +911,7 @@ var proxiedFunctionTable = [ } if (symbolsOnly) { - print( + writeOutput( JSON.stringify({ deps: symbolDeps, asyncFuncs, @@ -781,6 +925,8 @@ var proxiedFunctionTable = [ if (errorOccured()) { throw Error('Aborting compilation due to previous errors'); } + + if (outputFile) await outputHandle.close(); } addToCompileTimeContext({ diff --git a/src/lib/libaddfunction.js b/src/lib/libaddfunction.js new file mode 100644 index 0000000000000..4f2cde4089bb9 --- /dev/null +++ b/src/lib/libaddfunction.js @@ -0,0 +1,256 @@ +/** + * @license + * Copyright 2020 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + // This gives correct answers for everything less than 2^{14} = 16384 + // I hope nobody is contemplating functions with 16384 arguments... + $uleb128EncodeWithLen__internal: true, + $uleb128EncodeWithLen: (arr) => { + const n = arr.length; +#if ASSERTIONS + assert(n < 16384); +#endif + // 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]; + }, +#if WASM_JS_TYPES + // Converts a signature like 'vii' into a description of the wasm types, like + // { parameters: ['i32', 'i32'], results: [] }. + $sigToWasmTypes__internal: true, + $sigToWasmTypes: (sig) => { +#if ASSERTIONS && !WASM_BIGINT + assert(!sig.includes('j'), 'i64 not permitted in function signatures when WASM_BIGINT is disabled'); +#endif + var typeNames = { + 'i': 'i32', + 'j': 'i64', + 'f': 'f32', + 'd': 'f64', + 'e': 'externref', +#if MEMORY64 + 'p': 'i64', +#else + 'p': 'i32', +#endif + }; + var type = { + parameters: [], + results: sig[0] == 'v' ? [] : [typeNames[sig[0]]] + }; + for (var i = 1; i < sig.length; ++i) { +#if ASSERTIONS + assert(sig[i] in typeNames, 'invalid signature char: ' + sig[i]); +#endif + type.parameters.push(typeNames[sig[i]]); + } + return type; + }, +#endif + $wasmTypeCodes__internal: true, + // Note: using template literal here instead of plain object + // because jsify serializes objects w/o quotes and Closure will then + // incorrectly mangle the properties. + $wasmTypeCodes: `{ + 'i': 0x7f, // i32 +#if MEMORY64 + 'p': 0x7e, // i64 +#else + 'p': 0x7f, // i32 +#endif + 'j': 0x7e, // i64 + 'f': 0x7d, // f32 + 'd': 0x7c, // f64 + 'e': 0x6f, // externref + }`, + + $generateTypePack__internal: true, + $generateTypePack__deps: ['$uleb128EncodeWithLen', '$wasmTypeCodes'], + $generateTypePack: (types) => uleb128EncodeWithLen(Array.from(types, (type) => { + var code = wasmTypeCodes[type]; +#if ASSERTIONS + assert(code, `invalid signature char: ${type}`); +#endif + return code; + })), + +#if !WASM2JS || WASM == 2 + // Wraps a JS function as a wasm function with a given signature. + $convertJsFunctionToWasm__deps: [ + '$uleb128EncodeWithLen', +#if WASM_JS_TYPES + '$sigToWasmTypes', +#endif + '$generateTypePack' + ], + $convertJsFunctionToWasm: (func, sig) => { +#if ASSERTIONS && !WASM_BIGINT + assert(!sig.includes('j'), 'i64 not permitted in function signatures when WASM_BIGINT is disabled'); +#endif +#if WASM_JS_TYPES + // If the type reflection proposal is available, use the new + // "WebAssembly.Function" constructor. + // Otherwise, construct a minimal wasm module importing the JS function and + // re-exporting it. + if (WebAssembly.Function) { + return new WebAssembly.Function(sigToWasmTypes(sig), func); + } +#endif + + // 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; + }, +#endif // !WASM2JS && WASM != 2 + + $freeTableIndexes: [], + + // Weak map of functions in the table to their indexes, created on first use. + $functionsInTableMap: undefined, + + $getEmptyTableSlot__deps: ['$freeTableIndexes', '$wasmTable'], + $getEmptyTableSlot: () => { + // Reuse a free index if there is one, otherwise grow. + if (freeTableIndexes.length) { + return freeTableIndexes.pop(); + } +#if ASSERTIONS + try { + #endif + // Grow the table + return wasmTable['grow']({{{ toIndexType('1') }}}); +#if ASSERTIONS + } catch (err) { + if (!(err instanceof RangeError)) { + throw err; + } + abort('Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.'); + } +#endif + }, + + $updateTableMap__deps: ['$getWasmTableEntry'], + $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); + } + } + } + }, + + $getFunctionAddress__deps: ['$updateTableMap', '$functionsInTableMap', '$wasmTable'], + $getFunctionAddress: (func) => { + // First, create the map if this is the first use. + if (!functionsInTableMap) { + functionsInTableMap = new WeakMap(); + updateTableMap(0, {{{ from64Expr('wasmTable.length') }}}); + } + return functionsInTableMap.get(func) || 0; + }, + + /** + * Add a function to the table. + * 'sig' parameter is required if the function being added is a JS function. + */ + $addFunction__docs: '/** @param {string=} sig */', + $addFunction__deps: ['$getFunctionAddress', + '$functionsInTableMap', '$getEmptyTableSlot', + '$setWasmTableEntry', +#if !WASM2JS || WASM == 2 + '$convertJsFunctionToWasm', +#endif +#if ASSERTIONS >= 2 + '$getWasmTableEntry', '$wasmTable', +#endif + ], + + $addFunction: (func, sig) => { +#if ASSERTIONS + assert(typeof func != 'undefined'); +#endif // ASSERTIONS + // 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. + +#if ASSERTIONS >= 2 + // Make sure functionsInTableMap is actually up to date, that is, that this + // function is not actually in the wasm Table despite not being tracked in + // functionsInTableMap. + for (var i = 0; i < wasmTable.length; i++) { + assert(getWasmTableEntry(i) != func, 'function in Table but not functionsInTableMap'); + } +#endif + + var ret = getEmptyTableSlot(); + +#if WASM2JS && WASM != 2 + setWasmTableEntry(ret, func); +#else + // 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; + } +#if ASSERTIONS + assert(typeof sig != 'undefined', 'Missing signature argument to addFunction: ' + func); +#endif + var wrapped = convertJsFunctionToWasm(func, sig); + setWasmTableEntry(ret, wrapped); + } +#endif + + functionsInTableMap.set(func, ret); + + return ret; + }, + + $removeFunction__deps: ['$functionsInTableMap', '$freeTableIndexes', + '$getWasmTableEntry', '$setWasmTableEntry'], + $removeFunction: (index) => { + functionsInTableMap.delete(getWasmTableEntry(index)); + setWasmTableEntry(index, null); + freeTableIndexes.push(index); + }, +}); diff --git a/src/lib/libasync.js b/src/lib/libasync.js new file mode 100644 index 0000000000000..c79e4f6bfccbf --- /dev/null +++ b/src/lib/libasync.js @@ -0,0 +1,642 @@ +/** + * @license + * Copyright 2014 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +// +// Async support via ASYNCIFY +// + +addToLibrary({ + // error handling + + $runAndAbortIfError: (func) => { + try { + return func(); + } catch (e) { + abort(e); + } + }, + +#if ASYNCIFY + $Asyncify__deps: ['$runAndAbortIfError', '$callUserCallback', +#if !MINIMAL_RUNTIME + '$runtimeKeepalivePush', '$runtimeKeepalivePop', +#endif +#if ASYNCIFY == 1 + // Needed by allocateData and handleSleep respectively + 'malloc', 'free', +#endif + ], + + $Asyncify: { + // + // Asyncify code that is shared between mode 1 (original) and mode 2 (JSPI). + // +#if ASYNCIFY == 1 && MEMORY64 + rewindArguments: new Map(), +#endif + instrumentWasmImports(imports) { +#if EMBIND_GEN_MODE + // Instrumenting is not needed when generating code. + return imports; +#endif +#if ASYNCIFY_DEBUG + dbg('asyncify instrumenting imports'); +#endif +#if ASSERTIONS && ASYNCIFY == 2 + assert('Suspending' in WebAssembly, 'JSPI not supported by current environment. Perhaps it needs to be enabled via flags?'); +#endif + var importPattern = {{{ new RegExp(`^(${ASYNCIFY_IMPORTS_EXCEPT_JS_LIBS.map(x => x.split('.')[1]).join('|').replace(/\*/g, '.*')})$`) }}}; + + for (let [x, original] of Object.entries(imports)) { + if (typeof original == 'function') { + let isAsyncifyImport = original.isAsync || importPattern.test(x); +#if ASYNCIFY == 2 + // Wrap async imports with a suspending WebAssembly function. + if (isAsyncifyImport) { +#if ASYNCIFY_DEBUG + dbg('asyncify: suspendOnReturnedPromise for', x, original); +#endif + imports[x] = original = new WebAssembly.Suspending(original); + } +#endif +#if ASSERTIONS && ASYNCIFY != 2 // We cannot apply assertions with stack switching, as the imports must not be modified from suspender.suspendOnReturnedPromise TODO find a way + imports[x] = (...args) => { + var originalAsyncifyState = Asyncify.state; + try { + return original(...args); + } finally { + // Only asyncify-declared imports are allowed to change the + // state. + // Changing the state from normal to disabled is allowed (in any + // function) as that is what shutdown does (and we don't have an + // explicit list of shutdown imports). + var changedToDisabled = + originalAsyncifyState === Asyncify.State.Normal && + Asyncify.state === Asyncify.State.Disabled; + // invoke_* functions are allowed to change the state if we do + // not ignore indirect calls. + var ignoredInvoke = x.startsWith('invoke_') && + {{{ !ASYNCIFY_IGNORE_INDIRECT }}}; + if (Asyncify.state !== originalAsyncifyState && + !isAsyncifyImport && + !changedToDisabled && + !ignoredInvoke) { + abort(`import ${x} was not in ASYNCIFY_IMPORTS, but changed the state`); + } + } + }; +#if MAIN_MODULE + // The dynamic library loader needs to be able to read .sig + // properties, so that it knows function signatures when it adds + // them to the table. + imports[x].sig = original.sig; +#endif // MAIN_MODULE +#endif // ASSERTIONS + } + } + }, +#if ASYNCIFY == 1 && MEMORY64 + saveRewindArguments(func, passedArguments) { + return Asyncify.rewindArguments.set(func, Array.from(passedArguments)); + }, + restoreRewindArguments(func) { +#if ASSERTIONS + assert(Asyncify.rewindArguments.has(func)); +#endif + return Asyncify.rewindArguments.get(func); + }, +#endif + + instrumentFunction(original) { + var wrapper = (...args) => { +#if ASYNCIFY_DEBUG >= 2 + dbg(`ASYNCIFY: ${' '.repeat(Asyncify.exportCallStack.length)} try ${original}`); +#endif +#if ASYNCIFY == 1 + Asyncify.exportCallStack.push(original); + try { +#endif +#if ASYNCIFY == 1 && MEMORY64 + Asyncify.saveRewindArguments(original, args); +#endif + return original(...args); +#if ASYNCIFY == 1 + } finally { + if (!ABORT) { + var top = Asyncify.exportCallStack.pop(); +#if ASSERTIONS + assert(top === original); +#endif +#if ASYNCIFY_DEBUG >= 2 + dbg(`ASYNCIFY: ${' '.repeat(Asyncify.exportCallStack.length)} finally ${original}`); +#endif + Asyncify.maybeStopUnwind(); + } + } +#endif + }; +#if ASYNCIFY == 1 + Asyncify.funcWrappers.set(original, wrapper); +#endif +#if MAIN_MODULE + wrapper.orig = original; +#endif + return wrapper; + }, + + instrumentWasmExports(exports) { +#if EMBIND_GEN_MODE + // Instrumenting is not needed when generating code. + return exports; +#endif +#if ASYNCIFY_DEBUG + dbg('asyncify instrumenting exports'); +#endif +#if ASYNCIFY == 2 + var exportPattern = {{{ new RegExp(`^(${ASYNCIFY_EXPORTS.join('|').replace(/\*/g, '.*')})$`) }}}; + Asyncify.asyncExports = new Set(); +#endif + var ret = {}; + for (let [x, original] of Object.entries(exports)) { + if (typeof original == 'function') { + #if ASYNCIFY == 2 + // Wrap all exports with a promising WebAssembly function. + let isAsyncifyExport = exportPattern.test(x); + if (isAsyncifyExport) { + Asyncify.asyncExports.add(original); + original = Asyncify.makeAsyncFunction(original); + } +#endif + var wrapper = Asyncify.instrumentFunction(original); + ret[x] = wrapper; + + } else { + ret[x] = original; + } + } + return ret; + }, + +#if ASYNCIFY == 1 + // + // Original implementation of Asyncify. + // + State: { + Normal: 0, + Unwinding: 1, + Rewinding: 2, + Disabled: 3, + }, + state: 0, + StackSize: {{{ ASYNCIFY_STACK_SIZE }}}, + currData: null, + // The return value passed to wakeUp() in + // Asyncify.handleSleep((wakeUp) => {...}) is stored here, + // so we can return it later from the C function that called + // Asyncify.handleSleep() after rewinding finishes. + handleSleepReturnValue: 0, + // We must track which wasm exports are called into and + // exited, so that we know where the call stack began, + // which is where we must call to rewind it. + // This list contains the original Wasm exports. + exportCallStack: [], + callstackFuncToId: new Map(), + callStackIdToFunc: new Map(), + // Maps wasm functions to their corresponding wrapper function. + funcWrappers: new Map(), + callStackId: 0, + asyncPromiseHandlers: null, // { resolve, reject } pair for when *all* asynchronicity is done + sleepCallbacks: [], // functions to call every time we sleep + + getCallStackId(func) { +#if ASSERTIONS + assert(func); +#endif + 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_DEBUG + dbg('ASYNCIFY: maybe stop unwind', Asyncify.exportCallStack); +#endif + 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; +#if ASYNCIFY_DEBUG + dbg('ASYNCIFY: stop unwind'); +#endif + {{{ 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() { +#if ASSERTIONS + assert(Asyncify.currData, 'Tried to wait for an async operation when none is in progress.'); + assert(!Asyncify.asyncPromiseHandlers, 'Cannot have multiple async operations in flight at once'); +#endif + 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({{{ C_STRUCTS.asyncify_data_s.__size__ }}} + Asyncify.StackSize); + Asyncify.setDataHeader(ptr, ptr + {{{ C_STRUCTS.asyncify_data_s.__size__ }}}, Asyncify.StackSize); + Asyncify.setDataRewindFunc(ptr); + return ptr; + }, + + setDataHeader(ptr, stack, stackSize) { + {{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.stack_ptr, 'stack', '*') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.stack_limit, 'stack + stackSize', '*') }}}; + }, + + setDataRewindFunc(ptr) { + var bottomOfCallStack = Asyncify.exportCallStack[0]; +#if ASYNCIFY_DEBUG >= 2 + dbg(`ASYNCIFY: setDataRewindFunc(${ptr}), bottomOfCallStack is`, bottomOfCallStack, new Error().stack); +#endif +#if ASSERTIONS + assert(bottomOfCallStack, 'exportCallStack is empty'); +#endif + var rewindId = Asyncify.getCallStackId(bottomOfCallStack); + {{{ makeSetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'rewindId', 'i32') }}}; + }, + + getDataRewindFunc(ptr) { + var id = {{{ makeGetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'i32') }}}; + var func = Asyncify.callStackIdToFunc.get(id); +#if ASSERTIONS + assert(func, `id ${id} not found in callStackIdToFunc`); +#endif + return func; + }, + + doRewind(ptr) { + var original = Asyncify.getDataRewindFunc(ptr); +#if ASYNCIFY_DEBUG + dbg('ASYNCIFY: doRewind:', original); +#endif + var func = Asyncify.funcWrappers.get(original); +#if ASSERTIONS + assert(original); + assert(func); +#endif + // Once we have rewound and the stack we no longer need to artificially + // keep the runtime alive. + {{{ runtimeKeepalivePop(); }}} +#if MEMORY64 + // When re-winding, the arguments to a function are ignored. For i32 arguments we + // can just call the function with no args at all since and the engine will produce zeros + // for all arguments. However, for i64 arguments we get `undefined cannot be converted to + // BigInt`. + return func(...Asyncify.restoreRewindArguments(original)); +#else + return func(); +#endif + }, + + // This receives a function to call to start the async operation, and + // handles everything else for the user of this API. See emscripten_sleep() + // and other async methods for simple examples of usage. + handleSleep(startAsync) { +#if ASSERTIONS + assert(Asyncify.state !== Asyncify.State.Disabled, 'Asyncify cannot be done during or after the runtime exits'); +#endif + if (ABORT) return; +#if ASYNCIFY_DEBUG + dbg(`ASYNCIFY: handleSleep ${Asyncify.state}`); +#endif + 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 ASSERTIONS + assert(!handleSleepReturnValue || typeof handleSleepReturnValue == 'number' || typeof handleSleepReturnValue == 'boolean'); // old emterpretify API supported other stuff +#endif + if (ABORT) return; + Asyncify.handleSleepReturnValue = handleSleepReturnValue; + reachedCallback = true; + if (!reachedAfterCallback) { + // We are happening synchronously, so no need for async. + return; + } +#if ASSERTIONS + // This async operation did not happen synchronously, so we did + // unwind. In that case there can be no compiled code on the stack, + // as it might break later operations (we can rewind ok now, but if + // we unwind again, we would unwind through the extra compiled code + // too). + assert(!Asyncify.exportCallStack.length, 'Waking up (starting to rewind) must be done from JS, without compiled code on the stack.'); +#endif +#if ASYNCIFY_DEBUG + dbg(`ASYNCIFY: start rewind ${Asyncify.currData}`); +#endif + 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 ASYNCIFY_DEBUG + dbg(`ASYNCIFY: start unwind ${Asyncify.currData}`); +#endif + if (typeof MainLoop != 'undefined' && MainLoop.func) { + MainLoop.pause(); + } + runAndAbortIfError(() => _asyncify_start_unwind(Asyncify.currData)); + } + } else if (Asyncify.state === Asyncify.State.Rewinding) { + // Stop a resume. +#if ASYNCIFY_DEBUG + dbg('ASYNCIFY: stop rewind'); +#endif + 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; + }, + + // Unlike `handleSleep`, accepts a function returning a `Promise` + // and uses the fulfilled value instead of passing in a separate callback. + // + // This is particularly useful for native JS `async` functions where the + // returned value will "just work" and be passed back to C++. + handleAsync: (startAsync) => Asyncify.handleSleep((wakeUp) => { + // TODO: add error handling as a second param when handleSleep implements it. + startAsync().then(wakeUp); + }), + +#elif ASYNCIFY == 2 + // + // JSPI implementation of Asyncify. + // + + // Stores all the exported raw Wasm functions that are wrapped with async + // WebAssembly.Functions. + asyncExports: null, + isAsyncExport(func) { + return Asyncify.asyncExports?.has(func); + }, + handleAsync: async (startAsync) => { + {{{ runtimeKeepalivePush(); }}} + try { + return await startAsync(); + } finally { + {{{ runtimeKeepalivePop(); }}} + } + }, + handleSleep: (startAsync) => Asyncify.handleAsync(() => new Promise(startAsync)), + makeAsyncFunction(original) { +#if ASYNCIFY_DEBUG + dbg('asyncify: makeAsyncFunction for', original); +#endif + return WebAssembly.promising(original); + }, +#endif + }, + + emscripten_sleep__deps: ['$safeSetTimeout'], + emscripten_sleep__async: true, + emscripten_sleep: (ms) => Asyncify.handleSleep((wakeUp) => safeSetTimeout(wakeUp, ms)), + + emscripten_wget_data__deps: ['$asyncLoad', 'malloc'], + emscripten_wget_data__async: true, + 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); + {{{ makeSetValue('pbuffer', 0, 'buffer', '*') }}}; + {{{ makeSetValue('pnum', 0, 'byteArray.length', 'i32') }}}; + {{{ makeSetValue('perror', 0, '0', 'i32') }}}; + } catch (err) { + {{{ makeSetValue('perror', 0, '1', 'i32') }}}; + } + }), + + emscripten_scan_registers__deps: ['$safeSetTimeout'], + emscripten_scan_registers__async: true, + 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 + {{{ C_STRUCTS.asyncify_data_s.__size__ }}}; + var stackEnd = {{{ makeGetValue('Asyncify.currData', 0, '*') }}}; + {{{ makeDynCall('vpp', 'func') }}}(stackBegin, stackEnd); + wakeUp(); + }, 0); + }); + }, + + _load_secondary_module__sig: 'v', + _load_secondary_module__async: true, + _load_secondary_module: async function() { + // 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': wasmExports}; + // Replace '.wasm' suffix with '.deferred.wasm'. + var deferred = wasmBinaryFile.slice(0, -5) + '.deferred.wasm'; + await instantiateAsync(null, deferred, imports); + }, + + $Fibers__deps: ['$Asyncify', 'emscripten_stack_set_limits', '$stackRestore'], + $Fibers: { + nextFiber: 0, + trampolineRunning: false, + trampoline() { + if (!Fibers.trampolineRunning && Fibers.nextFiber) { + Fibers.trampolineRunning = true; + do { + var fiber = Fibers.nextFiber; + Fibers.nextFiber = 0; +#if ASYNCIFY_DEBUG >= 2 + dbg("ASYNCIFY/FIBER: trampoline jump into fiber", fiber, new Error().stack); +#endif + Fibers.finishContextSwitch(fiber); + } while (Fibers.nextFiber); + Fibers.trampolineRunning = false; + } + }, + /* + * NOTE: This function is the asynchronous part of emscripten_fiber_swap. + */ + finishContextSwitch(newFiber) { + var stack_base = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_base, '*') }}}; + var stack_max = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_limit, '*') }}}; + _emscripten_stack_set_limits(stack_base, stack_max); + +#if STACK_OVERFLOW_CHECK >= 2 + ___set_stack_limits(stack_base, stack_max); +#endif + + stackRestore({{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.stack_ptr, '*') }}}); + + var entryPoint = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.entry, '*') }}}; + + if (entryPoint !== 0) { +#if STACK_OVERFLOW_CHECK + writeStackCookie(); +#endif +#if ASYNCIFY_DEBUG + dbg('ASYNCIFY/FIBER: entering fiber', newFiber, 'for the first time'); +#endif + Asyncify.currData = null; + {{{ makeSetValue('newFiber', C_STRUCTS.emscripten_fiber_s.entry, 0, '*') }}}; + + var userData = {{{ makeGetValue('newFiber', C_STRUCTS.emscripten_fiber_s.user_data, '*') }}}; + {{{ makeDynCall('vp', 'entryPoint') }}}(userData); + } else { + var asyncifyData = newFiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}}; + Asyncify.currData = asyncifyData; + +#if ASYNCIFY_DEBUG + dbg('ASYNCIFY/FIBER: start rewind', asyncifyData, '(resuming fiber', newFiber, ')'); +#endif + Asyncify.state = Asyncify.State.Rewinding; + _asyncify_start_rewind(asyncifyData); + Asyncify.doRewind(asyncifyData); + } + }, + }, + + emscripten_fiber_swap__deps: ["$Asyncify", "$Fibers", '$stackSave'], + emscripten_fiber_swap__async: true, + emscripten_fiber_swap: (oldFiber, newFiber) => { + if (ABORT) return; +#if ASYNCIFY_DEBUG + dbg('ASYNCIFY/FIBER: swap', oldFiber, '->', newFiber, 'state:', Asyncify.state); +#endif + if (Asyncify.state === Asyncify.State.Normal) { + Asyncify.state = Asyncify.State.Unwinding; + + var asyncifyData = oldFiber + {{{ C_STRUCTS.emscripten_fiber_s.asyncify_data }}}; + Asyncify.setDataRewindFunc(asyncifyData); + Asyncify.currData = asyncifyData; + +#if ASYNCIFY_DEBUG + dbg('ASYNCIFY/FIBER: start unwind', asyncifyData); +#endif + _asyncify_start_unwind(asyncifyData); + + var stackTop = stackSave(); + {{{ makeSetValue('oldFiber', C_STRUCTS.emscripten_fiber_s.stack_ptr, 'stackTop', '*') }}}; + + Fibers.nextFiber = newFiber; + } else { +#if ASSERTIONS + assert(Asyncify.state === Asyncify.State.Rewinding); +#endif +#if ASYNCIFY_DEBUG + dbg('ASYNCIFY/FIBER: stop rewind'); +#endif + Asyncify.state = Asyncify.State.Normal; + _asyncify_stop_rewind(); + Asyncify.currData = null; + } + }, +#else // ASYNCIFY + emscripten_sleep: () => { + abort('Please compile your program with async support in order to use asynchronous operations like emscripten_sleep'); + }, + emscripten_wget: (url, file) => { + abort('Please compile your program with async support in order to use asynchronous operations like emscripten_wget'); + }, + emscripten_wget_data: (url, pbuffer, pnum, perror) => { + abort('Please compile your program with async support in order to use asynchronous operations like emscripten_wget_data'); + }, + emscripten_scan_registers: (func) => { + abort('Please compile your program with async support in order to use asynchronous operations like emscripten_scan_registers'); + }, + emscripten_fiber_swap: (oldFiber, newFiber) => { + abort('Please compile your program with async support in order to use asynchronous operations like emscripten_fiber_swap'); + }, +#endif // ASYNCIFY +}); + +if (ASYNCIFY) { + extraLibraryFuncs.push('$Asyncify'); +} diff --git a/src/lib/libatomic.js b/src/lib/libatomic.js new file mode 100644 index 0000000000000..dc4fd5cb1b6e5 --- /dev/null +++ b/src/lib/libatomic.js @@ -0,0 +1,158 @@ +/** + * @license + * Copyright 2023 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +assert(SHARED_MEMORY); + +addToLibrary({ +// Chrome 87 shipped Atomics.waitAsync: +// https://www.chromestatus.com/feature/6243382101803008 +// However its implementation is faulty: +// https://bugs.chromium.org/p/chromium/issues/detail?id=1167541 +// Firefox Nightly 86.0a1 (2021-01-15) does not yet have it: +// https://bugzilla.mozilla.org/show_bug.cgi?id=1467846 +// And at the time of writing, no other browser has it either. +#if MIN_CHROME_VERSION < 91 || MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED || MIN_FIREFOX_VERSION != TARGET_NOT_SUPPORTED || ENVIRONMENT_MAY_BE_NODE + // Partially polyfill Atomics.waitAsync() if not available in the browser. + // Also polyfill for old Chrome-based browsers, where Atomics.waitAsync is + // broken until Chrome 91, see: + // https://bugs.chromium.org/p/chromium/issues/detail?id=1167541 + // https://github.com/tc39/proposal-atomics-wait-async/blob/master/PROPOSAL.md + // This polyfill performs polling with setTimeout() to observe a change in the + // target memory location. + $polyfillWaitAsync__postset: `if (!Atomics.waitAsync || (globalThis.navigator?.userAgent && Number((navigator.userAgent.match(/Chrom(e|ium)\\/([0-9]+)\\./)||[])[2]) < 91)) { + let __Atomics_waitAsyncAddresses = [/*[i32a, index, value, maxWaitMilliseconds, promiseResolve]*/]; + function __Atomics_pollWaitAsyncAddresses() { + let now = performance.now(); + let l = __Atomics_waitAsyncAddresses.length; + for (let i = 0; i < l; ++i) { + let a = __Atomics_waitAsyncAddresses[i]; + let expired = (now > a[3]); + let awoken = (Atomics.load(a[0], a[1]) != a[2]); + if (expired || awoken) { + __Atomics_waitAsyncAddresses[i--] = __Atomics_waitAsyncAddresses[--l]; + __Atomics_waitAsyncAddresses.length = l; + a[4](awoken ? 'ok': 'timed-out'); + } + } + if (l) { + // If we still have addresses to wait, loop the timeout handler to continue polling. + setTimeout(__Atomics_pollWaitAsyncAddresses, 10); + } + } + #if ASSERTIONS && WASM_WORKERS + if (!ENVIRONMENT_IS_WASM_WORKER) err('Current environment does not support Atomics.waitAsync(): polyfilling it, but this is going to be suboptimal.'); + #endif + /** + * @param {number=} maxWaitMilliseconds + */ + Atomics.waitAsync = (i32a, index, value, maxWaitMilliseconds) => { + let val = Atomics.load(i32a, index); + if (val != value) return { async: false, value: 'not-equal' }; + if (maxWaitMilliseconds <= 0) return { async: false, value: 'timed-out' }; + maxWaitMilliseconds = performance.now() + (maxWaitMilliseconds || Infinity); + let promiseResolve; + let promise = new Promise((resolve) => { promiseResolve = resolve; }); + if (!__Atomics_waitAsyncAddresses[0]) setTimeout(__Atomics_pollWaitAsyncAddresses, 10); + __Atomics_waitAsyncAddresses.push([i32a, index, value, maxWaitMilliseconds, promiseResolve]); + return { async: true, value: promise }; + }; +}`, +#endif + + $polyfillWaitAsync__internal: true, + $polyfillWaitAsync: () => { + // nop, used for its postset to ensure `Atomics.waitAsync()` polyfill is + // included exactly once and only included when needed. + // Any function using Atomics.waitAsync should depend on this. + }, + + $atomicWaitStates__internal: true, + $atomicWaitStates: ['ok', 'not-equal', 'timed-out'], + $liveAtomicWaitAsyncs: {}, + $liveAtomicWaitAsyncs__internal: true, + $liveAtomicWaitAsyncCounter: 0, + $liveAtomicWaitAsyncCounter__internal: true, + + emscripten_atomic_wait_async__deps: ['$atomicWaitStates', '$liveAtomicWaitAsyncs', '$liveAtomicWaitAsyncCounter', '$polyfillWaitAsync', '$callUserCallback'], + emscripten_atomic_wait_async: (addr, val, asyncWaitFinished, userData, maxWaitMilliseconds) => { + let wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('addr', 'i32') }}}, val, maxWaitMilliseconds); + if (!wait.async) return atomicWaitStates.indexOf(wait.value); + // Increment waitAsync generation counter, account for wraparound in case + // application does huge amounts of waitAsyncs per second (not sure if + // possible?) + // Valid counterrange: 0...2^31-1 + let counter = liveAtomicWaitAsyncCounter; + liveAtomicWaitAsyncCounter = Math.max(0, (liveAtomicWaitAsyncCounter+1)|0); + liveAtomicWaitAsyncs[counter] = addr; + {{{ runtimeKeepalivePush() }}} + wait.value.then((value) => { + if (liveAtomicWaitAsyncs[counter]) { + {{{ runtimeKeepalivePop() }}} + delete liveAtomicWaitAsyncs[counter]; + callUserCallback(() => {{{ makeDynCall('vpiip', 'asyncWaitFinished') }}}(addr, val, atomicWaitStates.indexOf(value), userData)); + } + }); + return -counter; + }, + + emscripten_atomic_cancel_wait_async__deps: ['$liveAtomicWaitAsyncs'], + emscripten_atomic_cancel_wait_async: (waitToken) => { +#if ASSERTIONS + if (waitToken == {{{ cDefs.ATOMICS_WAIT_NOT_EQUAL }}}) { + warnOnce('Attempted to call emscripten_atomic_cancel_wait_async() with a value ATOMICS_WAIT_NOT_EQUAL (1) that is not a valid wait token! Check success in return value from call to emscripten_atomic_wait_async()'); + } else if (waitToken == {{{ cDefs.ATOMICS_WAIT_TIMED_OUT }}}) { + warnOnce('Attempted to call emscripten_atomic_cancel_wait_async() with a value ATOMICS_WAIT_TIMED_OUT (2) that is not a valid wait token! Check success in return value from call to emscripten_atomic_wait_async()'); + } else if (waitToken > 0) { + warnOnce(`Attempted to call emscripten_atomic_cancel_wait_async() with an invalid wait token value ${waitToken}`); + } +#endif + var address = liveAtomicWaitAsyncs[waitToken]; + if (address) { + // Notify the waitAsync waiters on the memory location, so that JavaScript + // garbage collection can occur. + // See https://github.com/WebAssembly/threads/issues/176 + // This has the unfortunate effect of causing spurious wakeup of all other + // waiters at the address (which causes a small performance loss). + Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}}); + delete liveAtomicWaitAsyncs[waitToken]; + {{{ runtimeKeepalivePop() }}} + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + } + // This waitToken does not exist. + return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}}; + }, + + emscripten_atomic_cancel_all_wait_asyncs__deps: ['$liveAtomicWaitAsyncs'], + emscripten_atomic_cancel_all_wait_asyncs: () => { + let waitAsyncs = Object.values(liveAtomicWaitAsyncs); + waitAsyncs.forEach((address) => { + Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}}); + }); + liveAtomicWaitAsyncs = {}; + return waitAsyncs.length; + }, + + emscripten_atomic_cancel_all_wait_asyncs_at_address__deps: ['$liveAtomicWaitAsyncs'], + emscripten_atomic_cancel_all_wait_asyncs_at_address: (address) => { + let numCancelled = 0; + Object.keys(liveAtomicWaitAsyncs).forEach((waitToken) => { + if (liveAtomicWaitAsyncs[waitToken] == address) { + Atomics.notify(HEAP32, {{{ getHeapOffset('address', 'i32') }}}); + delete liveAtomicWaitAsyncs[waitToken]; + numCancelled++; + } + }); + return numCancelled; + }, + + emscripten_has_threading_support: () => !!globalThis.SharedArrayBuffer, + + emscripten_num_logical_cores: () => +#if ENVIRONMENT_MAY_BE_NODE + ENVIRONMENT_IS_NODE ? require('os').cpus().length : +#endif + navigator['hardwareConcurrency'], +}); diff --git a/src/lib/libautodebug.js b/src/lib/libautodebug.js new file mode 100644 index 0000000000000..dafdb6a9056c5 --- /dev/null +++ b/src/lib/libautodebug.js @@ -0,0 +1,166 @@ +/** + * @license + * Copyright 2022 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + + +#if !AUTODEBUG +#error "Should only be included in AUTODEBUG mode" +#endif + +addToLibrary({ + $log_execution: (loc) => dbg('log_execution ' + loc), + $get_i32: (loc, index, value) => { + dbg('get_i32 ' + [loc, index, value]); + return value; + }, + $get_i64__deps: ['setTempRet0'], + $get_i64: (loc, index, low, high) => { + dbg('get_i64 ' + [loc, index, low, high]); + _setTempRet0(high); + return low; + }, + $get_f32: (loc, index, value) => { + dbg('get_f32 ' + [loc, index, value]); + return value; + }, + $get_f64: (loc, index, value) => { + dbg('get_f64 ' + [loc, index, value]); + return value; + }, + $get_funcref: (loc, index, value) => { + dbg('get_funcref ' + [loc, index, value]); + return value; + }, + $get_externref: (loc, index, value) => { + dbg('get_externref ' + [loc, index, value]); + return value; + }, + $get_anyref: (loc, index, value) => { + dbg('get_anyref ' + [loc, index, value]); + return value; + }, + $get_exnref: (loc, index, value) => { + dbg('get_exnref ' + [loc, index, value]); + return value; + }, + $set_i32: (loc, index, value) => { + dbg('set_i32 ' + [loc, index, value]); + return value; + }, + $set_i64__deps: ['setTempRet0'], + $set_i64: (loc, index, low, high) => { + dbg('set_i64 ' + [loc, index, low, high]); + _setTempRet0(high); + return low; + }, + $set_f32: (loc, index, value) => { + dbg('set_f32 ' + [loc, index, value]); + return value; + }, + $set_f64: (loc, index, value) => { + dbg('set_f64 ' + [loc, index, value]); + return value; + }, + $set_funcref: (loc, index, value) => { + dbg('set_afuncef ' + [loc, index, value]); + return value; + }, + $set_externref: (loc, index, value) => { + dbg('set_externref ' + [loc, index, value]); + return value; + }, + $set_anyref: (loc, index, value) => { + dbg('set_anyref ' + [loc, index, value]); + return value; + }, + $set_exnref: (loc, index, value) => { + dbg('set_exnref ' + [loc, index, value]); + return value; + }, + $load_ptr: (loc, bytes, offset, ptr) => { + dbg('load_ptr ' + [loc, bytes, offset, ptr]); + return ptr; + }, + $load_val_i32: (loc, value) => { + dbg('load_val_i32 ' + [loc, value]); + return value; + }, + $load_val_i64__deps: ['setTempRet0'], + $load_val_i64: (loc, low, high) => { + dbg('load_val_i64 ' + [loc, low, high]); + _setTempRet0(high); + return low; + }, + $load_val_f32: (loc, value) => { + dbg('load_val_f32 ' + [loc, value]); + return value; + }, + $load_val_f64: (loc, value) => { + dbg('load_val_f64 ' + [loc, value]); + return value; + }, + $store_ptr: (loc, bytes, offset, ptr) => { + dbg('store_ptr ' + [loc, bytes, offset, ptr]); + return ptr; + }, + $store_val_i32: (loc, value) => { + dbg('store_val_i32 ' + [loc, value]); + return value; + }, + $store_val_i64__deps: ['setTempRet0'], + $store_val_i64: (loc, low, high) => { + dbg('store_val_i64 ' + [loc, low, high]); + _setTempRet0(high); + return low; + }, + $store_val_f32: (loc, value) => { + dbg('store_val_f32 ' + [loc, value]); + return value; + }, + $store_val_f64: (loc, value) => { + dbg('store_val_f64 ' + [loc, value]); + return value; + }, + $memory_grow_pre: (loc, delta) => { + dbg('memory_grow_pre ' + [loc, delta]); + return delta; + }, + $memory_grow_post: (loc, result) => { + dbg('memory_grow_post ' + [loc, result]); + return result; + }, +}); + +extraLibraryFuncs.push( + '$log_execution', + '$get_i32', + '$get_i64', + '$get_f32', + '$get_f64', + '$get_funcref', + '$get_externref', + '$get_anyref', + '$get_exnref', + '$set_i32', + '$set_i64', + '$set_f32', + '$set_f64', + '$set_funcref', + '$set_externref', + '$set_anyref', + '$set_exnref', + '$load_ptr', + '$load_val_i32', + '$load_val_i64', + '$load_val_f32', + '$load_val_f64', + '$store_ptr', + '$store_val_i32', + '$store_val_i64', + '$store_val_f32', + '$store_val_f64', + '$memory_grow_pre', + '$memory_grow_post', +); diff --git a/src/lib/libbase64.js b/src/lib/libbase64.js new file mode 100644 index 0000000000000..48546622878f1 --- /dev/null +++ b/src/lib/libbase64.js @@ -0,0 +1,50 @@ +/** + * @license + * Copyright 2020 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + // Decodes a _known valid_ base64 string (without validation) and returns it as a new Uint8Array. + // Benchmarked to be around 5x faster compared to a simple + // "Uint8Array.from(atob(b64), c => c.charCodeAt(0))" (TODO: perhaps use this form in -Oz builds?) + $base64Decode__postset: ` + // Precreate a reverse lookup table from chars + // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" back to + // bytes to make decoding fast. + for (var base64ReverseLookup = new Uint8Array(123/*'z'+1*/), i = 25; i >= 0; --i) { + base64ReverseLookup[48+i] = 52+i; // '0-9' + base64ReverseLookup[65+i] = i; // 'A-Z' + base64ReverseLookup[97+i] = 26+i; // 'a-z' + } + base64ReverseLookup[43] = 62; // '+' + base64ReverseLookup[47] = 63; // '/' +`, + $base64Decode__docs: '/** @noinline */', + $base64Decode: (b64) => { +#if JS_BASE64_API + return Uint8Array.fromBase64(b64); +#else +#if ENVIRONMENT_MAY_BE_NODE + if (ENVIRONMENT_IS_NODE) { + var buf = Buffer.from(b64, 'base64'); + return new Uint8Array(buf.buffer, buf.byteOffset, buf.length); + } +#endif + +#if ASSERTIONS + assert(b64.length % 4 == 0); +#endif + var b1, b2, i = 0, j = 0, bLength = b64.length; + var output = new Uint8Array((bLength*3>>2) - (b64[bLength-2] == '=') - (b64[bLength-1] == '=')); + for (; i < bLength; i += 4, j += 3) { + b1 = base64ReverseLookup[b64.charCodeAt(i+1)]; + b2 = base64ReverseLookup[b64.charCodeAt(i+2)]; + output[j] = base64ReverseLookup[b64.charCodeAt(i)] << 2 | b1 >> 4; + output[j+1] = b1 << 4 | b2 >> 2; + output[j+2] = b2 << 6 | base64ReverseLookup[b64.charCodeAt(i+3)]; + } + return output; +#endif + }, +}); diff --git a/src/lib/libbootstrap.js b/src/lib/libbootstrap.js new file mode 100644 index 0000000000000..d521315b1cd0c --- /dev/null +++ b/src/lib/libbootstrap.js @@ -0,0 +1,53 @@ +/** + * @license + * Copyright 2015 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +// When bootstrapping struct info, we can't use the full library because +// it itself depends on the struct info information. + +#if !BOOTSTRAPPING_STRUCT_INFO +assert(false, "libbootstrap.js only designed for use with BOOTSTRAPPING_STRUCT_INFO") +#endif + +assert(Object.keys(LibraryManager.library).length === 0); +addToLibrary({ + $callRuntimeCallbacks: () => {}, + + $wasmMemory: 'memory', + + $ExitStatus: class { + name = 'ExitStatus'; + constructor(status) { + this.message = `Program terminated with exit(${status})`; + this.status = status; + } + }, + + $exitJS__deps: ['$ExitStatus'], + $exitJS: (code) => quit_(code, new ExitStatus(code)), + + $handleException: (e) => { + if (e instanceof ExitStatus || e == 'unwind') { + return EXITSTATUS; + } + quit_(1, e); + }, + + fd_write__sig: 'iippp', + fd_write: (fd, iov, iovcnt, pnum) => { + // implementation almost copied from libwasi.js one for SYSCALLS_REQUIRE_FILESYSTEM=0 + // (the only difference is that we can't use C_STRUCTS here) + var num = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = {{{ makeGetValue('iov', 0, '*') }}}; + var len = {{{ makeGetValue('iov', POINTER_SIZE, '*') }}}; + iov += {{{ POINTER_SIZE }}} * 2; + process.stdout.write(HEAPU8.subarray(ptr, ptr + len)); + num += len; + } + {{{ makeSetValue('pnum', 0, 'num', '*') }}}; + return 0; + }, +}); diff --git a/src/lib/libbrowser.js b/src/lib/libbrowser.js new file mode 100644 index 0000000000000..6d60a4bd9ad55 --- /dev/null +++ b/src/lib/libbrowser.js @@ -0,0 +1,884 @@ +/** + * @license + * Copyright 2011 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +// Utilities for browser environments +var LibraryBrowser = { + $Browser__deps: [ + '$callUserCallback', + '$getFullscreenElement', + '$safeSetTimeout', + '$warnOnce', +#if FILESYSTEM + '$preloadPlugins', +#if MAIN_MODULE + '$preloadedWasm', +#endif +#endif + ], + + $Browser: { + useWebGL: false, + isFullscreen: false, + pointerLock: false, + moduleContextCreatedCallbacks: [], + workers: [], + preloadedImages: {}, + preloadedAudios: {}, + + getCanvas: () => Module['canvas'], + + init() { + if (Browser.initted) return; + Browser.initted = true; + +#if FILESYSTEM + // 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 = () => { +#if ASSERTIONS + assert(img.complete, `Image ${name} could not be decoded`); +#endif + 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); +#endif + + // 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, +#if MIN_WEBGL_VERSION >= 2 + majorVersion: 2, +#elif MAX_WEBGL_VERSION >= 2 // libbrowser.js defaults: use the WebGL version chosen at compile time (unless overridden below) + majorVersion: (typeof WebGL2RenderingContext != 'undefined') ? 2 : 1, +#else + majorVersion: 1, +#endif + }; + + 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) { +#if ASSERTIONS + if (!useWebGL) assert(typeof GLctx == 'undefined', 'cannot set in module if GLctx is used, but we are a non-GL context that would replace it'); +#endif + 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(); + }, + +#if ASSERTIONS + requestFullScreen() { + abort('Module.requestFullScreen has been replaced by Module.requestFullscreen (without a capital S)'); + }, +#endif + + 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; + }, + + // abort and pause-aware versions TODO: build main loop on top of this? + + 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; + }, + + // Browsers specify wheel direction according to the page CSS pixel Y direction: + // Scrolling mouse wheel down (==towards user/away from screen) on Windows/Linux (and macOS without 'natural scroll' enabled) + // is the positive wheel direction. Scrolling mouse wheel up (towards the screen) is the negative wheel direction. + // This function returns the wheel direction in the browser page coordinate system (+: down, -: up). Note that this is often the + // opposite of native code: In native APIs the positive scroll direction is to scroll up (away from the user). + // NOTE: The mouse wheel delta is a decimal number, and can be a fractional value within -1 and 1. If you need to represent + // this as an integer, don't simply cast to int, or you may receive scroll events for wheel delta == 0. + // NOTE: We convert all units returned by events into steps, i.e. individual wheel notches. + // These conversions are only approximations. Changing browsers, operating systems, or even settings can change the values. + 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: {}, + + // Return the mouse coordinates relative to the top, left of the canvas, corrected for scroll offset. + 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); +#if ASSERTIONS + // If this assert lands, it's likely because the browser doesn't support scrollX or pageXOffset + // and we have no viable fallback. + assert((typeof scrollX != 'undefined') && (typeof scrollY != 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.'); +#endif + 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 }; + }, + + // Directly set the Browser state with new mouse coordinates calculated using calculateMouseCoords. + 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; + }, + + // Unpack a "mouse" event, handling SDL touch paths and pointerlock compatibility stuff. + 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 = {{{ makeGetValue('SDL.screen', '0', 'u32') }}}; + flags = flags | 0x00800000; // set SDL_FULLSCREEN flag + {{{ makeSetValue('SDL.screen', '0', 'flags', 'i32') }}}; + } + Browser.updateCanvasDimensions(Browser.getCanvas()); + Browser.updateResizeListeners(); + }, + + setWindowedCanvasSize() { + // check if SDL is available + if (typeof SDL != "undefined") { + var flags = {{{ makeGetValue('SDL.screen', '0', 'u32') }}}; + flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag + {{{ makeSetValue('SDL.screen', '0', 'flags', 'i32') }}}; + } + 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"); + } + } + } + }, + }, + + $requestFullscreen: 'Browser.requestFullscreen', +#if ASSERTIONS + $requestFullScreen: 'Browser.requestFullScreen', +#endif + $setCanvasSize: 'Browser.setCanvasSize', + $getUserMedia: 'Browser.getUserMedia', + $createContext: 'Browser.createContext', + + emscripten_run_preload_plugins__deps: ['$PATH'], + emscripten_run_preload_plugins__proxy: 'sync', + 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) {{{ makeDynCall('vp', 'onload') }}}(file); + }, + () => { + {{{ runtimeKeepalivePop() }}} + if (onerror) {{{ makeDynCall('vp', 'onerror') }}}(file); + }, + true // don'tCreateFile - it's already there + ); + return 0; + }, + + $Browser_asyncPrepareDataCounter: 0, + + emscripten_run_preload_plugins_data__proxy: 'sync', + emscripten_run_preload_plugins_data__deps: ['$stringToNewUTF8', '$Browser_asyncPrepareDataCounter'], + 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, + {{{ makeHEAPView('U8', 'data', 'data + size') }}}, + true, true, + () => { + {{{ runtimeKeepalivePop() }}} + if (onload) {{{ makeDynCall('vpp', 'onload') }}}(arg, cname); + }, + () => { + {{{ runtimeKeepalivePop() }}} + if (onerror) {{{ makeDynCall('vp', 'onerror') }}}(arg); + }, + true // don'tCreateFile - it's already there + ); + }, + + // Callable from pthread, executes in pthread context. + emscripten_async_run_script__deps: ['emscripten_run_script', '$safeSetTimeout'], + emscripten_async_run_script: (script, millis) => { + // TODO: cache these to avoid generating garbage + safeSetTimeout(() => _emscripten_run_script(script), millis); + }, + + // TODO: currently not callable from a pthread, but immediately calls onerror() if not on main thread. + emscripten_async_load_script__deps: ['$UTF8ToString'], + emscripten_async_load_script: async (url, onload, onerror) => { + url = UTF8ToString(url); +#if PTHREADS + if (ENVIRONMENT_IS_PTHREAD) { + err(`emscripten_async_load_script("${url}") failed, emscripten_async_load_script is currently not available in pthreads!`); + onerror && {{{ makeDynCall('v', 'onerror') }}}(); + return; + } +#endif +#if ASSERTIONS + assert(runDependencies === 0, 'async_load_script must be run when no other dependencies are active'); +#endif + {{{ runtimeKeepalivePush() }}} + + var loadDone = () => { + {{{ runtimeKeepalivePop() }}} + if (onload) { + var onloadCallback = () => callUserCallback({{{ makeDynCall('v', 'onload') }}}); + if (runDependencies > 0) { + dependenciesFulfilled = onloadCallback; + } else { + onloadCallback(); + } + } + } + + var loadError = () => { + {{{ runtimeKeepalivePop() }}} + if (onerror) { + callUserCallback({{{ makeDynCall('v', 'onerror') }}}); + } + }; + +#if ENVIRONMENT_MAY_BE_NODE && DYNAMIC_EXECUTION + if (ENVIRONMENT_IS_NODE) { + try { + var data = await readAsync(url, false); + eval(data); + loadDone(); + } catch (e) { + err(e); + loadError(); + } + return; + } +#endif + + var script = document.createElement('script'); + script.onload = loadDone; + script.onerror = loadError; + script.src = url; + document.body.appendChild(script); + }, + + emscripten_get_window_title__proxy: 'sync', + 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_set_window_title__proxy: 'sync', + emscripten_set_window_title: (title) => document.title = UTF8ToString(title), + + emscripten_get_screen_size__proxy: 'sync', + emscripten_get_screen_size: (width, height) => { + {{{ makeSetValue('width', '0', 'screen.width', 'i32') }}}; + {{{ makeSetValue('height', '0', 'screen.height', 'i32') }}}; + }, + + emscripten_hide_mouse__proxy: 'sync', + 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_set_canvas_size__proxy: 'sync', + emscripten_set_canvas_size: (width, height) => Browser.setCanvasSize(width, height), + + emscripten_get_canvas_size__proxy: 'sync', + emscripten_get_canvas_size: (width, height, isFullscreen) => { + var canvas = Browser.getCanvas(); + {{{ makeSetValue('width', '0', 'canvas.width', 'i32') }}}; + {{{ makeSetValue('height', '0', 'canvas.height', 'i32') }}}; + {{{ makeSetValue('isFullscreen', '0', 'Browser.isFullscreen ? 1 : 0', 'i32') }}}; + }, + + // To avoid creating worker parent->child chains, always proxies to execute on the main thread. + emscripten_create_worker__proxy: 'sync', + emscripten_create_worker__deps: ['$UTF8ToString', 'realloc'], + 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_destroy_worker__deps: ['free'], + emscripten_destroy_worker__proxy: 'sync', + emscripten_destroy_worker: (id) => { + var info = Browser.workers[id]; + info.worker.terminate(); + _free(info.buffer); + Browser.workers[id] = null; + }, + + emscripten_call_worker__proxy: 'sync', + 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: {{{ makeDynCall('vpip', 'callback') }}}, + arg + }); + info.awaited++; + } + var transferObject = { + 'funcName': funcName, + 'callbackId': callbackId, + 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 + }; + if (data) { + info.worker.postMessage(transferObject, [transferObject.data.buffer]); + } else { + info.worker.postMessage(transferObject); + } + }, + +#if BUILD_AS_WORKER + emscripten_worker_respond_provisionally__proxy: 'sync', + emscripten_worker_respond_provisionally: (data, size) => { + if (workerResponded) abort('already responded with final response!'); + var transferObject = { + 'callbackId': workerCallbackId, + 'finalResponse': false, + 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 + }; + if (data) { + postMessage(transferObject, [transferObject.data.buffer]); + } else { + postMessage(transferObject); + } + }, + + emscripten_worker_respond__proxy: 'sync', + emscripten_worker_respond: (data, size) => { + if (workerResponded) abort('already responded with final response!'); + workerResponded = true; + var transferObject = { + 'callbackId': workerCallbackId, + 'finalResponse': true, + 'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 + }; + if (data) { + postMessage(transferObject, [transferObject.data.buffer]); + } else { + postMessage(transferObject); + } + }, +#endif + + emscripten_get_worker_queue_size__proxy: 'sync', + emscripten_get_worker_queue_size: (id) => { + var info = Browser.workers[id]; + if (!info) return -1; + return info.awaited; + }, + + emscripten_get_preloaded_image_data__deps: ['$getPreloadedImageData', '$UTF8ToString'], + emscripten_get_preloaded_image_data__proxy: 'sync', + emscripten_get_preloaded_image_data: (path, w, h) => getPreloadedImageData(UTF8ToString(path), w, h), + + $getPreloadedImageData__internal: true, + $getPreloadedImageData__data: ['$PATH_FS', 'malloc'], + $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); + + {{{ makeSetValue('w', '0', 'canvas.width', 'i32') }}}; + {{{ makeSetValue('h', '0', 'canvas.height', 'i32') }}}; + return buf; + }, + +#if !WASMFS // WasmFS implements this in wasm + emscripten_get_preloaded_image_data_from_FILE__deps: ['$getPreloadedImageData', 'fileno'], + emscripten_get_preloaded_image_data_from_FILE__proxy: 'sync', + 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; + } +#endif +}; + +autoAddDeps(LibraryBrowser, '$Browser'); + +addToLibrary(LibraryBrowser); diff --git a/src/lib/libc_preprocessor.js b/src/lib/libc_preprocessor.js new file mode 100644 index 0000000000000..9763b58d9e67c --- /dev/null +++ b/src/lib/libc_preprocessor.js @@ -0,0 +1,293 @@ +addToLibrary({ + // Removes all C++ '//' and '/* */' comments from the given source string. + // N.b. will also eat comments inside strings. + $remove_cpp_comments_in_shaders: (code) => { + var i = 0, out = '', ch, next, len = code.length; + for (; i < len; ++i) { + ch = code[i]; + if (ch == '/') { + next = code[i+1]; + if (next == '/') { + while (i < len && code[i+1] != '\n') ++i; + } else if (next == '*') { + while (i < len && (code[i-1] != '*' || code[i] != '/')) ++i; + } else { + out += ch; + } + } else { + out += ch; + } + } + return out; + }, + + // Finds the index of closing parens from the opening parens at arr[i]. + // Used polymorphically for strings ("foo") and token arrays (['(', 'foo', ')']) as input. + $find_closing_parens_index: (arr, i, opening='(', closing=')') => { + for (var nesting = 0; i < arr.length; ++i) { + if (arr[i] == opening) ++nesting; + if (arr[i] == closing && --nesting == 0) { + return i; + } + } + }, + + // Runs C preprocessor algorithm on the given string 'code'. + // Supported preprocessor directives: #if, #ifdef, #ifndef, #else, #elif, #endif, #define and #undef. + // predefs: Specifies a dictionary of { 'key1': function(arg0, arg1) {...}, 'key2': ... } of predefined preprocessing variables + $preprocess_c_code__deps: ['$find_closing_parens_index'], + $preprocess_c_code: function(code, defs = {}) { + var i = 0, // iterator over the input string + len = code.length, // cache input length + out = '', // generates the preprocessed output string + stack = [1]; // preprocessing stack (state of active/inactive #ifdef/#else blocks we are currently inside) + // a mapping 'symbolname' -> function(args) which evaluates the given cpp macro, e.g. #define FOO(x) x+10. + defs['defined'] = (args) => { // built-in "#if defined(x)"" macro. +#if ASSERTIONS + assert(args.length == 1); + assert(/^[A-Za-z0-9_$]+$/.test(args[0].trim())); // Test that a C preprocessor identifier contains only valid characters (we likely parsed wrong if this fails) +#endif + return defs[args[0].trim()] ? 1 : 0; + }; + + // Returns true if str[i] is whitespace. + function isWhitespace(str, i) { + return !(str.charCodeAt(i) > 32); // Compare as negation to treat end-of-string undefined as whitespace + } + + // Returns index to the next whitespace character starting at str[i]. + function nextWhitespace(str, i) { + while (!isWhitespace(str, i)) ++i; + return i; + } + + // Returns an integer ID classification of the character at str[idx], used for tokenization purposes. + function classifyChar(str, idx) { + var cc = str.charCodeAt(idx); + #if ASSERTIONS + assert(!(cc > 127), "Only 7-bit ASCII can be used in preprocessor #if/#ifdef/#define statements!"); + #endif + if (cc > 32) { + if (cc < 48) return 1; // an operator symbol, any of !"#$%&'()*+,-./ + if (cc < 58) return 2; // a number 0123456789 + if (cc < 65) return 1; // an operator symbol, any of :;<=>?@ + if (cc < 91 || cc == 95/*_*/) return 3; // a character, any of A-Z or _ + if (cc < 97) return 1; // an operator symbol, any of [\]^` + if (cc < 123) return 3; // a character, any of a-z + return 1; // an operator symbol, any of {|}~ + } + return cc < 33 ? 0 : 4; // 0=whitespace, 4=end-of-string + } + + // Returns a tokenized array of the given string expression, i.e. "FOO > BAR && BAZ" -> ["FOO", ">", "BAR", "&&", "BAZ"] + // Optionally keeps whitespace as tokens to be able to reconstruct the original input string. + function tokenize(exprString, keepWhitespace) { + var out = [], len = exprString.length; + for (var i = 0; i <= len; ++i) { + var kind = classifyChar(exprString, i); + if (kind == 2/*0-9*/ || kind == 3/*a-z*/) { // a character or a number + for (var j = i+1; j <= len; ++j) { + var kind2 = classifyChar(exprString, j); + if (kind2 != kind && (kind2 != 2/*0-9*/ || kind != 3/*a-z*/)) { // parse number sequence "423410", and identifier sequence "FOO32BAR" + out.push(exprString.substring(i, j)); + i = j-1; + break; + } + } + } else if (kind == 1/*operator symbol*/) { + // Lookahead for two-character operators. + var op2 = exprString.slice(i, i + 2); + if (['<=', '>=', '==', '!=', '&&', '||'].includes(op2)) { + out.push(op2); + ++i; + } else { + out.push(exprString[i]); + } + } + } + return out; + } + + // Expands preprocessing macros on substring str[lineStart...lineEnd] + function expandMacros(str, lineStart, lineEnd) { + if (lineEnd === undefined) lineEnd = str.length; + var len = str.length; + var out = ''; + for (var i = lineStart; i < lineEnd; ++i) { + var kind = classifyChar(str, i); + if (kind == 3/*a-z*/) { + for (var j = i + 1; j <= lineEnd; ++j) { + var kind2 = classifyChar(str, j); + if (kind2 != 2/*0-9*/ && kind2 != 3/*a-z*/) { + var symbol = str.substring(i, j); + var pp = defs[symbol]; + if (pp) { + var expanded = str.substring(lineStart, i); + if (pp.length) { // Expanding a macro? (#define FOO(X) ...) + while (isWhitespace(str, j)) ++j; + if (str[j] == '(') { + var closeParens = find_closing_parens_index(str, j); + // N.b. this has a limitation that multiparameter macros cannot nest with other multiparameter macros + // e.g. FOO(a, BAR(b, c)) is not supported. + expanded += pp(str.substring(j+1, closeParens).split(',')) + str.substring(closeParens+1, lineEnd); + } else { + var j2 = nextWhitespace(str, j); + expanded += pp([str.substring(j, j2)]) + str.substring(j2, lineEnd); + } + } else { // Expanding a non-macro (#define FOO BAR) + expanded += pp() + str.substring(j, lineEnd); + } + return expandMacros(expanded, 0); + } + out += symbol; + i = j-1; + break; + } + } + } else { + out += str[i]; + } + } + return out; + } + + // Given a token list e.g. ['2', '>', '1'], returns a function that evaluates that token list. + function buildExprTree(tokens) { + // Consume tokens array into a function tree until the tokens array is exhausted + // to a single root node that evaluates it. + while (tokens.length > 1 || typeof tokens[0] != 'function') { + tokens = ((tokens) => { + // Find the index 'i' of the operator we should evaluate next: + var i, j, p, operatorAndPriority = -2; + for (j = 0; j < tokens.length; ++j) { + if ((p = ['*', '/', '+', '-', '!', '<', '<=', '>', '>=', '==', '!=', '&&', '||', '('].indexOf(tokens[j])) > operatorAndPriority) { + i = j; + operatorAndPriority = p; + } + } + + if (operatorAndPriority == 13 /* parens '(' */) { + // Find the closing parens position + var j = find_closing_parens_index(tokens, i); + if (j) { + tokens.splice(i, j+1-i, buildExprTree(tokens.slice(i+1, j))); + return tokens; + } + } + + if (operatorAndPriority == 4 /* unary ! */) { + // Special case: the unary operator ! needs to evaluate right-to-left. + i = tokens.lastIndexOf('!'); + var innerExpr = buildExprTree(tokens.slice(i+1, i+2)); + tokens.splice(i, 2, function() { return !innerExpr(); }) + return tokens; + } + + // A binary operator: + if (operatorAndPriority >= 0) { + var left = buildExprTree(tokens.slice(0, i)); + var right = buildExprTree(tokens.slice(i+1)); + switch(tokens[i]) { + case '&&': return [function() { return left() && right(); }]; + case '||': return [function() { return left() || right(); }]; + case '==': return [function() { return left() == right(); }]; + case '!=': return [function() { return left() != right(); }]; + case '<' : return [function() { return left() < right(); }]; + case '<=': return [function() { return left() <= right(); }]; + case '>' : return [function() { return left() > right(); }]; + case '>=': return [function() { return left() >= right(); }]; + case '+': return [function() { return left() + right(); }]; + case '-': return [function() { return left() - right(); }]; + case '*': return [function() { return left() * right(); }]; + case '/': return [function() { return Math.floor(left() / right()); }]; + } + } + // else a number: +#if ASSERTIONS + assert(tokens[i] !== ')', 'Parsing failure, mismatched parentheses in parsing!' + tokens.toString()); + assert(operatorAndPriority == -1); +#endif + var num = Number(tokens[i]); + return [function() { return num; }] + })(tokens); + } + return tokens[0]; + } + + // Preprocess the input one line at a time. + for (; i < len; ++i) { + // Find the start of the current line. + var lineStart = i; + + // Seek iterator to end of current line. + i = code.indexOf('\n', i); + if (i < 0) i = len; + + // Find the first non-whitespace character on the line. + for (var j = lineStart; j < i && isWhitespace(code, j); ++j); + + // Is this a non-preprocessor directive line? + var thisLineIsInActivePreprocessingBlock = stack[stack.length-1]; + if (code[j] != '#') { // non-preprocessor line? + if (thisLineIsInActivePreprocessingBlock) { + out += expandMacros(code, lineStart, i) + '\n'; + } + continue; + } + // This is a preprocessor directive line, e.g. #ifdef or #define. + + // Parse the line as # + var space = nextWhitespace(code, j); + var directive = code.substring(j+1, space); + var expression = code.substring(space, i).trim(); + switch(directive) { + case 'if': + var tokens = tokenize(expandMacros(expression, 0)); + var exprTree = buildExprTree(tokens); + var evaluated = exprTree(); + stack.push(!!evaluated * stack[stack.length-1]); + break; + case 'ifdef': stack.push(!!defs[expression] * stack[stack.length-1]); break; + case 'ifndef': stack.push(!defs[expression] * stack[stack.length-1]); break; + case 'else': stack[stack.length-1] = (1-stack[stack.length-1]) * stack[stack.length-2]; break; + case 'endif': stack.pop(); break; + case 'define': + if (thisLineIsInActivePreprocessingBlock) { + // This could either be a macro with input args (#define MACRO(x,y) x+y), or a direct expansion #define FOO 2, + // figure out which. + var macroStart = expression.indexOf('('); + var firstWs = nextWhitespace(expression, 0); + if (firstWs < macroStart) macroStart = 0; + if (macroStart > 0) { // #define MACRO( x , y , z ) + var macroEnd = expression.indexOf(')', macroStart); + let params = expression.substring(macroStart+1, macroEnd).split(',').map(x => x.trim()); + let value = tokenize(expression.substring(macroEnd+1).trim()) + defs[expression.substring(0, macroStart)] = (args) => { + var ret = ''; + value.forEach((x) => { + var argIndex = params.indexOf(x); + ret += (argIndex >= 0) ? args[argIndex] : x; + }); + return ret; + }; + } else { // #define FOO (x + y + z) + let value = expandMacros(expression.substring(firstWs+1).trim(), 0); + defs[expression.substring(0, firstWs)] = () => value; + } + } + break; + case 'undef': if (thisLineIsInActivePreprocessingBlock) delete defs[expression]; break; + default: + if (directive != 'version' && directive != 'pragma' && directive != 'extension' && directive != 'line') { // GLSL shader compiler specific #directives. +#if ASSERTIONS + err('Unrecognized preprocessor directive #' + directive + '!'); +#endif + } + + // Unknown preprocessor macro, just pass through the line to output. + out += expandMacros(code, lineStart, i) + '\n'; + } + } + return out; + } +}); diff --git a/src/lib/libccall.js b/src/lib/libccall.js new file mode 100644 index 0000000000000..e97ef2926beba --- /dev/null +++ b/src/lib/libccall.js @@ -0,0 +1,154 @@ +/** + * @license + * Copyright 2022 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + // Returns the C function with a specified identifier (for C++, you need to do manual name mangling) +#if MODULARIZE == 'instance' && !INCLUDE_FULL_LIBRARY + $getCFunc__deps: [() => error('ccall is not yet compatible with MODULARIZE=instance')], +#endif + $getCFunc__internal: true, + $getCFunc: (ident) => { + var func = Module['_' + ident]; // closure exported function +#if ASSERTIONS + assert(func, 'Cannot call unknown function ' + ident + ', make sure it is exported'); +#endif + return func; + }, + + // C calling interface. + $ccall__deps: ['$getCFunc', '$writeArrayToMemory', '$stringToUTF8OnStack', '$stackSave', '$stackRestore', '$stackAlloc'], + $ccall__docs: ` + /** + * @param {string|null=} returnType + * @param {Array=} argTypes + * @param {Array=} args + * @param {Object=} opts + */`, + $ccall: (ident, returnType, argTypes, args, opts) => { + // For fast lookup of conversion functions + var toC = { +#if MEMORY64 + 'pointer': (p) => {{{ to64('p') }}}, +#endif + 'string': (str) => { + var ret = 0; + if (str !== null && str !== undefined && str !== 0) { // null string + ret = stringToUTF8OnStack(str); + } + return {{{ to64('ret') }}}; + }, + 'array': (arr) => { + var ret = stackAlloc(arr.length); + writeArrayToMemory(arr, ret); + return {{{ to64('ret') }}}; + } + }; + + function convertReturnValue(ret) { + if (returnType === 'string') { + return UTF8ToString({{{ from64Expr('ret') }}}); + } +#if MEMORY64 + if (returnType === 'pointer') return Number(ret); +#endif + if (returnType === 'boolean') return Boolean(ret); + return ret; + } + + var func = getCFunc(ident); + var cArgs = []; + var stack = 0; +#if ASSERTIONS + assert(returnType !== 'array', 'Return type should not be "array".'); +#endif + 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]; + } + } + } +#if ASYNCIFY == 1 + // Data for a previous async operation that was in flight before us. + var previousAsync = Asyncify.currData; +#endif + var ret = func(...cArgs); + function onDone(ret) { +#if ASYNCIFY == 1 + runtimeKeepalivePop(); +#endif + if (stack !== 0) stackRestore(stack); + return convertReturnValue(ret); + } +#if ASYNCIFY + var asyncMode = opts?.async; +#endif + +#if ASYNCIFY == 1 + // 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) { +#if ASSERTIONS + // A change in async operation happened. If there was already an async + // operation in flight before us, that is an error: we should not start + // another async operation while one is active, and we should not stop one + // either. The only valid combination is to have no change in the async + // data (so we either had one in flight and left it alone, or we didn't have + // one), or to have nothing in flight and to start one. + assert(!(previousAsync && Asyncify.currData), 'We cannot start an async operation when one is already flight'); + assert(!(previousAsync && !Asyncify.currData), 'We cannot stop an async operation in flight'); +#endif + // 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. +#if ASSERTIONS + assert(asyncMode, 'The call to ' + ident + ' is running asynchronously. If this was intended, add the async option to the ccall/cwrap call.'); +#endif + return Asyncify.whenDone().then(onDone); + } +#endif + +#if ASYNCIFY == 2 + if (asyncMode) return ret.then(onDone); +#endif + + ret = onDone(ret); +#if ASYNCIFY == 1 + // If this is an async ccall, ensure we return a promise + if (asyncMode) return Promise.resolve(ret); +#endif + return ret; + }, + + $cwrap__docs: ` + /** + * @param {string=} returnType + * @param {Array=} argTypes + * @param {Object=} opts + */`, + $cwrap__deps: [ '$ccall', +#if !ASSERTIONS + '$getCFunc', +#endif + ], + $cwrap: (ident, returnType, argTypes, opts) => { +#if !ASSERTIONS + // 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); + } +#endif + return (...args) => ccall(ident, returnType, argTypes, args, opts); + }, +}); diff --git a/src/lib/libcore.js b/src/lib/libcore.js new file mode 100644 index 0000000000000..ef8c6d9893819 --- /dev/null +++ b/src/lib/libcore.js @@ -0,0 +1,2644 @@ +/** + * @license + * Copyright 2010 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +//"use strict"; + +// An implementation of basic necessary libraries for the web. This integrates +// with a compiled libc and with the rest of the JS runtime. +// +// We search the Library object when there is an external function. If the +// entry in the Library is a function, we insert it. If it is a string, we +// do another lookup in the library (a simple way to write a function once, +// if it can be called by different names). We also allow dependencies, +// using __deps. Initialization code to be run after allocating all +// global constants can be defined by __postset. +// +// Note that the full function name will be '_' + the name in the Library +// object. For convenience, the short name appears here. Note that if you add a +// new function with an '_', it will not be found. + +addToLibrary({ + // JS aliases for native stack manipulation functions and tempret handling + $stackSave__deps: ['emscripten_stack_get_current'], + $stackSave: () => _emscripten_stack_get_current(), + $stackRestore__deps: ['_emscripten_stack_restore'], + $stackRestore: (val) => __emscripten_stack_restore(val), + $stackAlloc__deps: ['_emscripten_stack_alloc'], + $stackAlloc: (sz) => __emscripten_stack_alloc(sz), + $getTempRet0__deps: ['_emscripten_tempret_get'], + $getTempRet0: (val) => __emscripten_tempret_get(), + $setTempRet0__deps: ['_emscripten_tempret_set'], + $setTempRet0: (val) => __emscripten_tempret_set(val), + + // Aliases that allow legacy names (without leading $) for the + // functions to continue to work in `__deps` entries. + stackAlloc: '$stackAlloc', + stackSave: '$stackSave', + stackRestore: '$stackSave', + setTempRet0: '$setTempRet0', + getTempRet0: '$getTempRet0', + + // Assign a name to a given function. This is mostly useful for debugging + // purposes in cases where new functions are created at runtime. + $createNamedFunction: (name, func) => Object.defineProperty(func, 'name', { value: name }), + + $ptrToString: (ptr) => { +#if ASSERTIONS + assert(typeof ptr === 'number', `ptrToString expects a number, got ${typeof ptr}`); +#endif +#if MEMORY64 + // Convert to 64-bit unsigned value. We need to use BigInt here since + // Number cannot represent the full 64-bit range. + if (ptr < 0) ptr = 2n**64n + BigInt(ptr); +#else + // Convert to 32-bit unsigned value + ptr >>>= 0; +#endif + return '0x' + ptr.toString(16).padStart({{{ POINTER_SIZE * 2 }}}, '0'); + }, + + $zeroMemory: (ptr, size) => HEAPU8.fill(0, ptr, ptr + size), + +#if SAFE_HEAP + // Trivial wrappers around runtime functions that make these symbols available + // to native code. + segfault: '=segfault', + alignfault: '=alignfault', +#endif + + // ========================================================================== + // JavaScript <-> C string interop + // ========================================================================== + +#if !MINIMAL_RUNTIME + $exitJS__docs: '/** @param {boolean|number=} implicit */', + $exitJS__deps: [ + 'proc_exit', +#if ASSERTIONS || EXIT_RUNTIME + '$keepRuntimeAlive', +#endif +#if PTHREADS + '$exitOnMainThread', +#endif +#if PTHREADS_DEBUG || ASSERTIONS + '$runtimeKeepaliveCounter', +#endif + ], + $exitJS: (status, implicit) => { + EXITSTATUS = status; + +#if ASSERTIONS && !EXIT_RUNTIME + checkUnflushedContent(); +#endif // ASSERTIONS && !EXIT_RUNTIME + +#if PTHREADS + if (ENVIRONMENT_IS_PTHREAD) { + // implicit exit can never happen on a pthread +#if ASSERTIONS + assert(!implicit); +#endif +#if PTHREADS_DEBUG + dbg(`Pthread ${ptrToString(_pthread_self())} called exit(${status}), posting exitOnMainThread.`); +#endif + // When running in a pthread we propagate the exit back to the main thread + // where it can decide if the whole process should be shut down or not. + // The pthread may have decided not to exit its own runtime, for example + // because it runs a main loop, but that doesn't affect the main thread. + exitOnMainThread(status); + throw 'unwind'; + } +#if PTHREADS_DEBUG + err(`main thread called exit(${status}): keepRuntimeAlive=${keepRuntimeAlive()} (counter=${runtimeKeepaliveCounter})`); +#endif // PTHREADS_DEBUG +#endif // PTHREADS + +#if EXIT_RUNTIME + if (!keepRuntimeAlive()) { + exitRuntime(); + } +#endif + +#if ASSERTIONS + // if exit() was called explicitly, warn the user if the runtime isn't actually being shut down + if (keepRuntimeAlive() && !implicit) { + var msg = `program exited (with status: ${status}), but keepRuntimeAlive() is set (counter=${runtimeKeepaliveCounter}) due to an async operation, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)`; +#if MODULARIZE + readyPromiseReject?.(msg); +#endif // MODULARIZE + err(msg); + } +#endif // ASSERTIONS + + _proc_exit(status); + }, +#endif + +#if MINIMAL_RUNTIME + // minimal runtime doesn't do any exit cleanup handling so just + // map exit directly to the lower-level proc_exit syscall. + exit: 'proc_exit', +#else + exit: '$exitJS', +#endif + + // Returns a pointer ('p'), which means an i32 on wasm32 and an i64 wasm64 + // We have a separate JS version `getHeapMax()` which can be called directly + // avoiding any wrapper added for wasm64. + emscripten_get_heap_max__deps: ['$getHeapMax'], + emscripten_get_heap_max: () => getHeapMax(), + + $getHeapMax: () => +#if ALLOW_MEMORY_GROWTH +#if MEMORY64 == 1 + {{{ MAXIMUM_MEMORY }}}, +#else + // 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. + {{{ Math.min(MAXIMUM_MEMORY, FOUR_GB - WASM_PAGE_SIZE) }}}, +#endif +#else // no growth + HEAPU8.length, +#endif + +#if ABORTING_MALLOC + $abortOnCannotGrowMemory: (requestedSize) => { +#if ASSERTIONS +#if ALLOW_MEMORY_GROWTH + abort(`Cannot enlarge memory arrays to size ${requestedSize} bytes (OOM). If you want malloc to return NULL (0) instead of this abort, do not link with -sABORTING_MALLOC (that is, the default when growth is enabled is to not abort, but you have overridden that)`); +#else // ALLOW_MEMORY_GROWTH + abort(`Cannot enlarge memory arrays to size ${requestedSize} bytes (OOM). Either (1) compile with -sINITIAL_MEMORY=X with X higher than the current value ${HEAP8.length}, (2) compile with -sALLOW_MEMORY_GROWTH which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -sABORTING_MALLOC=0`); +#endif // ALLOW_MEMORY_GROWTH +#else // ASSERTIONS + abort('OOM'); +#endif // ASSERTIONS + }, +#endif // ABORTING_MALLOC + + // Grows the wasm memory to the given byte size, and updates the JS views to + // it. Returns 1 on success, 0 on error. + $growMemory: (size) => { + var oldHeapSize = wasmMemory.buffer.byteLength; + var pages = ((size - oldHeapSize + {{{ WASM_PAGE_SIZE - 1 }}}) / {{{ WASM_PAGE_SIZE }}}) | 0; +#if RUNTIME_DEBUG + dbg(`growMemory: ${size} (+${size - oldHeapSize} bytes / ${pages} pages)`); +#endif + try { + // round size grow request up to wasm page size (fixed 64KB per spec) + wasmMemory.grow({{{ toIndexType('pages') }}}); // .grow() takes a delta compared to the previous size +#if !GROWABLE_ARRAYBUFFERS + updateMemoryViews(); +#endif +#if MEMORYPROFILER + if (typeof emscriptenMemoryProfiler != 'undefined') { + emscriptenMemoryProfiler.onMemoryResize(oldHeapSize, wasmMemory.buffer.byteLength); + } +#endif + return 1 /*success*/; + } catch(e) { +#if ASSERTIONS + err(`growMemory: Attempted to grow heap from ${oldHeapSize} bytes to ${size} bytes, but got error: ${e}`); +#endif + } + // implicit 0 return to save code size (caller will cast "undefined" into 0 + // anyhow) + }, + + emscripten_resize_heap__deps: [ +#if ABORTING_MALLOC + '$abortOnCannotGrowMemory', +#endif +#if ALLOW_MEMORY_GROWTH +#if ASSERTIONS == 2 + 'emscripten_get_now', +#endif + '$getHeapMax', + '$alignMemory', + '$growMemory', +#endif + ], + emscripten_resize_heap: 'ip', + emscripten_resize_heap: (requestedSize) => { + var oldSize = HEAPU8.length; +#if !MEMORY64 && !CAN_ADDRESS_2GB + // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. + requestedSize >>>= 0; +#endif +#if ALLOW_MEMORY_GROWTH == 0 +#if ABORTING_MALLOC + abortOnCannotGrowMemory(requestedSize); +#else + return false; // malloc will report failure +#endif // ABORTING_MALLOC +#else // ALLOW_MEMORY_GROWTH == 0 + // With multithreaded builds, races can happen (another thread might increase the size + // in between), so return a failure, and let the caller retry. +#if SHARED_MEMORY + if (requestedSize <= oldSize) { + return false; + } +#elif ASSERTIONS + assert(requestedSize > oldSize); +#endif + +#if EMSCRIPTEN_TRACING + // Report old layout one last time + _emscripten_trace_report_memory_layout(); +#endif + + // 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) { +#if ASSERTIONS + err(`Cannot enlarge memory, requested ${requestedSize} bytes, but the limit is ${maxHeapSize} bytes!`); +#endif +#if ABORTING_MALLOC + abortOnCannotGrowMemory(requestedSize); +#else + return false; +#endif + } + + // 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) { +#if MEMORY_GROWTH_LINEAR_STEP == -1 + var overGrownHeapSize = oldSize * (1 + {{{ MEMORY_GROWTH_GEOMETRIC_STEP }}} / cutDown); // ensure geometric growth +#if MEMORY_GROWTH_GEOMETRIC_CAP + // but limit overreserving (default to capping at +96MB overgrowth at most) + overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + {{{ MEMORY_GROWTH_GEOMETRIC_CAP }}} ); +#endif + +#else + var overGrownHeapSize = oldSize + {{{ MEMORY_GROWTH_LINEAR_STEP }}} / cutDown; // ensure linear growth +#endif + + var newSize = Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), {{{ WASM_PAGE_SIZE }}})); + +#if ASSERTIONS == 2 + var t0 = _emscripten_get_now(); +#endif + var replacement = growMemory(newSize); +#if ASSERTIONS == 2 + var t1 = _emscripten_get_now(); + dbg(`Heap resize call from ${oldSize} to ${newSize} took ${(t1 - t0)} msecs. Success: ${!!replacement}`); +#endif + if (replacement) { +#if ASSERTIONS && WASM2JS + err('Warning: Enlarging memory arrays, this is not fast! ' + [oldSize, newSize]); +#endif + +#if EMSCRIPTEN_TRACING + traceLogMessage("Emscripten", `Enlarging memory arrays from ${oldSize} to ${newSize}`); + // And now report the new layout + _emscripten_trace_report_memory_layout(); +#endif + return true; + } + } +#if ASSERTIONS + err(`Failed to grow the heap from ${oldSize} bytes to ${newSize} bytes, not enough memory!`); +#endif +#if ABORTING_MALLOC + abortOnCannotGrowMemory(requestedSize); +#else + return false; +#endif +#endif // ALLOW_MEMORY_GROWTH + }, + +#if !GROWABLE_ARRAYBUFFERS + // Called after wasm grows memory. At that time we need to update the views. + // Without this notification, we'd need to check the buffer in JS every time + // we return from any wasm, which adds overhead. See + // https://github.com/WebAssembly/WASI/issues/82 + emscripten_notify_memory_growth: (memoryIndex) => { +#if ASSERTIONS + assert(memoryIndex == 0); +#endif + updateMemoryViews(); + }, +#endif + + _emscripten_system: (command) => { +#if ENVIRONMENT_MAY_BE_NODE + 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 {{{ cDefs.SIGHUP }}}; + case 'SIGQUIT': return {{{ cDefs.SIGQUIT }}}; + case 'SIGFPE': return {{{ cDefs.SIGFPE }}}; + case 'SIGKILL': return {{{ cDefs.SIGKILL }}}; + case 'SIGALRM': return {{{ cDefs.SIGALRM }}}; + case 'SIGTERM': return {{{ cDefs.SIGTERM }}}; + default: return {{{ cDefs.SIGINT }}}; + } + } + return _W_EXITCODE(0, signalToNumber(ret.signal)); + } + + return _W_EXITCODE(ret.status, 0); + } +#endif // ENVIRONMENT_MAY_BE_NODE + // 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 -{{{ cDefs.ENOSYS }}}; + }, + + // ========================================================================== + // stdlib.h + // ========================================================================== + +#if !STANDALONE_WASM + // Used to implement the native `abort` symbol. Note that we use the + // JavaScript `abort` helper in order to implement this function, but we use a + // distinct name here to avoid confusing the two. + _abort_js: () => +#if ASSERTIONS + abort('native code called abort()'), +#else + abort(''), +#endif +#endif + + // This object can be modified by the user during startup, which affects + // the initial values of the environment accessible by getenv. + $ENV: {}, + +#if !STANDALONE_WASM + // ========================================================================== + // assert.h + // ========================================================================== + + __assert_fail: (condition, filename, line, func) => + abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']), +#endif + +#if STACK_OVERFLOW_CHECK >= 2 + // Set stack limits used by binaryen's `StackCheck` pass. +#if MAIN_MODULE + $setStackLimits__deps: ['$setDylinkStackLimits'], +#endif + $setStackLimits: () => { + var stackLow = _emscripten_stack_get_base(); + var stackHigh = _emscripten_stack_get_end(); +#if RUNTIME_DEBUG + dbg(`setStackLimits: ${ptrToString(stackLow)}, ${ptrToString(stackHigh)}`); +#endif +#if MAIN_MODULE + // With dynamic linking we could have any number of pre-loaded libraries + // that each need to have their stack limits set. + setDylinkStackLimits(stackLow, stackHigh); +#else + ___set_stack_limits(stackLow, stackHigh); +#endif + }, +#endif + + $withStackSave__deps: ['$stackSave', '$stackRestore'], + $withStackSave: (f) => { + var stack = stackSave(); + var ret = f(); + stackRestore(stack); + return ret; + }, + + // ========================================================================== + // setjmp.h + // ========================================================================== + +#if SUPPORT_LONGJMP == 'emscripten' + // In WebAssemblyLowerEmscriptenEHSjLj pass in the LLVM backend, function + // calls that exist in the same function with setjmp are converted to a code + // sequence that includes invokes, malloc, free, saveSetjmp, and + // emscripten_longjmp. setThrew is called from invokes, but we don't have + // any way to express that dependency so we use emscripten_throw_longjmp as + // a proxy and declare the dependency here. + _emscripten_throw_longjmp__deps: ['setThrew'], + _emscripten_throw_longjmp: () => { +#if EXCEPTION_STACK_TRACES + throw new EmscriptenSjLj; +#else + throw Infinity; +#endif + }, +#elif !SUPPORT_LONGJMP +#if !INCLUDE_FULL_LIBRARY + // These are in order to print helpful error messages when either longjmp of + // setjmp is used. + longjmp__deps: [() => { + error('longjmp support was disabled (SUPPORT_LONGJMP=0), but it is required by the code (either set SUPPORT_LONGJMP=1, or remove uses of it in the project)'); + }], + get setjmp__deps() { + return this.longjmp__deps; + }, + // This is to print the correct error message when a program is built with + // SUPPORT_LONGJMP=1 but linked with SUPPORT_LONGJMP=0. When a program is + // built with SUPPORT_LONGJMP=1, the object file contains references of not + // longjmp but _emscripten_throw_longjmp, which is called from + // emscripten_longjmp. + get _emscripten_throw_longjmp__deps() { + return this.longjmp__deps; + }, +#endif + _emscripten_throw_longjmp: () => { + error('longjmp support was disabled (SUPPORT_LONGJMP=0), but it is required by the code (either set SUPPORT_LONGJMP=1, or remove uses of it in the project)'); + }, + // will never be emitted, as the dep errors at compile time + longjmp: (env, value) => { + abort('longjmp not supported (build with -s SUPPORT_LONGJMP)'); + }, + setjmp: (env) => { + abort('setjmp not supported (build with -s SUPPORT_LONGJMP)'); + }, +#endif + + // ========================================================================== + // errno.h + // ========================================================================== + + // We use a string literal here to avoid the string quotes on the object + // keys being removed when processed by jsifier. + $ERRNO_CODES: `{ + 'EPERM': {{{ cDefs.EPERM }}}, + 'ENOENT': {{{ cDefs.ENOENT }}}, + 'ESRCH': {{{ cDefs.ESRCH }}}, + 'EINTR': {{{ cDefs.EINTR }}}, + 'EIO': {{{ cDefs.EIO }}}, + 'ENXIO': {{{ cDefs.ENXIO }}}, + 'E2BIG': {{{ cDefs.E2BIG }}}, + 'ENOEXEC': {{{ cDefs.ENOEXEC }}}, + 'EBADF': {{{ cDefs.EBADF }}}, + 'ECHILD': {{{ cDefs.ECHILD }}}, + 'EAGAIN': {{{ cDefs.EAGAIN }}}, + 'EWOULDBLOCK': {{{ cDefs.EWOULDBLOCK }}}, + 'ENOMEM': {{{ cDefs.ENOMEM }}}, + 'EACCES': {{{ cDefs.EACCES }}}, + 'EFAULT': {{{ cDefs.EFAULT }}}, + 'ENOTBLK': {{{ cDefs.ENOTBLK }}}, + 'EBUSY': {{{ cDefs.EBUSY }}}, + 'EEXIST': {{{ cDefs.EEXIST }}}, + 'EXDEV': {{{ cDefs.EXDEV }}}, + 'ENODEV': {{{ cDefs.ENODEV }}}, + 'ENOTDIR': {{{ cDefs.ENOTDIR }}}, + 'EISDIR': {{{ cDefs.EISDIR }}}, + 'EINVAL': {{{ cDefs.EINVAL }}}, + 'ENFILE': {{{ cDefs.ENFILE }}}, + 'EMFILE': {{{ cDefs.EMFILE }}}, + 'ENOTTY': {{{ cDefs.ENOTTY }}}, + 'ETXTBSY': {{{ cDefs.ETXTBSY }}}, + 'EFBIG': {{{ cDefs.EFBIG }}}, + 'ENOSPC': {{{ cDefs.ENOSPC }}}, + 'ESPIPE': {{{ cDefs.ESPIPE }}}, + 'EROFS': {{{ cDefs.EROFS }}}, + 'EMLINK': {{{ cDefs.EMLINK }}}, + 'EPIPE': {{{ cDefs.EPIPE }}}, + 'EDOM': {{{ cDefs.EDOM }}}, + 'ERANGE': {{{ cDefs.ERANGE }}}, + 'ENOMSG': {{{ cDefs.ENOMSG }}}, + 'EIDRM': {{{ cDefs.EIDRM }}}, + 'ECHRNG': {{{ cDefs.ECHRNG }}}, + 'EL2NSYNC': {{{ cDefs.EL2NSYNC }}}, + 'EL3HLT': {{{ cDefs.EL3HLT }}}, + 'EL3RST': {{{ cDefs.EL3RST }}}, + 'ELNRNG': {{{ cDefs.ELNRNG }}}, + 'EUNATCH': {{{ cDefs.EUNATCH }}}, + 'ENOCSI': {{{ cDefs.ENOCSI }}}, + 'EL2HLT': {{{ cDefs.EL2HLT }}}, + 'EDEADLK': {{{ cDefs.EDEADLK }}}, + 'ENOLCK': {{{ cDefs.ENOLCK }}}, + 'EBADE': {{{ cDefs.EBADE }}}, + 'EBADR': {{{ cDefs.EBADR }}}, + 'EXFULL': {{{ cDefs.EXFULL }}}, + 'ENOANO': {{{ cDefs.ENOANO }}}, + 'EBADRQC': {{{ cDefs.EBADRQC }}}, + 'EBADSLT': {{{ cDefs.EBADSLT }}}, + 'EDEADLOCK': {{{ cDefs.EDEADLOCK }}}, + 'EBFONT': {{{ cDefs.EBFONT }}}, + 'ENOSTR': {{{ cDefs.ENOSTR }}}, + 'ENODATA': {{{ cDefs.ENODATA }}}, + 'ETIME': {{{ cDefs.ETIME }}}, + 'ENOSR': {{{ cDefs.ENOSR }}}, + 'ENONET': {{{ cDefs.ENONET }}}, + 'ENOPKG': {{{ cDefs.ENOPKG }}}, + 'EREMOTE': {{{ cDefs.EREMOTE }}}, + 'ENOLINK': {{{ cDefs.ENOLINK }}}, + 'EADV': {{{ cDefs.EADV }}}, + 'ESRMNT': {{{ cDefs.ESRMNT }}}, + 'ECOMM': {{{ cDefs.ECOMM }}}, + 'EPROTO': {{{ cDefs.EPROTO }}}, + 'EMULTIHOP': {{{ cDefs.EMULTIHOP }}}, + 'EDOTDOT': {{{ cDefs.EDOTDOT }}}, + 'EBADMSG': {{{ cDefs.EBADMSG }}}, + 'ENOTUNIQ': {{{ cDefs.ENOTUNIQ }}}, + 'EBADFD': {{{ cDefs.EBADFD }}}, + 'EREMCHG': {{{ cDefs.EREMCHG }}}, + 'ELIBACC': {{{ cDefs.ELIBACC }}}, + 'ELIBBAD': {{{ cDefs.ELIBBAD }}}, + 'ELIBSCN': {{{ cDefs.ELIBSCN }}}, + 'ELIBMAX': {{{ cDefs.ELIBMAX }}}, + 'ELIBEXEC': {{{ cDefs.ELIBEXEC }}}, + 'ENOSYS': {{{ cDefs.ENOSYS }}}, + 'ENOTEMPTY': {{{ cDefs.ENOTEMPTY }}}, + 'ENAMETOOLONG': {{{ cDefs.ENAMETOOLONG }}}, + 'ELOOP': {{{ cDefs.ELOOP }}}, + 'EOPNOTSUPP': {{{ cDefs.EOPNOTSUPP }}}, + 'EPFNOSUPPORT': {{{ cDefs.EPFNOSUPPORT }}}, + 'ECONNRESET': {{{ cDefs.ECONNRESET }}}, + 'ENOBUFS': {{{ cDefs.ENOBUFS }}}, + 'EAFNOSUPPORT': {{{ cDefs.EAFNOSUPPORT }}}, + 'EPROTOTYPE': {{{ cDefs.EPROTOTYPE }}}, + 'ENOTSOCK': {{{ cDefs.ENOTSOCK }}}, + 'ENOPROTOOPT': {{{ cDefs.ENOPROTOOPT }}}, + 'ESHUTDOWN': {{{ cDefs.ESHUTDOWN }}}, + 'ECONNREFUSED': {{{ cDefs.ECONNREFUSED }}}, + 'EADDRINUSE': {{{ cDefs.EADDRINUSE }}}, + 'ECONNABORTED': {{{ cDefs.ECONNABORTED }}}, + 'ENETUNREACH': {{{ cDefs.ENETUNREACH }}}, + 'ENETDOWN': {{{ cDefs.ENETDOWN }}}, + 'ETIMEDOUT': {{{ cDefs.ETIMEDOUT }}}, + 'EHOSTDOWN': {{{ cDefs.EHOSTDOWN }}}, + 'EHOSTUNREACH': {{{ cDefs.EHOSTUNREACH }}}, + 'EINPROGRESS': {{{ cDefs.EINPROGRESS }}}, + 'EALREADY': {{{ cDefs.EALREADY }}}, + 'EDESTADDRREQ': {{{ cDefs.EDESTADDRREQ }}}, + 'EMSGSIZE': {{{ cDefs.EMSGSIZE }}}, + 'EPROTONOSUPPORT': {{{ cDefs.EPROTONOSUPPORT }}}, + 'ESOCKTNOSUPPORT': {{{ cDefs.ESOCKTNOSUPPORT }}}, + 'EADDRNOTAVAIL': {{{ cDefs.EADDRNOTAVAIL }}}, + 'ENETRESET': {{{ cDefs.ENETRESET }}}, + 'EISCONN': {{{ cDefs.EISCONN }}}, + 'ENOTCONN': {{{ cDefs.ENOTCONN }}}, + 'ETOOMANYREFS': {{{ cDefs.ETOOMANYREFS }}}, + 'EUSERS': {{{ cDefs.EUSERS }}}, + 'EDQUOT': {{{ cDefs.EDQUOT }}}, + 'ESTALE': {{{ cDefs.ESTALE }}}, + 'ENOTSUP': {{{ cDefs.ENOTSUP }}}, + 'ENOMEDIUM': {{{ cDefs.ENOMEDIUM }}}, + 'EILSEQ': {{{ cDefs.EILSEQ }}}, + 'EOVERFLOW': {{{ cDefs.EOVERFLOW }}}, + 'ECANCELED': {{{ cDefs.ECANCELED }}}, + 'ENOTRECOVERABLE': {{{ cDefs.ENOTRECOVERABLE }}}, + 'EOWNERDEAD': {{{ cDefs.EOWNERDEAD }}}, + 'ESTRPIPE': {{{ cDefs.ESTRPIPE }}}, + }`, + +#if PURE_WASI + $strError: (errno) => errno + '', +#else + $strError__deps: ['strerror', '$UTF8ToString'], + $strError: (errno) => UTF8ToString(_strerror(errno)), +#endif + +#if PROXY_POSIX_SOCKETS == 0 + // ========================================================================== + // netdb.h + // ========================================================================== + + $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; + }, + $inetNtop4: (addr) => + (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff), + $inetPton6__deps: ['htons'], + $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] + ]; + }, + $inetNtop6__deps: ['$inetNtop4', 'ntohs'], + $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; + }, + + $readSockaddr__deps: ['$inetNtop4', '$inetNtop6', 'ntohs'], + $readSockaddr: (sa, salen) => { + // family / port offsets are common to both sockaddr_in and sockaddr_in6 + var family = {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_family, 'i16') }}}; + var port = _ntohs({{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_port, 'u16') }}}); + var addr; + + switch (family) { + case {{{ cDefs.AF_INET }}}: + if (salen !== {{{ C_STRUCTS.sockaddr_in.__size__ }}}) { + return { errno: {{{ cDefs.EINVAL }}} }; + } + addr = {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'i32') }}}; + addr = inetNtop4(addr); + break; + case {{{ cDefs.AF_INET6 }}}: + if (salen !== {{{ C_STRUCTS.sockaddr_in6.__size__ }}}) { + return { errno: {{{ cDefs.EINVAL }}} }; + } + addr = [ + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+0, 'i32') }}}, + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+4, 'i32') }}}, + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+8, 'i32') }}}, + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+12, 'i32') }}} + ]; + addr = inetNtop6(addr); + break; + default: + return { errno: {{{ cDefs.EAFNOSUPPORT }}} }; + } + + return { family: family, addr: addr, port: port }; + }, + $writeSockaddr__docs: '/** @param {number=} addrlen */', + $writeSockaddr__deps: ['$inetPton4', '$inetPton6', '$zeroMemory', 'htons'], + $writeSockaddr: (sa, family, addr, port, addrlen) => { + switch (family) { + case {{{ cDefs.AF_INET }}}: + addr = inetPton4(addr); + zeroMemory(sa, {{{ C_STRUCTS.sockaddr_in.__size__ }}}); + if (addrlen) { + {{{ makeSetValue('addrlen', 0, C_STRUCTS.sockaddr_in.__size__, 'i32') }}}; + } + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_family, 'family', 'i16') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'addr', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_port, '_htons(port)', 'i16') }}}; + break; + case {{{ cDefs.AF_INET6 }}}: + addr = inetPton6(addr); + zeroMemory(sa, {{{ C_STRUCTS.sockaddr_in6.__size__ }}}); + if (addrlen) { + {{{ makeSetValue('addrlen', 0, C_STRUCTS.sockaddr_in6.__size__, 'i32') }}}; + } + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_family, 'family', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+0, 'addr[0]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+4, 'addr[1]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+8, 'addr[2]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+12, 'addr[3]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_port, '_htons(port)', 'i16') }}}; + break; + default: + return {{{ cDefs.EAFNOSUPPORT }}}; + } + return 0; + }, + + // We can't actually resolve hostnames in the browser, so instead + // we're generating fake IP addresses with lookup_name that we can + // resolve later on with lookup_addr. + // We do the aliasing in 172.29.*.*, giving us 65536 possibilities. + $DNS__deps: ['$inetPton4', '$inetPton6'], + $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++; +#if ASSERTIONS + assert(id < 65535, 'exceeded max address mappings of 65535'); +#endif + + 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; + } + }, + + _emscripten_lookup_name__deps: ['$UTF8ToString', '$DNS', '$inetPton4'], + _emscripten_lookup_name: (name) => { + // uint32_t _emscripten_lookup_name(const char *name); + var nameString = UTF8ToString(name); + return inetPton4(DNS.lookup_name(nameString)); + }, + + getaddrinfo__deps: ['$DNS', '$inetPton4', '$inetNtop4', '$inetPton6', '$inetNtop6', '$writeSockaddr', 'malloc', 'htonl'], + getaddrinfo__proxy: 'sync', + 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 = {{{ cDefs.AF_UNSPEC }}}; + 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 === {{{ cDefs.AF_INET6 }}} ? + {{{ C_STRUCTS.sockaddr_in6.__size__ }}} : + {{{ C_STRUCTS.sockaddr_in.__size__ }}}; + addr = family === {{{ cDefs.AF_INET6 }}} ? + inetNtop6(addr) : + inetNtop4(addr); + sa = _malloc(salen); + errno = writeSockaddr(sa, family, addr, port); +#if ASSERTIONS + assert(!errno); +#endif + + ai = _malloc({{{ C_STRUCTS.addrinfo.__size__ }}}); + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_family, 'family', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_socktype, 'type', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_protocol, 'proto', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_canonname, 'canon', '*') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addr, 'sa', '*') }}}; + if (family === {{{ cDefs.AF_INET6 }}}) { + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in6.__size__, 'i32') }}}; + } else { + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in.__size__, 'i32') }}}; + } + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_next, '0', 'i32') }}}; + + return ai; + } + + if (hint) { + flags = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}}; + family = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_family, 'i32') }}}; + type = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_socktype, 'i32') }}}; + proto = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_protocol, 'i32') }}}; + } + if (type && !proto) { + proto = type === {{{ cDefs.SOCK_DGRAM }}} ? {{{ cDefs.IPPROTO_UDP }}} : {{{ cDefs.IPPROTO_TCP }}}; + } + if (!type && proto) { + type = proto === {{{ cDefs.IPPROTO_UDP }}} ? {{{ cDefs.SOCK_DGRAM }}} : {{{ cDefs.SOCK_STREAM }}}; + } + + // 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 = {{{ cDefs.IPPROTO_TCP }}}; + } + if (type === 0) { + type = {{{ cDefs.SOCK_STREAM }}}; + } + + if (!node && !service) { + return {{{ cDefs.EAI_NONAME }}}; + } + if (flags & ~({{{ cDefs.AI_PASSIVE }}}|{{{ cDefs.AI_CANONNAME }}}|{{{ cDefs.AI_NUMERICHOST }}}| + {{{ cDefs.AI_NUMERICSERV }}}|{{{ cDefs.AI_V4MAPPED }}}|{{{ cDefs.AI_ALL }}}|{{{ cDefs.AI_ADDRCONFIG }}})) { + return {{{ cDefs.EAI_BADFLAGS }}}; + } + if (hint !== 0 && ({{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}} & {{{ cDefs.AI_CANONNAME }}}) && !node) { + return {{{ cDefs.EAI_BADFLAGS }}}; + } + if (flags & {{{ cDefs.AI_ADDRCONFIG }}}) { + // TODO + return {{{ cDefs.EAI_NONAME }}}; + } + if (type !== 0 && type !== {{{ cDefs.SOCK_STREAM }}} && type !== {{{ cDefs.SOCK_DGRAM }}}) { + return {{{ cDefs.EAI_SOCKTYPE }}}; + } + if (family !== {{{ cDefs.AF_UNSPEC }}} && family !== {{{ cDefs.AF_INET }}} && family !== {{{ cDefs.AF_INET6 }}}) { + return {{{ cDefs.EAI_FAMILY }}}; + } + + if (service) { + service = UTF8ToString(service); + port = parseInt(service, 10); + + if (isNaN(port)) { + if (flags & {{{ cDefs.AI_NUMERICSERV }}}) { + return {{{ cDefs.EAI_NONAME }}}; + } + // TODO support resolving well-known service names from: + // http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt + return {{{ cDefs.EAI_SERVICE }}}; + } + } + + if (!node) { + if (family === {{{ cDefs.AF_UNSPEC }}}) { + family = {{{ cDefs.AF_INET }}}; + } + if ((flags & {{{ cDefs.AI_PASSIVE }}}) === 0) { + if (family === {{{ cDefs.AF_INET }}}) { + addr = _htonl({{{ cDefs.INADDR_LOOPBACK }}}); + } else { + addr = [0, 0, 0, _htonl(1)]; + } + } + ai = allocaddrinfo(family, type, proto, null, addr, port); + {{{ makeSetValue('out', '0', '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 === {{{ cDefs.AF_UNSPEC }}} || family === {{{ cDefs.AF_INET }}}) { + family = {{{ cDefs.AF_INET }}}; + } + else if (family === {{{ cDefs.AF_INET6 }}} && (flags & {{{ cDefs.AI_V4MAPPED }}})) { + addr = [0, 0, _htonl(0xffff), addr]; + family = {{{ cDefs.AF_INET6 }}}; + } else { + return {{{ cDefs.EAI_NONAME }}}; + } + } else { + addr = inetPton6(node); + if (addr !== null) { + // incoming node is a valid ipv6 address + if (family === {{{ cDefs.AF_UNSPEC }}} || family === {{{ cDefs.AF_INET6 }}}) { + family = {{{ cDefs.AF_INET6 }}}; + } else { + return {{{ cDefs.EAI_NONAME }}}; + } + } + } + if (addr != null) { + ai = allocaddrinfo(family, type, proto, node, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; + return 0; + } + if (flags & {{{ cDefs.AI_NUMERICHOST }}}) { + return {{{ cDefs.EAI_NONAME }}}; + } + + // + // try as a hostname + // + // resolve the hostname to a temporary fake address + node = DNS.lookup_name(node); + addr = inetPton4(node); + if (family === {{{ cDefs.AF_UNSPEC }}}) { + family = {{{ cDefs.AF_INET }}}; + } else if (family === {{{ cDefs.AF_INET6 }}}) { + addr = [0, 0, _htonl(0xffff), addr]; + } + ai = allocaddrinfo(family, type, proto, null, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; + return 0; + }, + + getnameinfo__deps: ['$DNS', '$readSockaddr', '$stringToUTF8'], + getnameinfo: (sa, salen, node, nodelen, serv, servlen, flags) => { + var info = readSockaddr(sa, salen); + if (info.errno) { + return {{{ cDefs.EAI_FAMILY }}}; + } + var port = info.port; + var addr = info.addr; + + var overflowed = false; + + if (node && nodelen) { + var lookup; + if ((flags & {{{ cDefs.NI_NUMERICHOST }}}) || !(lookup = DNS.lookup_addr(addr))) { + if (flags & {{{ cDefs.NI_NAMEREQD }}}) { + return {{{ cDefs.EAI_NONAME }}}; + } + } 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 {{{ cDefs.EAI_OVERFLOW }}}; + } + + return 0; + }, + + // Implement netdb.h protocol entry (getprotoent, getprotobyname, getprotobynumber, setprotoent, endprotoent) + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/getprotobyname.html + // The Protocols object holds our 'fake' protocols 'database'. + $Protocols: { + list: [], + map: {} + }, + setprotoent__deps: ['$Protocols', '$stringToAscii', 'malloc'], + 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); + {{{ makeSetValue('aliasListBuf', 'j', 'aliasBuf', '*') }}}; + } + {{{ makeSetValue('aliasListBuf', 'j', '0', '*') }}}; // Terminating NULL pointer. + + // generate protoent + var pe = _malloc({{{ C_STRUCTS.protoent.__size__ }}}); + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_name, 'nameBuf', '*') }}}; + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_aliases, 'aliasListBuf', '*') }}}; + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_proto, 'proto', 'i32') }}}; + 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; + }, + + endprotoent: () => { + // void endprotoent(void); + // We're not using a real protocol database so we don't do a real close. + }, + + getprotoent__deps: ['setprotoent', '$Protocols'], + 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; + }, + + getprotobyname__deps: ['setprotoent', '$Protocols'], + getprotobyname: (name) => { + // struct protoent *getprotobyname(const char *); + name = UTF8ToString(name); + _setprotoent(true); + var result = Protocols.map[name]; + return result; + }, + + getprotobynumber__deps: ['setprotoent', '$Protocols'], + getprotobynumber: (number) => { + // struct protoent *getprotobynumber(int proto); + _setprotoent(true); + var result = Protocols.map[number]; + return result; + }, + + // ========================================================================== + // sockets. Note that the implementation assumes all sockets are always + // nonblocking + // ========================================================================== +#if SOCKET_WEBRTC + $Sockets__deps: [ + () => 'var SocketIO = ' + read('../third_party/socket.io.js') + ';\n', + () => 'var Peer = ' + read('../third_party/wrtcp.js') + ';\n' + ], +#endif + $Sockets: { + BUFFER_SIZE: 10*1024, // initial size + MAX_BUFFER_SIZE: 10*1024*1024, // maximum size we will grow the buffer + + nextFd: 1, + fds: {}, + nextport: 1, + maxport: 65535, + peer: null, + connections: {}, + portmap: {}, + localAddr: 0xfe00000a, // Local address is always 10.0.0.254 + addrPool: [ 0x0200000a, 0x0300000a, 0x0400000a, 0x0500000a, + 0x0600000a, 0x0700000a, 0x0800000a, 0x0900000a, 0x0a00000a, + 0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a] /* 0x0100000a is reserved */ + }, + +#endif // PROXY_POSIX_SOCKETS == 0 + + $timers: {}, + + // Helper function for setitimer that registers timers with the eventloop. + // Timers always fire on the main thread, either directly from JS (here) or + // or when the main thread is busy waiting calling _emscripten_yield. + _setitimer_js__proxy: 'sync', + _setitimer_js__deps: ['$timers', '$callUserCallback', '_emscripten_timeout', 'emscripten_get_now'], + _setitimer_js: (which, timeout_ms) => { +#if RUNTIME_DEBUG + dbg(`setitimer_js ${which} timeout=${timeout_ms}`); +#endif + // 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(() => { +#if ASSERTIONS + assert(which in timers); +#endif + delete timers[which]; +#if RUNTIME_DEBUG + dbg(`itimer fired: ${which}`); +#endif + callUserCallback(() => __emscripten_timeout(which, _emscripten_get_now())); + }, timeout_ms); + timers[which] = { id, timeout_ms }; + return 0; + }, + + // Helper for raise() to avoid signature mismatch failures: + // https://github.com/emscripten-core/posixtestsuite/issues/6 + __call_sighandler: (fp, sig) => {{{ makeDynCall('vi', 'fp') }}}(sig), + + // ========================================================================== + // emscripten.h + // ========================================================================== + + emscripten_run_script: (ptr) => { + {{{ makeEval('eval(UTF8ToString(ptr));') }}} + }, + + emscripten_run_script_int__docs: '/** @suppress{checkTypes} */', + emscripten_run_script_int: (ptr) => { + {{{ makeEval('return eval(UTF8ToString(ptr))|0;') }}} + }, + + // Mark as `noleakcheck` otherwise lsan will report the last returned string + // as a leak. + emscripten_run_script_string__noleakcheck: true, + emscripten_run_script_string__deps: ['$lengthBytesUTF8', '$stringToUTF8', 'realloc'], + emscripten_run_script_string: (ptr) => { + {{{ makeEval("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_random: () => Math.random(), + + emscripten_date_now: () => Date.now(), + + emscripten_performance_now: () => {{{ getPerformanceNow() }}}(), + +#if PTHREADS && !AUDIO_WORKLET + // Pthreads need their clocks synchronized to the execution of the main + // thread, so, when using them, make sure to adjust all timings to the + // respective time origins. + emscripten_get_now: () => performance.timeOrigin + {{{ getPerformanceNow() }}}(), +#else +#if AUDIO_WORKLET // https://github.com/WebAudio/web-audio-api/issues/2413 + emscripten_get_now: `; + // AudioWorkletGlobalScope does not have performance.now() + // (https://github.com/WebAudio/web-audio-api/issues/2527), so if building + // with + // Audio Worklets enabled, do a dynamic check for its presence. + if (globalThis.performance && {{{ getPerformanceNow() }}}) { +#if PTHREADS + _emscripten_get_now = () => performance.timeOrigin + {{{ getPerformanceNow() }}}(); +#else + _emscripten_get_now = () => {{{ getPerformanceNow() }}}(); +#endif + } else { + _emscripten_get_now = Date.now; + } +`, +#else + // Modern environment where performance.now() is supported: + // N.B. a shorter form "_emscripten_get_now = performance.now;" is + // unfortunately not allowed even in current browsers (e.g. FF Nightly 75). + emscripten_get_now: () => {{{ getPerformanceNow() }}}(), +#endif +#endif + + emscripten_get_now_res: () => { // return resolution of get_now, in nanoseconds +#if ENVIRONMENT_MAY_BE_NODE + if (ENVIRONMENT_IS_NODE) { + return 1; // nanoseconds + } +#endif +#if AUDIO_WORKLET // https://github.com/WebAudio/web-audio-api/issues/2413 + if (globalThis.performance?.now == 'function') { + return 1000; // microseconds (1/1000 of a millisecond) + } + return 1000*1000; // milliseconds +#else + // Modern environment where performance.now() is supported: + return 1000; // microseconds (1/1000 of a millisecond) +#endif + }, + + // Represents whether emscripten_get_now is guaranteed monotonic; the Date.now + // implementation is not :( + $nowIsMonotonic__internal: true, +#if AUDIO_WORKLET // // https://github.com/WebAudio/web-audio-api/issues/2413 + $nowIsMonotonic: `!!globalThis.performance?.now;`, +#else + // Modern environment where performance.now() is supported + $nowIsMonotonic: 1, +#endif + + _emscripten_get_now_is_monotonic__internal: true, + _emscripten_get_now_is_monotonic__deps: ['$nowIsMonotonic'], + _emscripten_get_now_is_monotonic: () => nowIsMonotonic, + + $warnOnce: (text) => { + warnOnce.shown ||= {}; + if (!warnOnce.shown[text]) { + warnOnce.shown[text] = 1; +#if ENVIRONMENT_MAY_BE_NODE + if (ENVIRONMENT_IS_NODE) text = 'warning: ' + text; +#endif + err(text); + } + }, + + _emscripten_log_formatted__deps: ['$getCallstack'], + _emscripten_log_formatted: (flags, str) => { + str = UTF8ToString(str); + + if (flags & {{{ cDefs.EM_LOG_C_STACK | cDefs.EM_LOG_JS_STACK }}}) { + 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 & {{{ cDefs.EM_LOG_CONSOLE }}}) { + if (flags & {{{ cDefs.EM_LOG_ERROR }}}) { + console.error(str); + } else if (flags & {{{ cDefs.EM_LOG_WARN }}}) { + console.warn(str); + } else if (flags & {{{ cDefs.EM_LOG_INFO }}}) { + console.info(str); + } else if (flags & {{{ cDefs.EM_LOG_DEBUG }}}) { + console.debug(str); + } else { + console.log(str); + } + } else if (flags & {{{ cDefs.EM_LOG_ERROR | cDefs.EM_LOG_WARN }}}) { + err(str); + } else { + out(str); + } + }, + + // We never free the return values of this function so we need to allocate + // using builtin_malloc to avoid LSan reporting these as leaks. +#if RETAIN_COMPILER_SETTINGS + emscripten_get_compiler_setting__noleakcheck: true, + emscripten_get_compiler_setting__deps: ['$stringToNewUTF8'], + emscripten_get_compiler_setting: (name) => { + name = UTF8ToString(name); + + var ret = getCompilerSetting(name); + if (typeof ret == 'number' || typeof ret == 'boolean') return ret; + + var cache = _emscripten_get_compiler_setting.cache ??= {}; + var fullret = cache[name]; + if (fullret) return fullret; + return cache[name] = stringToNewUTF8(ret); + }, +#else + emscripten_get_compiler_setting: (name) => abort('You must build with -sRETAIN_COMPILER_SETTINGS for getCompilerSetting or emscripten_get_compiler_setting to work'), +#endif + + emscripten_has_asyncify: () => {{{ ASYNCIFY }}}, + + emscripten_debugger: () => { debugger }, + + emscripten_print_double__deps: ['$stringToUTF8', '$lengthBytesUTF8'], + emscripten_print_double: (x, to, max) => { + var str = x + ''; + if (to) return stringToUTF8(str, to, max); + else return lengthBytesUTF8(str); + }, + +#if USE_ASAN || USE_LSAN || UBSAN_RUNTIME + // When lsan or asan is enabled withBuiltinMalloc temporarily replaces calls + // to malloc, calloc, free, and memalign. + $withBuiltinMalloc__deps: [ + 'malloc', 'calloc', 'free', 'memalign', 'realloc', + 'emscripten_builtin_malloc', 'emscripten_builtin_free', 'emscripten_builtin_memalign', 'emscripten_builtin_calloc', 'emscripten_builtin_realloc' + ], + $withBuiltinMalloc__docs: '/** @suppress{checkTypes} */', + $withBuiltinMalloc: (func) => { + var prev_malloc = typeof _malloc != 'undefined' ? _malloc : undefined; + var prev_calloc = typeof _calloc != 'undefined' ? _calloc : undefined; + var prev_memalign = typeof _memalign != 'undefined' ? _memalign : undefined; + var prev_free = typeof _free != 'undefined' ? _free : undefined; + var prev_realloc = typeof _realloc != 'undefined' ? _realloc : undefined; + _malloc = _emscripten_builtin_malloc; + _calloc = _emscripten_builtin_calloc; + _memalign = _emscripten_builtin_memalign; + _free = _emscripten_builtin_free; + _realloc = _emscripten_builtin_realloc; + try { + return func(); + } finally { + _malloc = prev_malloc; + _calloc = prev_calloc; + _memalign = prev_memalign; + _free = prev_free; + _realloc = prev_realloc; + } + }, + + _emscripten_sanitizer_use_colors: () => { + var setting = Module['printWithColors']; + if (setting !== undefined) { + return setting; + } + return ENVIRONMENT_IS_NODE && process.stderr.isTTY; + }, + + _emscripten_sanitizer_get_option__deps: ['$withBuiltinMalloc', '$stringToNewUTF8', '$UTF8ToString'], + _emscripten_sanitizer_get_option__sig: 'pp', + _emscripten_sanitizer_get_option: (name) => { + return withBuiltinMalloc(() => stringToNewUTF8(Module[UTF8ToString(name)] || "")); + }, +#endif + + $readEmAsmArgsArray: [], + $readEmAsmArgs__deps: ['$readEmAsmArgsArray'], + $readEmAsmArgs: (sigPtr, buf) => { +#if ASSERTIONS + // Nobody should have mutated _readEmAsmArgsArray underneath us to be something else than an array. + assert(Array.isArray(readEmAsmArgsArray)); + // The input buffer is allocated on the stack, so it must be stack-aligned. + assert(buf % {{{ STACK_ALIGN }}} == 0); +#endif + 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++]) { +#if ASSERTIONS + var chr = String.fromCharCode(ch); + var validChars = ['d', 'f', 'i', 'p']; +#if WASM_BIGINT + // In WASM_BIGINT mode we support passing i64 values as bigint. + validChars.push('j'); +#endif + assert(validChars.includes(chr), `Invalid character ${ch}("${chr}") in readEmAsmArgs! Use only [${validChars}], and do not specify "v" for void return argument.`); +#endif + // Floats are always passed as doubles, so all types except for 'i' + // are 8 bytes and require alignment. + var wide = (ch != {{{ charCode('i') }}}); +#if !MEMORY64 + wide &= (ch != {{{ charCode('p') }}}); +#endif + buf += wide && (buf % 8) ? 4 : 0; + readEmAsmArgsArray.push( + // Special case for pointers under wasm64 or CAN_ADDRESS_2GB mode. + ch == {{{ charCode('p') }}} ? {{{ makeGetValue('buf', 0, '*') }}} : +#if WASM_BIGINT + ch == {{{ charCode('j') }}} ? {{{ makeGetValue('buf', 0, 'i64') }}} : +#endif + ch == {{{ charCode('i') }}} ? + {{{ makeGetValue('buf', 0, 'i32') }}} : + {{{ makeGetValue('buf', 0, 'double') }}} + ); + buf += wide ? 8 : 4; + } + return readEmAsmArgsArray; + }, + +#if HAVE_EM_ASM + $runEmAsmFunction__deps: ['$readEmAsmArgs'], + $runEmAsmFunction: (code, sigPtr, argbuf) => { + var args = readEmAsmArgs(sigPtr, argbuf); +#if ASSERTIONS + assert(ASM_CONSTS.hasOwnProperty(code), `No EM_ASM constant found at address ${code}. The loaded WebAssembly file is likely out of sync with the generated JavaScript.`); +#endif + return ASM_CONSTS[code](...args); + }, + + emscripten_asm_const_int__deps: ['$runEmAsmFunction'], + emscripten_asm_const_int: (code, sigPtr, argbuf) => { + return runEmAsmFunction(code, sigPtr, argbuf); + }, + emscripten_asm_const_double__deps: ['$runEmAsmFunction'], + emscripten_asm_const_double: (code, sigPtr, argbuf) => { + return runEmAsmFunction(code, sigPtr, argbuf); + }, + + emscripten_asm_const_ptr__deps: ['$runEmAsmFunction'], + emscripten_asm_const_ptr: (code, sigPtr, argbuf) => { + return runEmAsmFunction(code, sigPtr, argbuf); + }, + + $runMainThreadEmAsm__deps: ['$readEmAsmArgs', +#if PTHREADS + '$proxyToMainThread' +#endif + ], + $runMainThreadEmAsm: (emAsmAddr, sigPtr, argbuf, sync) => { + var args = readEmAsmArgs(sigPtr, argbuf); +#if PTHREADS + if (ENVIRONMENT_IS_PTHREAD) { + // EM_ASM functions are variadic, receiving the actual arguments as a buffer + // in memory. the last parameter (argBuf) points to that data. We need to + // always un-variadify that, *before proxying*, as in the async case this + // is a stack allocation that LLVM made, which may go away before the main + // thread gets the message. For that reason we handle proxying *after* the + // call to readEmAsmArgs, and therefore we do that manually here instead + // of using __proxy. (And dor simplicity, do the same in the sync + // case as well, even though it's not strictly necessary, to keep the two + // code paths as similar as possible on both sides.) + return proxyToMainThread(0, emAsmAddr, sync, ...args); + } +#endif +#if ASSERTIONS + assert(ASM_CONSTS.hasOwnProperty(emAsmAddr), `No EM_ASM constant found at address ${emAsmAddr}. The loaded WebAssembly file is likely out of sync with the generated JavaScript.`); +#endif + return ASM_CONSTS[emAsmAddr](...args); + }, + emscripten_asm_const_int_sync_on_main_thread__deps: ['$runMainThreadEmAsm'], + emscripten_asm_const_int_sync_on_main_thread: (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1), + + emscripten_asm_const_ptr_sync_on_main_thread__deps: ['$runMainThreadEmAsm'], + emscripten_asm_const_ptr_sync_on_main_thread: (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1), + + emscripten_asm_const_double_sync_on_main_thread: 'emscripten_asm_const_int_sync_on_main_thread', + emscripten_asm_const_async_on_main_thread__deps: ['$runMainThreadEmAsm'], + emscripten_asm_const_async_on_main_thread: (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 0), +#endif + +#if !DECLARE_ASM_MODULE_EXPORTS + // When DECLARE_ASM_MODULE_EXPORTS is not set we export native symbols + // at runtime rather than statically in JS code. + $exportWasmSymbols__deps: ['$asmjsMangle', +#if DYNCALLS || !WASM_BIGINT + , '$dynCalls' +#endif + ], + $exportWasmSymbols: (wasmExports) => { + for (var [name, exportedSymbol] of Object.entries(wasmExports)) { + name = asmjsMangle(name); +#if DYNCALLS || !WASM_BIGINT + if (name.startsWith('dynCall_')) { + dynCalls[name.substr(8)] = exportedSymbol; + } +#endif + // Globals are currently statically enumerated into the output JS. + // TODO: If the number of Globals grows large, consider giving them a + // similar DECLARE_ASM_MODULE_EXPORTS = 0 treatment. + if (typeof exportedSymbol.value === 'undefined') { +#if MINIMAL_RUNTIME + globalThis[name] = exportedSymbol; +#else + globalThis[name] = Module[name] = exportedSymbol; +#endif + } + } + exportAliases(wasmExports); + }, +#endif + + // Parses as much of the given JS string to an integer, with quiet error + // handling (returns a NaN on error). E.g. jstoi_q("123abc") returns 123. + // Note that "smart" radix handling is employed for input string: + // "0314" is parsed as octal, and "0x1234" is parsed as base-16. + $jstoi_q__docs: '/** @suppress {checkTypes} */', + $jstoi_q: (str) => parseInt(str), + +#if LINK_AS_CXX + // libunwind + + _Unwind_Backtrace__deps: ['$getCallstack'], + _Unwind_Backtrace: (func, arg) => { + var trace = getCallstack(); + var parts = trace.split('\n'); + for (var i = 0; i < parts.length; i++) { + var ret = {{{ makeDynCall('iii', 'func') }}}(0, arg); + if (ret !== 0) return; + } + }, + + _Unwind_GetIPInfo: (context, ipBefore) => abort('Unwind_GetIPInfo'), + + _Unwind_FindEnclosingFunction: (ip) => 0, // we cannot succeed + + _Unwind_RaiseException__deps: ['__cxa_throw'], + _Unwind_RaiseException: (ex) => { + err('Warning: _Unwind_RaiseException is not correctly implemented'); + return ___cxa_throw(ex, 0, 0); + }, + + _Unwind_DeleteException: (ex) => err('TODO: Unwind_DeleteException'), +#endif + + // special runtime support + +#if STACK_OVERFLOW_CHECK + // Used by wasm-emscripten-finalize to implement STACK_OVERFLOW_CHECK + __handle_stack_overflow__deps: ['emscripten_stack_get_base', 'emscripten_stack_get_end', '$ptrToString'], + __handle_stack_overflow: (requested) => { + var base = _emscripten_stack_get_base(); + var end = _emscripten_stack_get_end(); + abort(`stack overflow (Attempt to set SP to ${ptrToString(requested)}` + + `, with stack limits [${ptrToString(end)} - ${ptrToString(base)}` + + ']). If you require more stack space build with -sSTACK_SIZE='); + }, +#endif + +#if MINIMAL_RUNTIME // MINIMAL_RUNTIME does not have a global runtime variable thisProgram + $getExecutableName: () => { +#if ENVIRONMENT_MAY_BE_NODE + if (ENVIRONMENT_IS_NODE && process.argv.length > 1) { + return process.argv[1].replace(/\\/g, '/'); + } +#endif + return "./this.program"; + }, +#else + $getExecutableName: () => thisProgram || './this.program', +#endif + + // Receives a Web Audio context plus a set of elements to listen for user + // input events on, and registers a context resume() for them. This lets + // audio work properly in an automatic way, as browsers won't let audio run + // without user interaction. + // If @elements is not provided, we default to the document and canvas + // elements, which handle common use cases. + // TODO(sbc): Remove seemingly unused elements argument + $autoResumeAudioContext__docs: '/** @param {Object=} elements */', + $autoResumeAudioContext: (ctx, elements) => { + if (!elements) { + elements = [document, document.getElementById('canvas')]; + } + ['keydown', 'mousedown', 'touchstart'].forEach((event) => { + elements.forEach((element) => { + element?.addEventListener(event, () => { + if (ctx.state === 'suspended') ctx.resume(); + }, { 'once': true }); + }); + }); + }, + +#if DYNCALLS || !WASM_BIGINT + $dynCalls__internal: true, + $dynCalls: {}, + $dynCallLegacy__deps: ['$dynCalls'], + $dynCallLegacy: (sig, ptr, args) => { + sig = sig.replace(/p/g, {{{ MEMORY64 ? "'j'" : "'i'" }}}) +#if ASSERTIONS + assert(sig in dynCalls, `bad function pointer type - sig is not in dynCalls: '${sig}'`); + if (args?.length) { +#if WASM_BIGINT + // j (64-bit integer) is fine, and is implemented as a BigInt. Without + // legalization, the number of parameters should match (j is not expanded + // into two i's). + assert(args.length === sig.length - 1); +#else + // j (64-bit integer) must be passed in as two numbers [low 32, high 32]. + assert(args.length === sig.substring(1).replace(/j/g, '--').length); +#endif + } else { + assert(sig.length == 1); + } +#endif + var f = dynCalls[sig]; + return f(ptr, ...args); + }, + $dynCall__deps: [ +#if DYNCALLS || !WASM_BIGINT + '$dynCallLegacy', +#endif +#if !DYNCALLS + '$getWasmTableEntry', +#endif + ], +#endif + + // Used in library code to get JS function from wasm function pointer. + // All callers should use direct table access where possible and only fall + // back to this function if needed. + $getDynCaller__deps: ['$dynCall'], + $getDynCaller: (sig, ptr, promising = false) => { +#if ASSERTIONS && !DYNCALLS + assert(sig.includes('j') || sig.includes('p'), 'getDynCaller should only be called with i64 sigs') +#endif + return (...args) => dynCall(sig, ptr, args, promising); + }, + + $dynCall: (sig, ptr, args = [], promising = false) => { +#if ASSERTIONS && (DYNCALLS || !WASM_BIGINT || !JSPI) + assert(!promising, 'async dynCall is not supported in this mode') +#endif +#if MEMORY64 + // With MEMORY64 we have an additional step to convert `p` arguments to + // bigint. This is the runtime equivalent of the wrappers we create for wasm + // exports in `emscripten.py:create_wasm64_wrappers`. + for (var i = 1; i < sig.length; ++i) { + if (sig[i] == 'p') args[i-1] = BigInt(args[i-1]); + } +#endif +#if DYNCALLS + var rtn = dynCallLegacy(sig, ptr, args); +#else +#if !WASM_BIGINT + // Without WASM_BIGINT support we cannot directly call function with i64 as + // part of their signature, so we rely on the dynCall functions generated by + // wasm-emscripten-finalize + if (sig.includes('j')) { + return dynCallLegacy(sig, ptr, args); + } +#endif +#if ASSERTIONS + assert(getWasmTableEntry(ptr), `missing table entry in dynCall: ${ptr}`); +#endif + var func = getWasmTableEntry(ptr); +#if JSPI + if (promising) { + func = WebAssembly.promising(func); + } +#endif + var rtn = func(...args); +#endif // DYNCALLS + + function convert(rtn) { +#if MEMORY64 + return sig[0] == 'p' ? Number(rtn) : rtn; +#elif CAN_ADDRESS_2GB + return sig[0] == 'p' ? rtn >>> 0 : rtn; +#else + return rtn; +#endif + } + +#if JSPI + if (promising) { + return rtn.then(convert); + } +#endif + return convert(rtn); + }, + + $callRuntimeCallbacks__internal: true, + $callRuntimeCallbacks: (callbacks) => { + while (callbacks.length > 0) { + // Pass the module as the first argument. + callbacks.shift()(Module); + } + }, + +#if SHRINK_LEVEL == 0 || ASYNCIFY == 2 + // A mirror copy of contents of wasmTable in JS side, to avoid relatively + // slow wasmTable.get() call. Only used when not compiling with -Os, -Oz, or + // JSPI which needs to instrument the functions. + $wasmTableMirror__internal: true, + $wasmTableMirror: [], + + $setWasmTableEntry__internal: true, + $setWasmTableEntry__deps: ['$wasmTableMirror', '$wasmTable'], + $setWasmTableEntry: (idx, func) => { + /** @suppress {checkTypes} */ + wasmTable.set({{{ toIndexType('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({{{ toIndexType('idx') }}}); + }, + + $getWasmTableEntry__internal: true, + $getWasmTableEntry__deps: ['$wasmTableMirror', '$wasmTable'], + $getWasmTableEntry: (funcPtr) => { +#if MEMORY64 + // Function pointers should show up as numbers, even under wasm64, but + // we still have some places where bigint values can flow here. + // https://github.com/emscripten-core/emscripten/issues/18200 + funcPtr = Number(funcPtr); +#endif + var func = wasmTableMirror[funcPtr]; + if (!func) { + /** @suppress {checkTypes} */ + wasmTableMirror[funcPtr] = func = wasmTable.get({{{ toIndexType('funcPtr') }}}); +#if ASYNCIFY == 2 + if (Asyncify.isAsyncExport(func)) { + wasmTableMirror[funcPtr] = func = Asyncify.makeAsyncFunction(func); + } +#endif + } +#if ASSERTIONS && ASYNCIFY != 2 // With JSPI the function stored in the table will be a wrapper. + /** @suppress {checkTypes} */ + assert(wasmTable.get({{{ toIndexType('funcPtr') }}}) == func, 'JavaScript-side Wasm function table mirror is out of date!'); +#endif + return func; + }, + +#else + + $setWasmTableEntry__docs: '/** @suppress{checkTypes} */', + $setWasmTableEntry__deps: ['$wasmTable'], + $setWasmTableEntry: (idx, func) => wasmTable.set({{{ toIndexType('idx') }}}, func), + + $getWasmTableEntry__docs: '/** @suppress{checkTypes} */', + $getWasmTableEntry__deps: ['$wasmTable'], + $getWasmTableEntry: (funcPtr) => { + // In -Os and -Oz builds, do not implement a JS side wasm table mirror for small + // code size, but directly access wasmTable, which is a bit slower as uncached. + return wasmTable.get({{{ toIndexType('funcPtr') }}}); + }, +#endif // SHRINK_LEVEL == 0 + + // Callable in pthread without __proxy needed. + emscripten_exit_with_live_runtime: () => { + {{{ runtimeKeepalivePush() }}} + throw 'unwind'; + }, + +#if !MINIMAL_RUNTIME + _emscripten_runtime_keepalive_clear__deps: ['$runtimeKeepaliveCounter'], +#endif + _emscripten_runtime_keepalive_clear: () => { +#if isSymbolNeeded('$noExitRuntime') + noExitRuntime = false; +#endif +#if !MINIMAL_RUNTIME + runtimeKeepaliveCounter = 0; +#endif + }, + + emscripten_force_exit__deps: ['exit', '_emscripten_runtime_keepalive_clear', +#if !EXIT_RUNTIME && ASSERTIONS + '$warnOnce', +#endif + ], + emscripten_force_exit__proxy: 'sync', + emscripten_force_exit: (status) => { +#if RUNTIME_DEBUG + dbg('emscripten_force_exit'); +#endif +#if !EXIT_RUNTIME && ASSERTIONS + warnOnce('emscripten_force_exit cannot actually shut down the runtime, as the build does not have EXIT_RUNTIME set'); +#endif + __emscripten_runtime_keepalive_clear(); + _exit(status); + }, + + emscripten_out: (str) => out(UTF8ToString(str)), + emscripten_outn: (str, len) => out(UTF8ToString(str, len)), + + emscripten_err: (str) => err(UTF8ToString(str)), + emscripten_errn: (str, len) => err(UTF8ToString(str, len)), + +#if ASSERTIONS || RUNTIME_DEBUG + emscripten_dbg: (str) => dbg(UTF8ToString(str)), + emscripten_dbgn: (str, len) => dbg(UTF8ToString(str, len)), + + emscripten_dbg_backtrace: (str) => { + dbg(UTF8ToString(str) + '\n' + new Error().stack); + }, +#endif + + // Use program_invocation_short_name and program_invocation_name in compiled + // programs. This function is for implementing them. + _emscripten_get_progname__deps: ['$getExecutableName', '$stringToUTF8'], + _emscripten_get_progname: (str, len) => stringToUTF8(getExecutableName(), str, len), + + emscripten_console_log: (str) => { +#if ASSERTIONS + assert(typeof str == 'number'); +#endif + console.log(UTF8ToString(str)); + }, + + emscripten_console_warn: (str) => { +#if ASSERTIONS + assert(typeof str == 'number'); +#endif + console.warn(UTF8ToString(str)); + }, + + emscripten_console_error: (str) => { +#if ASSERTIONS + assert(typeof str == 'number'); +#endif + console.error(UTF8ToString(str)); + }, + + emscripten_console_trace: (str) => { +#if ASSERTIONS + assert(typeof str == 'number'); +#endif + console.trace(UTF8ToString(str)); + }, + + emscripten_throw_number: (number) => { + throw number; + }, + + emscripten_throw_string: (str) => { +#if ASSERTIONS + assert(typeof str == 'number'); +#endif + throw UTF8ToString(str); + }, + +#if !MINIMAL_RUNTIME +#if STACK_OVERFLOW_CHECK + $handleException__deps: ['emscripten_stack_get_current'], +#endif + $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') { +#if RUNTIME_DEBUG + dbg(`handleException: unwinding: EXITSTATUS=${EXITSTATUS}`); +#endif + return EXITSTATUS; + } +#if STACK_OVERFLOW_CHECK + checkStackCookie(); + if (e instanceof WebAssembly.RuntimeError) { + if (_emscripten_stack_get_current() <= 0) { + err('Stack overflow detected. You can try increasing -sSTACK_SIZE (currently set to {{{ STACK_SIZE }}})'); + } + } +#endif + quit_(1, e); + }, + + $runtimeKeepaliveCounter__internal: true, + $runtimeKeepaliveCounter: 0, + +#if isSymbolNeeded('$noExitRuntime') + // If the `noExitRuntime` symbol is included in the build then + // keepRuntimeAlive is always conditional since its state can change + // at runtime. + $keepRuntimeAlive__deps: ['$runtimeKeepaliveCounter'], + $keepRuntimeAlive: () => noExitRuntime || runtimeKeepaliveCounter > 0, +#elif !EXIT_RUNTIME && !PTHREADS + // When `noExitRuntime` is not included and EXIT_RUNTIME=0 then we know the + // runtime can never exit (i.e. should always be kept alive). + // However, since pthreads themselves always need to be able to exit we + // have to track `runtimeKeepaliveCounter` in that case. + $keepRuntimeAlive: () => true, +#else + $keepRuntimeAlive__deps: ['$runtimeKeepaliveCounter'], + $keepRuntimeAlive: () => runtimeKeepaliveCounter > 0, +#endif + + // Callable in pthread without __proxy needed. + $runtimeKeepalivePush__deps: ['$runtimeKeepaliveCounter'], + $runtimeKeepalivePush__sig: 'v', + $runtimeKeepalivePush: () => { + runtimeKeepaliveCounter += 1; +#if RUNTIME_DEBUG + dbg(`runtimeKeepalivePush -> counter=${runtimeKeepaliveCounter}`); +#endif + }, + + $runtimeKeepalivePop__deps: ['$runtimeKeepaliveCounter'], + $runtimeKeepalivePop__sig: 'v', + $runtimeKeepalivePop: () => { +#if ASSERTIONS + assert(runtimeKeepaliveCounter > 0); +#endif + runtimeKeepaliveCounter -= 1; +#if RUNTIME_DEBUG + dbg(`runtimeKeepalivePop -> counter=${runtimeKeepaliveCounter}`); +#endif + }, + + emscripten_runtime_keepalive_push: '$runtimeKeepalivePush', + emscripten_runtime_keepalive_pop: '$runtimeKeepalivePop', + emscripten_runtime_keepalive_check: '$keepRuntimeAlive', + + // Used to call user callbacks from the embedder / event loop. For example + // setTimeout or any other kind of event handler that calls into user case + // needs to use this wrapper. + // + // The job of this wrapper is the handle emscripten-specific exceptions such + // as ExitStatus and 'unwind' and prevent these from escaping to the top + // level. + $callUserCallback__deps: ['$handleException', '$maybeExit'], + $callUserCallback: (func) => { +#if EXIT_RUNTIME + if (runtimeExited || ABORT) { +#else + if (ABORT) { +#endif +#if ASSERTIONS + err('user callback triggered after runtime exited or application aborted. Ignoring.'); +#endif + return; + } + try { + func(); + maybeExit(); + } catch (e) { + handleException(e); + } + }, + + $maybeExit__deps: ['exit', '$handleException', '$keepRuntimeAlive', +#if PTHREADS + '_emscripten_thread_exit', +#endif +#if RUNTIME_DEBUG >= 2 + '$runtimeKeepaliveCounter', +#endif + ], + $maybeExit: () => { +#if EXIT_RUNTIME + if (runtimeExited) { + return; + } +#endif +#if RUNTIME_DEBUG >= 2 + dbg(`maybeExit: user callback done: runtimeKeepaliveCounter=${runtimeKeepaliveCounter}`); +#endif + if (!keepRuntimeAlive()) { +#if RUNTIME_DEBUG + dbg(`maybeExit: calling exit() implicitly after user callback completed: ${EXITSTATUS}`); +#endif + try { +#if PTHREADS + if (ENVIRONMENT_IS_PTHREAD) { + // exit the current thread, but only if there is one active. + // TODO(https://github.com/emscripten-core/emscripten/issues/25076): + // Unify this check with the runtimeExited check above + if (_pthread_self()) __emscripten_thread_exit(EXITSTATUS); + return; + } +#endif + _exit(EXITSTATUS); + } catch (e) { + handleException(e); + } + } + }, + + $asyncLoad: async (url) => { + var arrayBuffer = await readAsync(url); + #if ASSERTIONS + assert(arrayBuffer, `Loading data file "${url}" failed (no arrayBuffer).`); + #endif + return new Uint8Array(arrayBuffer); + }, + +#else // MINIMAL_RUNTIME + $callUserCallback: (func) => { + // MINIMAL_RUNTIME doesn't support the runtimeKeepalive stuff, but under + // some circumstances it supportes `runtimeExited` +#if EXIT_RUNTIME + if (runtimeExited) { +#if ASSERTIONS + err('user callback triggered after runtime exited or application aborted. Ignoring.'); +#endif + return; + } +#endif + func(); + }, +#endif // MINIMAL_RUNTIME + + $asmjsMangle: (x) => { + if (x == '__main_argc_argv') { + x = 'main'; + } +#if DYNCALLS + return x.startsWith('dynCall_') ? x : '_' + x; +#else + return '_' + x; +#endif + }, + + $alignMemory: (size, alignment) => { +#if ASSERTIONS + assert(alignment, "alignment argument is required"); +#endif + return Math.ceil(size / alignment) * alignment; + }, + + // Allocate memory for an mmap operation. This allocates space of the right + // page-aligned size, and clears the allocated space. +#if hasExportedSymbol('emscripten_builtin_memalign') + $mmapAlloc__deps: ['$zeroMemory', '$alignMemory'], +#endif + $mmapAlloc: (size) => { +#if hasExportedSymbol('emscripten_builtin_memalign') + size = alignMemory(size, {{{ WASM_PAGE_SIZE }}}); + var ptr = _emscripten_builtin_memalign({{{ WASM_PAGE_SIZE }}}, size); + if (ptr) zeroMemory(ptr, size); + return ptr; +#elif ASSERTIONS + abort('internal error: mmapAlloc called but `emscripten_builtin_memalign` native symbol not exported'); +#else + abort(); +#endif + }, + +#if RELOCATABLE + // Globals that are normally exported from the wasm module but in relocatable + // mode are created here and imported by the module. + __stack_pointer: "new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}, {{{ to64(STACK_HIGH) }}})", + // tell the memory segments where to place themselves + __memory_base: "new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': false}, {{{ to64(GLOBAL_BASE) }}})", + // the wasm backend reserves slot 0 for the NULL function pointer + __table_base: "new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': false}, {{{ to64(TABLE_BASE) }}})", +#if MEMORY64 == 2 + __memory_base32: "new WebAssembly.Global({'value': 'i32', 'mutable': false}, {{{ GLOBAL_BASE }}})", +#endif +#if MEMORY64 + __table_base32: {{{ TABLE_BASE }}}, +#endif + // To support such allocations during startup, track them on __heap_base and + // then when the main module is loaded it reads that value and uses it to + // initialize sbrk (the main module is relocatable itself, and so it does not + // have __heap_base hardcoded into it - it receives it from JS as an extern + // global, basically). + __heap_base: '{{{ HEAP_BASE }}}', + __stack_high: '{{{ STACK_HIGH }}}', + __stack_low: '{{{ STACK_LOW }}}', + __global_base: '{{{ GLOBAL_BASE }}}', +#if ASYNCIFY == 1 + __asyncify_state: "new WebAssembly.Global({'value': 'i32', 'mutable': true}, 0)", + __asyncify_data: "new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}, {{{ to64(0) }}})", +#endif +#endif // RELOCATABLE + + _emscripten_fs_load_embedded_files__deps: ['$FS', '$PATH'], + _emscripten_fs_load_embedded_files: (ptr) => { +#if RUNTIME_DEBUG + dbg('preloading data files'); +#endif + do { + var name_addr = {{{ makeGetValue('ptr', '0', '*') }}}; + ptr += {{{ POINTER_SIZE }}}; + var len = {{{ makeGetValue('ptr', '0', '*') }}}; + ptr += {{{ POINTER_SIZE }}}; + var content = {{{ makeGetValue('ptr', '0', '*') }}}; + ptr += {{{ POINTER_SIZE }}}; + var name = UTF8ToString(name_addr) +#if RUNTIME_DEBUG + dbg(`preloading files: ${name}`); +#endif + 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 ({{{ makeGetValue('ptr', '0', '*') }}}); +#if RUNTIME_DEBUG + dbg('done preloading data files'); +#endif + }, + + $HandleAllocator: class { + allocated = [undefined]; + freelist = []; + get(id) { +#if ASSERTIONS + assert(this.allocated[id] !== undefined, `invalid handle: ${id}`); +#endif + 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) { +#if ASSERTIONS + assert(this.allocated[id] !== undefined); +#endif + // 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); + } + }, + + $wasmTable__docs: '/** @type {WebAssembly.Table} */', +#if RELOCATABLE + // In RELOCATABLE mode we create the table in JS. + $wasmTable: `=new WebAssembly.Table({ + 'initial': {{{ toIndexType(INITIAL_TABLE) }}}, +#if !ALLOW_TABLE_GROWTH + 'maximum': {{{ toIndexType(INITIAL_TABLE) }}}, +#endif +#if MEMORY64 == 1 + 'address': 'i64', +#endif + 'element': 'anyfunc' +}); +`, +#else + // `wasmTable` is a JS alias for the Wasm `__indirect_function_table` export + $wasmTable: '__indirect_function_table', +#endif + +#if IMPORTED_MEMORY + // This gets defined in src/runtime_init_memory.js + $wasmMemory: undefined, +#else + // `wasmMemory` is a JS alias for the Wasm `memory` export + $wasmMemory: 'memory', +#endif + + $getUniqueRunDependency: (id) => { +#if ASSERTIONS + var orig = id; + while (1) { + if (!runDependencyTracking[id]) return id; + id = orig + Math.random(); + } +#else + return id; +#endif + }, + + $noExitRuntime__postset: () => addAtModule(makeModuleReceive('noExitRuntime')), + $noExitRuntime: {{{ !EXIT_RUNTIME }}}, + +#if !MINIMAL_RUNTIME + // A counter of dependencies for calling run(). If we need to + // do asynchronous work before running, increment this and + // decrement it. Incrementing must happen in a place like + // Module.preRun (used by emcc to add file preloading). + // Note that you can add dependencies in preRun, even though + // it happens right before run - run will be postponed until + // the dependencies are met. + $runDependencies__internal: true, + $runDependencies: 0, + // overridden to take different actions when all run dependencies are fulfilled + $dependenciesFulfilled__internal: true, + $dependenciesFulfilled: null, +#if ASSERTIONS + $runDependencyTracking__internal: true, + $runDependencyTracking: {}, + $runDependencyWatcher__internal: true, + $runDependencyWatcher: null, +#endif + + $addRunDependency__deps: ['$runDependencies', '$removeRunDependency', +#if ASSERTIONS + '$runDependencyTracking', + '$runDependencyWatcher', +#endif + ], + $addRunDependency: (id) => { + runDependencies++; + +#if expectToReceiveOnModule('monitorRunDependencies') + Module['monitorRunDependencies']?.(runDependencies); +#endif + +#if ASSERTIONS +#if RUNTIME_DEBUG + dbg('addRunDependency', id); +#endif + assert(id, 'addRunDependency requires an ID') + assert(!runDependencyTracking[id]); + runDependencyTracking[id] = 1; + if (runDependencyWatcher === null && globalThis.setInterval) { + // Check for missing dependencies every few seconds + runDependencyWatcher = setInterval(() => { + if (ABORT) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + return; + } + var shown = false; + for (var dep in runDependencyTracking) { + if (!shown) { + shown = true; + err('still waiting on run dependencies:'); + } + err(`dependency: ${dep}`); + } + if (shown) { + err('(end of list)'); + } + }, 10000); +#if ENVIRONMENT_MAY_BE_NODE + // Prevent this timer from keeping the runtime alive if nothing + // else is. + runDependencyWatcher.unref?.() +#endif + } +#endif + }, + + $removeRunDependency__deps: ['$runDependencies', '$dependenciesFulfilled', +#if ASSERTIONS + '$runDependencyTracking', + '$runDependencyWatcher', +#endif + ], + $removeRunDependency: (id) => { + runDependencies--; + +#if expectToReceiveOnModule('monitorRunDependencies') + Module['monitorRunDependencies']?.(runDependencies); +#endif + +#if ASSERTIONS +#if RUNTIME_DEBUG + dbg('removeRunDependency', id); +#endif + assert(id, 'removeRunDependency requires an ID'); + assert(runDependencyTracking[id]); + delete runDependencyTracking[id]; +#endif + if (runDependencies == 0) { +#if ASSERTIONS + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } +#endif + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); // can add another dependenciesFulfilled + } + } + }, +#endif + + // The following addOn functions are for adding runtime callbacks at + // various executions points. Each addOn function has a corresponding + // compiled time version named addAt that will instead inline during + // compilation (see parseTools.mjs). + // Note: if there are both runtime and compile time code, the runtime + // callbacks will be invoked before the compile time code. + + // See ATPRERUNS in parseTools.mjs for more information. + $onPreRuns: [], + $onPreRuns__internal: true, + $onPreRuns__deps: ['$callRuntimeCallbacks'], + $onPreRuns__postset: () => { + ATPRERUNS.unshift('callRuntimeCallbacks(onPreRuns);'); + }, + $addOnPreRun__deps: ['$onPreRuns'], + $addOnPreRun: (cb) => onPreRuns.push(cb), + // See ATINITS in parseTools.mjs for more information. + $onInits: [], + $onInits__internal: true, + $onInits__deps: ['$callRuntimeCallbacks'], + $onInits__postset: () => { + ATINITS.unshift('callRuntimeCallbacks(onInits);'); + }, + $addOnInit__deps: ['$onInits'], + $addOnInit: (cb) => onInits.push(cb), + // See ATPOSTCTORS in parseTools.mjs for more information. + $onPostCtors: [], + $onPostCtors__internal: true, + $onPostCtors__deps: ['$callRuntimeCallbacks'], + $onPostCtors__postset: () => { + ATPOSTCTORS.unshift('callRuntimeCallbacks(onPostCtors);'); + }, + $addOnPostCtor__deps: ['$onPostCtors'], + $addOnPostCtor: (cb) => onPostCtors.push(cb), + // See ATMAINS in parseTools.mjs for more information. + $onMains: [], + $onMains__internal: true, + $onMains__deps: ['$callRuntimeCallbacks'], + $onMains__postset: () => { + ATMAINS.unshift('callRuntimeCallbacks(onMains);'); + }, + $addOnPreMain__deps: ['$onMains'], + $addOnPreMain: (cb) => onMains.push(cb), + // See ATEXITS in parseTools.mjs for more information. + $onExits: [], + $onExits__internal: true, + $onExits__deps: ['$callRuntimeCallbacks'], + $onExits__postset: () => { + ATEXITS.unshift('callRuntimeCallbacks(onExits);'); + }, + $addOnExit__deps: ['$onExits'], + $addOnExit: (cb) => onExits.push(cb), + // See ATPOSTRUNS in parseTools.mjs for more information. + $onPostRuns: [], + $onPostRuns__internal: true, + $onPostRuns__deps: ['$callRuntimeCallbacks'], + $onPostRuns__postset: () => { + ATPOSTRUNS.unshift('callRuntimeCallbacks(onPostRuns);'); + }, + $addOnPostRun__deps: ['$onPostRuns'], + $addOnPostRun: (cb) => onPostRuns.push(cb), + + // We used to define these globals unconditionally in support code. + // Instead, we now define them here so folks can pull it in explicitly, on + // demand. + $STACK_SIZE: {{{ STACK_SIZE }}}, + $STACK_ALIGN: {{{ STACK_ALIGN }}}, + $POINTER_SIZE: {{{ POINTER_SIZE }}}, + $ASSERTIONS: {{{ ASSERTIONS }}}, +}); + +function autoAddDeps(lib, name) { + for (const item of Object.keys(lib)) { + if (!isDecorator(item)) { + lib[item + '__deps'] ??= []; + lib[item + '__deps'].push(name); + } + } +} + +#if LEGACY_RUNTIME +// Library functions that were previously included as runtime functions are +// automatically included when `LEGACY_RUNTIME` is set. +extraLibraryFuncs.push( + '$addFunction', + '$removeFunction', + '$allocate', + '$ALLOC_NORMAL', + '$ALLOC_STACK', + '$AsciiToString', + '$stringToAscii', + '$UTF16ToString', + '$stringToUTF16', + '$lengthBytesUTF16', + '$UTF32ToString', + '$stringToUTF32', + '$lengthBytesUTF32', + '$stringToNewUTF8', + '$stringToUTF8OnStack', + '$writeStringToMemory', + '$writeArrayToMemory', + '$writeAsciiToMemory', + '$intArrayFromString', + '$intArrayToString', + '$warnOnce', + '$ccall', + '$cwrap', + '$ExitStatus', + '$UTF8ArrayToString', + '$UTF8ToString', + '$stringToUTF8Array', + '$stringToUTF8', + '$lengthBytesUTF8', +); +#endif + +function wrapSyscallFunction(x, library, isWasi) { + if (isJsOnlySymbol(x) || isDecorator(x)) { + return; + } + + var t = library[x]; + if (typeof t == 'string') return; + t = t.toString(); + + // If a syscall uses FS, but !SYSCALLS_REQUIRE_FILESYSTEM, then the user + // has disabled the filesystem or we have proven some other way that this will + // not be called in practice, and do not need that code. + if (!SYSCALLS_REQUIRE_FILESYSTEM && t.includes('FS.')) { + library[x + '__deps'] = []; + t = modifyJSFunction(t, (args, body) => { + return `(${args}) => {\n` + + (ASSERTIONS ? "abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM');\n" : '') + + '}'; + }); + } + + var isVariadic = !isWasi && t.includes(', varargs'); +#if SYSCALLS_REQUIRE_FILESYSTEM + var canThrow = library[x + '__nothrow'] !== true; +#else + var canThrow = false; +#endif + + library[x + '__deps'] ??= []; + +#if PURE_WASI && !GROWABLE_ARRAYBUFFERS + // In PURE_WASI mode we can't assume the wasm binary was built by emscripten + // and politely notify us on memory growth. Instead we have to check for + // possible memory growth on each syscall. + var pre = '\nif (!HEAPU8.byteLength) _emscripten_notify_memory_growth(0);\n' + library[x + '__deps'].push('emscripten_notify_memory_growth'); +#else + var pre = ''; +#endif + var post = ''; + if (isVariadic) { + pre += 'SYSCALLS.varargs = varargs;\n'; + } + +#if SYSCALL_DEBUG + if (isVariadic) { + if (canThrow) { + post += 'finally { SYSCALLS.varargs = undefined; }\n'; + } else { + post += 'SYSCALLS.varargs = undefined;\n'; + } + } + pre += `dbg('syscall! ${x}: [' + Array.prototype.slice.call(arguments) + ']');\n`; + pre += "var canWarn = true;\n"; + pre += "var ret = (() => {"; + post += "})();\n"; + post += "if (ret && ret < 0 && canWarn) {\n"; + post += " dbg(`error: syscall may have failed with ${-ret} (${strError(-ret)})`);\n"; + post += "}\n"; + post += "dbg(`syscall return: ${ret}`);\n"; + post += "return ret;\n"; + // Emit dependency to strError() since we added use of it above. + library[x + '__deps'].push('$strError'); +#endif + delete library[x + '__nothrow']; + var handler = ''; + if (canThrow) { + pre += 'try {\n'; + handler += + "} catch (e) {\n" + + " if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e;\n"; +#if SYSCALL_DEBUG + handler += + " dbg(`error: syscall failed with ${e.errno} (${strError(e.errno)})`);\n" + + " canWarn = false;\n"; +#endif + // Musl syscalls are negated. + if (isWasi) { + handler += " return e.errno;\n"; + } else { + // Musl syscalls are negated. + handler += " return -e.errno;\n"; + } + handler += "}\n"; + } + post = handler + post; + + if (pre || post) { + t = modifyJSFunction(t, (args, body) => `function (${args}) {\n${pre}${body}${post}}\n`); + } + + library[x] = eval('(' + t + ')'); + // Automatically add dependency on `$SYSCALLS` + if (!WASMFS && t.includes('SYSCALLS')) { + library[x + '__deps'].push('$SYSCALLS'); + } +#if PTHREADS + // Most syscalls need to happen on the main JS thread (e.g. because the + // filesystem is in JS and on that thread). Proxy synchronously to there. + // There are some exceptions, syscalls that we know are ok to just run in + // any thread; those are marked as not being proxied with + // __proxy: false + // A syscall without a return value could perhaps be proxied asynchronously + // instead of synchronously, and marked with + // __proxy: 'async' + // (but essentially all syscalls do have return values). + if (library[x + '__proxy'] === undefined) { + library[x + '__proxy'] = 'sync'; + } +#endif +} diff --git a/src/lib/libdylink.js b/src/lib/libdylink.js new file mode 100644 index 0000000000000..a0e19f63197c0 --- /dev/null +++ b/src/lib/libdylink.js @@ -0,0 +1,1367 @@ +/** + * @license + * Copyright 2020 The Emscripten Authors + * SPDX-License-Identifier: MIT + * + * Dynamic library loading + */ + +#if !RELOCATABLE +#error "library_dylink.js requires RELOCATABLE" +#endif + +var LibraryDylink = { +#if FILESYSTEM + $registerWasmPlugin__deps: ['$preloadPlugins'], + $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}`); + } +#if DYLINK_DEBUG + dbg('registering preloadedWasm:', name); +#endif + preloadedWasm[name] = exports; + return byteArray; + }) + }; + preloadPlugins.push(wasmPlugin); + }, + + $preloadedWasm__deps: ['$registerWasmPlugin'], + $preloadedWasm__postset: ` + registerWasmPlugin(); + `, + $preloadedWasm: {}, + + $replaceORIGIN__deps: ['$PATH'], + $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; + }, +#endif // FILESYSTEM + + $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; + } +#if ASYNCIFY + // 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; + } +#endif + return true; + }, + + // Dynamic version of shared.py:make_invoke. This is needed for invokes + // that originate from side modules since these are not known at JS + // generation time. +#if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten' + $createInvokeFunction__internal: true, + $createInvokeFunction__deps: ['$dynCall', 'setThrew', '$stackSave', '$stackRestore'], + $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. +#if EXCEPTION_STACK_TRACES + // Exceptions thrown from C++ and longjmps will be an instance of + // EmscriptenEH. + if (!(e instanceof EmscriptenEH)) throw e; +#else + // 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; +#endif + _setThrew(1, 0); +#if WASM_BIGINT + // 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; +#endif + } + }, +#endif + + // Resolve a global symbol by name. This is used during module loading to + // resolve imports, and by `dlsym` when used with `RTLD_DEFAULT`. + // Returns both the resolved symbol (i.e. a function or a global) along with + // the canonical name of the symbol (in some cases is modify the symbol as + // part of the loop process, so that actual symbol looked up has a different + // name). + $resolveGlobalSymbol__deps: ['$isSymbolDefined', '$createNamedFunction', +#if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten' + '$createInvokeFunction', +#endif + ], + $resolveGlobalSymbol__internal: true, + $resolveGlobalSymbol: (symName, direct = false) => { + var sym; +#if !WASM_BIGINT + // First look for the orig$ symbol which is the symbol without i64 + // legalization performed. + if (direct && ('orig$' + symName in wasmImports)) { + symName = 'orig$' + symName; + } +#endif + if (isSymbolDefined(symName)) { + sym = wasmImports[symName]; + } +#if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten' + // 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])); + } +#endif +#if !DISABLE_EXCEPTION_CATCHING + else if (symName.startsWith('__cxa_find_matching_catch_')) { + // When the main module is linked we create whichever variants of + // `__cxa_find_matching_catch_` (see jsifier.js) that we know are needed, + // but a side module loaded at runtime might need different/additional + // variants so we create those dynamically. + sym = wasmImports[symName] = createNamedFunction(symName, (...args) => { +#if MEMORY64 + args = args.map(Number); +#endif + var rtn = findMatchingCatch(args); + return {{{ to64('rtn') }}}; + }); + } +#endif + return {sym, name: symName}; + }, + + $GOT: {}, + $currentModuleWeakSymbols: '=new Set({{{ JSON.stringify(Array.from(WEAK_IMPORTS)) }}})', + + // Create globals to each imported symbol. These are all initialized to zero + // and get assigned later in `updateGOT` + $GOTHandler__internal: true, + $GOTHandler__deps: ['$GOT', '$currentModuleWeakSymbols'], + $GOTHandler: { + get(obj, symName) { + var rtn = GOT[symName]; + if (!rtn) { + rtn = GOT[symName] = new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}); +#if DYLINK_DEBUG == 2 + dbg("new GOT entry: " + symName); +#endif + } + 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; + } + }, + + $isInternalSym__internal: true, + $isInternalSym: (symName) => { + // TODO: find a way to mark these in the binary or avoid exporting them. + return [ + '__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__') +#if SPLIT_MODULE + // Exports synthesized by wasm-split should be prefixed with '%' + || symName[0] == '%' +#endif + ; + }, + + $updateGOT__internal: true, + $updateGOT__deps: ['$GOT', '$isInternalSym', '$addFunction'], + $updateGOT: (exports, replace) => { +#if DYLINK_DEBUG + dbg(`updateGOT: adding ${Object.keys(exports).length} symbols`); +#endif + for (var symName in exports) { + if (isInternalSym(symName)) { + continue; + } + + var value = exports[symName]; +#if !WASM_BIGINT + if (symName.startsWith('orig$')) { + symName = symName.split('$')[1]; + replace = true; + } +#endif + + GOT[symName] ||= new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}); + if (replace || GOT[symName].value == 0) { +#if DYLINK_DEBUG == 2 + dbg(`updateGOT: before: ${symName} : ${GOT[symName].value}`); +#endif + if (typeof value == 'function') { + GOT[symName].value = {{{ to64('addFunction(value)') }}}; +#if DYLINK_DEBUG == 2 + dbg(`updateGOT: FUNC: ${symName} : ${GOT[symName].value}`); +#endif + } else if (typeof value == {{{ POINTER_JS_TYPE }}}) { + GOT[symName].value = value; + } else { + err(`unhandled export type for '${symName}': ${typeof value}`); + } +#if DYLINK_DEBUG == 2 + dbg(`updateGOT: after: ${symName} : ${GOT[symName].value} (${value})`); +#endif + } +#if DYLINK_DEBUG + else if (GOT[symName].value != value) { + dbg(`updateGOT: EXISTING SYMBOL: ${symName} : ${GOT[symName].value} (${value})`); + } +#endif + } +#if DYLINK_DEBUG + dbg("done updateGOT"); +#endif + }, + + // Applies relocations to exported things. + $relocateExports__internal: true, + $relocateExports__deps: ['$updateGOT'], + $relocateExports__docs: '/** @param {boolean=} replace */', + $relocateExports: (exports, memoryBase, replace) => { + var relocated = {}; + + for (var e in exports) { + var value = exports[e]; +#if SPLIT_MODULE + // Do not modify exports synthesized by wasm-split + if (e.startsWith('%')) { + relocated[e] = value + continue; + } +#endif + if (typeof value?.value != 'undefined') { + // a breaking change in the wasm spec, globals are now objects + // https://github.com/WebAssembly/mutable-global/issues/1 + value = value.value; + } + if (typeof value == {{{ POINTER_JS_TYPE }}}) { + value += {{{ to64('memoryBase') }}}; + } + relocated[e] = value; + } + updateGOT(relocated, replace); + return relocated; + }, + + $reportUndefinedSymbols__internal: true, + $reportUndefinedSymbols__deps: ['$GOT', '$resolveGlobalSymbol'], + $reportUndefinedSymbols: () => { +#if DYLINK_DEBUG + dbg('reportUndefinedSymbols'); +#endif + for (var [symName, entry] of Object.entries(GOT)) { + if (entry.value == 0) { + var value = resolveGlobalSymbol(symName, true).sym; + if (!value && !entry.required) { + // Ignore undefined symbols that are imported as weak. +#if DYLINK_DEBUG + dbg('ignoring undefined weak symbol:', symName); +#endif + continue; + } +#if ASSERTIONS + assert(value, `undefined symbol '${symName}'. perhaps a side module was not linked in? if this global was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment`); +#endif +#if DYLINK_DEBUG == 2 + dbg(`assigning dynamic symbol from main module: ${symName} -> ${prettyPrint(value)}`); +#endif + if (typeof value == 'function') { + /** @suppress {checkTypes} */ + entry.value = {{{ to64('addFunction(value, value.sig)') }}}; +#if DYLINK_DEBUG == 2 + dbg(`assigning table entry for : ${symName} -> ${entry.value}`); +#endif + } else if (typeof value == 'number') { + entry.value = {{{ to64('value') }}}; +#if MEMORY64 + } else if (typeof value == 'bigint') { + entry.value = value; +#endif + } else { + throw new Error(`bad export type for '${symName}': ${typeof value}`); + } + } + } +#if DYLINK_DEBUG + dbg('done reportUndefinedSymbols'); +#endif + }, + + // dynamic linker/loader (a-la ld.so on ELF systems) + $LDSO__deps: ['$newDSO'], + $LDSO: { + // name -> dso [refcount, name, module, global]; Used by dlopen + loadedLibsByName: {}, + // handle -> dso; Used by dlsym + loadedLibsByHandle: {}, + init() { +#if ASSERTIONS + // This function needs to run after the initial wasmImports object + // as been created. + assert(wasmImports); +#endif + newDSO('__main__', {{{ cDefs.RTLD_DEFAULT }}}, wasmImports); + }, + }, + + $dlSetError__internal: true, + $dlSetError__deps: ['__dl_seterr', '$stringToUTF8OnStack', '$stackSave', '$stackRestore'], + $dlSetError: (msg) => { +#if DYLINK_DEBUG + dbg('dlSetError:', msg); +#endif + var sp = stackSave(); + var cmsg = stringToUTF8OnStack(msg); + ___dl_seterr(cmsg, 0); + stackRestore(sp); + }, + + // We support some amount of allocation during startup in the case of + // dynamic linking, which needs to allocate memory for dynamic libraries that + // are loaded. That has to happen before the main program can start to run, + // because the main program needs those linked in before it runs (so we can't + // use normally malloc from the main program to do these allocations). + // + // Allocate memory even if malloc isn't ready yet. The allocated memory here + // must be zero initialized since its used for all static data, including bss. + $getMemory__noleakcheck: true, + $getMemory__deps: ['$GOT', '__heap_base', '$alignMemory', 'calloc'], + $getMemory: (size) => { + // After the runtime is initialized, we must only use sbrk() normally. +#if DYLINK_DEBUG + dbg("getMemory: " + size + " runtimeInitialized=" + runtimeInitialized); +#endif + 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, {{{ STACK_ALIGN }}}); +#if ASSERTIONS + assert(end <= HEAP8.length, 'failure to getMemory - memory growth etc. is not supported there, call malloc/sbrk directly or increase INITIAL_MEMORY'); +#endif + ___heap_base = end; + GOT['__heap_base'].value = {{{ to64('end') }}}; + return ret; + }, + + // returns the side module metadata as an object + // { memorySize, memoryAlign, tableSize, tableAlign, neededDynlibs} + $getDylinkMetadata__deps: ['$UTF8ArrayToString'], + $getDylinkMetadata__internal: true, + $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); +#if SUPPORT_BIG_ENDIAN + var magicNumberFound = int32View[0] == 0x6d736100 || int32View[0] == 0x0061736d; +#else + var magicNumberFound = int32View[0] == 0x6d736100; +#endif + 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 { +#if ASSERTIONS + err('unknown dylink.0 subsection:', subsectionType) +#endif + // unknown subsection + offset += subsectionSize; + } + } + +#if ASSERTIONS + var tableAlign = Math.pow(2, customSection.tableAlign); + assert(tableAlign === 1, `invalid tableAlign ${tableAlign}`); + assert(offset == end); +#endif + +#if DYLINK_DEBUG + dbg('dylink needed:', customSection.neededDynlibs); +#endif + + return customSection; + }, + +#if DYNCALLS || !WASM_BIGINT + $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; + } + } + } + }, +#endif + + // Module.symbols <- libModule.symbols (flags.global handler) + $mergeLibSymbols__deps: ['$isSymbolDefined'], + $mergeLibSymbols: (exports, libName) => { +#if DYNCALLS || !WASM_BIGINT + registerDynCallSymbols(exports); +#endif + // add symbols into global namespace TODO: weak linking etc. + for (var [sym, exp] of Object.entries(exports)) { +#if ASSERTIONS == 2 + if (isSymbolDefined(sym)) { + var curr = wasmImports[sym], next = exp; + // don't warn on functions - might be odr, linkonce_odr, etc. + if (!(typeof curr == 'function' && typeof next == 'function')) { + err(`warning: symbol '${sym}' from '${libName}' already exists (duplicate symbol? or weak linking, which isn't supported yet?)`); // + [curr, ' vs ', next]); + } + } +#endif + + // 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 ASYNCIFY + if (target in asyncifyStubs) { + asyncifyStubs[target] = exp; + } +#endif + if (!isSymbolDefined(target)) { + wasmImports[target] = exp; + } + } + setImport(sym); + +#if !hasExportedSymbol('main') + // 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') + } +#endif + } + }, + +#if DYLINK_DEBUG + $dumpTable__deps: ['$wasmTable'], + $dumpTable: () => { + var len = wasmTable.length; + for (var i = {{{ toIndexType(0) }}} ; i < len; i++) { + dbg(`table: ${i} : ${wasmTable.get(i)}`); + } + }, +#endif + + // Loads a side module from binary data or compiled Module. Returns the module's exports or a + // promise that resolves to its exports if the loadAsync flag is set. + $loadWebAssemblyModule__docs: ` + /** + * @param {string=} libName + * @param {Object=} localScope + * @param {number=} handle + */`, + $loadWebAssemblyModule__deps: [ + '$loadDynamicLibrary', '$getMemory', + '$relocateExports', '$resolveGlobalSymbol', '$GOTHandler', + '$getDylinkMetadata', '$alignMemory', + '$currentModuleWeakSymbols', + '$updateTableMap', + '$wasmTable', + '$addOnPostCtor', + ], + $loadWebAssemblyModule: (binary, flags, libName, localScope, handle) => { +#if DYLINK_DEBUG + dbg('loadWebAssemblyModule:', libName); +#endif + var metadata = getDylinkMetadata(binary); + + // loadModule loads the wasm module after all its dependencies have been loaded. + // can be called both sync/async. + function loadModule() { +#if ASSERTIONS + var originalTable = wasmTable; +#endif +#if PTHREADS + // The first thread to load a given module needs to allocate the static + // table and memory regions. Later threads re-use the same table region + // and can ignore the memory region (since memory is shared between + // threads already). + // If `handle` is specified than it is assumed that the calling thread has + // exclusive access to it for the duration of this function. See the + // locking in `dynlink.c`. + var firstLoad = !handle || !{{{ makeGetValue('handle', C_STRUCTS.dso.mem_allocated, 'i8') }}}; + if (firstLoad) { +#endif + // 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 ? {{{ from64Expr('wasmTable.length') }}} : 0; + if (handle) { + {{{ makeSetValue('handle', C_STRUCTS.dso.mem_allocated, '1', 'i8') }}}; + {{{ makeSetValue('handle', C_STRUCTS.dso.mem_addr, 'memoryBase', '*') }}}; + {{{ makeSetValue('handle', C_STRUCTS.dso.mem_size, 'metadata.memorySize', 'i32') }}}; + {{{ makeSetValue('handle', C_STRUCTS.dso.table_addr, 'tableBase', '*') }}}; + {{{ makeSetValue('handle', C_STRUCTS.dso.table_size, 'metadata.tableSize', 'i32') }}}; + } +#if PTHREADS + } else { + // Read the values for tableBase and memoryBase from shared memory. The + // thread that first loaded the DLL already set these values. + memoryBase = {{{ makeGetValue('handle', C_STRUCTS.dso.mem_addr, '*') }}}; + tableBase = {{{ makeGetValue('handle', C_STRUCTS.dso.table_addr, '*') }}}; + } +#endif + + if (metadata.tableSize) { +#if ASSERTIONS + assert({{{ from64Expr('wasmTable.length') }}} == tableBase, `unexpected table size while loading ${libName}: ${wasmTable.length}`); +#endif +#if DYLINK_DEBUG + dbg("loadModule: growing table by: " + metadata.tableSize); +#endif + wasmTable.grow({{{ toIndexType('metadata.tableSize') }}}); + } +#if DYLINK_DEBUG + dbg(`loadModule: memory[${memoryBase}:${memoryBase + metadata.memorySize}]` + + ` table[${tableBase}:${tableBase + metadata.tableSize}]`); +#endif + + // 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]; + } +#if ASSERTIONS + assert(resolved, `undefined symbol '${sym}'. perhaps a side module was not linked in? if this global was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment`); +#endif + 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 {{{ to64('memoryBase') }}}; + case '__table_base': + return {{{ to64('tableBase') }}}; +#if MEMORY64 +#if MEMORY64 == 2 + case '__memory_base32': + return memoryBase; +#endif + case '__table_base32': + return tableBase; +#endif + } + if (prop in wasmImports && !wasmImports[prop].stub) { + // No stub needed, symbol already exists in symbol table + var res = wasmImports[prop]; +#if ASYNCIFY + // Asyncify wraps exports, and we need to look through those wrappers. + if (res.orig) { + res = res.orig; + } +#endif + 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_MODULE_NAME }}}': proxy, + }; + + function postInstantiation(module, instance) { +#if ASSERTIONS + // the table should be unchanged + assert(wasmTable === originalTable); +#endif +#if PTHREADS + if (!ENVIRONMENT_IS_PTHREAD && libName) { +#if DYLINK_DEBUG + dbg('registering sharedModules:', libName) +#endif + // cache all loaded modules in `sharedModules`, which gets passed + // to new workers when they are created. + sharedModules[libName] = module; + } +#endif + // add new entries to functionsInTableMap + updateTableMap(tableBase, metadata.tableSize); + moduleExports = relocateExports(instance.exports, memoryBase); +#if ASYNCIFY + moduleExports = Asyncify.instrumentWasmExports(moduleExports); +#endif + if (!flags.allowUndefined) { + reportUndefinedSymbols(); + } +#if STACK_OVERFLOW_CHECK >= 2 + // If the runtime has already been initialized we set the stack limits + // now. Otherwise this is delayed until `setDylinkStackLimits` is + // called after initialization. + if (moduleExports['__set_stack_limits'] && runtimeInitialized) { + moduleExports['__set_stack_limits']({{{ to64('_emscripten_stack_get_base()') }}}, {{{ to64('_emscripten_stack_get_end()') }}}); + } +#endif + +#if MAIN_MODULE + function addEmAsm(addr, body) { + var args = []; + var arity = 0; + for (; arity < 16; arity++) { + if (body.indexOf('$' + arity) != -1) { + args.push('$' + arity); + } else { + break; + } + } + args = args.join(','); + var func = `(${args}) => { ${body} };`; +#if DYLINK_DEBUG + dbg('adding new EM_ASM constant at:', ptrToString(start)); +#endif + {{{ makeEval('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']; + {{{ from64('start') }}} + {{{ from64('stop') }}} + 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 i in cSig) { + var jsArg = cSig[i].split(' ').pop(); + jsArgs.push(jsArg.replace('*', '')); + } + } + var func = `(${jsArgs}) => ${body};`; +#if DYLINK_DEBUG + dbg(`adding new EM_JS function: ${jsArgs} = ${func}`); +#endif + {{{ makeEval('moduleExports[name] = eval(func)') }}}; + } + + for (var name in moduleExports) { + if (name.startsWith('__em_js__')) { + var start = moduleExports[name] + var jsString = UTF8ToString({{{ from64Expr('start') }}}); + // EM_JS strings are stored in the data section in the form + // SIG<::>BODY. + var parts = jsString.split('<::>'); + addEmJs(name.replace('__em_js__', ''), parts[0], parts[1]); + delete moduleExports[name]; + } + } +#endif + + // initialize the module +#if PTHREADS + // Only one thread should call __wasm_call_ctors, but all threads need + // to call _emscripten_tls_init + registerTLSInit(moduleExports['_emscripten_tls_init'], instance.exports, metadata) + if (firstLoad) { +#endif + var applyRelocs = moduleExports['__wasm_apply_data_relocs']; + if (applyRelocs) { + if (runtimeInitialized) { +#if DYLINK_DEBUG + dbg('applyRelocs'); +#endif + 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); + } + } +#if PTHREADS + } +#endif + 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); + } + + metadata.neededDynlibs.forEach((needed) => loadDynamicLibrary(needed, flags, localScope)); + return loadModule(); + }, + +#if STACK_OVERFLOW_CHECK >= 2 + // Sometimes we load libraries before runtime initialization. In this case + // we delay calling __set_stack_limits (which must be called for each + // module). + $setDylinkStackLimits: (stackTop, stackMax) => { + for (var name in LDSO.loadedLibsByName) { +#if DYLINK_DEBUG + dbg(`setDylinkStackLimits for '${name}'`); +#endif + var lib = LDSO.loadedLibsByName[name]; + lib.exports['__set_stack_limits']?.({{{ to64("stackTop") }}}, {{{ to64("stackMax") }}}); + } + }, +#endif + + $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; + }, + +#if FILESYSTEM + $findLibraryFS__deps: [ + '$replaceORIGIN', + '_emscripten_find_dylib', + '$withStackSave', + '$stackAlloc', + '$lengthBytesUTF8', + '$stringToUTF8OnStack', + '$stringToUTF8', + '$FS', + '$PATH', +#if WASMFS + '_wasmfs_identify', + '_wasmfs_read_file', +#endif + ], + $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)) { +#if WASMFS + var result = withStackSave(() => __wasmfs_identify(stringToUTF8OnStack(libName))); + return result === {{{ cDefs.EEXIST }}} ? libName : undefined; +#else + try { + FS.lookupPath(libName); + return libName; + } catch (e) { + return undefined; + } +#endif + } + 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; + }); + }, +#endif // FILESYSTEM + + // loadDynamicLibrary loads dynamic library @ lib URL / path and returns + // handle for loaded DSO. + // + // Several flags affect the loading: + // + // - if flags.global=true, symbols from the loaded library are merged into global + // process namespace. Flags.global is thus similar to RTLD_GLOBAL in ELF. + // + // - if flags.nodelete=true, the library will be never unloaded. Flags.nodelete + // is thus similar to RTLD_NODELETE in ELF. + // + // - if flags.loadAsync=true, the loading is performed asynchronously and + // loadDynamicLibrary returns corresponding promise. + // + // If a library was already loaded, it is not loaded a second time. However + // flags.global and flags.nodelete are handled every time a load request is made. + // Once a library becomes "global" or "nodelete", it cannot be removed or unloaded. + $loadDynamicLibrary__deps: ['$LDSO', '$loadWebAssemblyModule', + '$mergeLibSymbols', '$newDSO', + '$asyncLoad', +#if FILESYSTEM + '$preloadedWasm', + '$findLibraryFS', +#endif +#if DYNCALLS || !WASM_BIGINT + '$registerDynCallSymbols', +#endif + ], + $loadDynamicLibrary__docs: ` + /** + * @param {number=} handle + * @param {Object=} localScope + */`, + $loadDynamicLibrary: function(libName, flags = {global: true, nodelete: true}, localScope, handle) { +#if DYLINK_DEBUG + dbg(`loadDynamicLibrary: ${libName} handle: ${handle}`); + dbg('existing:', Object.keys(LDSO.loadedLibsByName)); +#endif + // 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 ASSERTIONS + assert(dso.exports !== 'loading', `Attempt to load '${libName}' twice before the first load completed`); +#endif + if (!flags.global) { + if (localScope) { + Object.assign(localScope, dso.exports); + } +#if DYNCALLS || !WASM_BIGINT + registerDynCallSymbols(dso.exports); +#endif + } 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() { +#if PTHREADS + var sharedMod = sharedModules[libName]; +#if DYLINK_DEBUG + dbg(`checking sharedModules: ${libName}: ${sharedMod ? 'found' : 'not found'}`); +#endif + if (sharedMod) { + return flags.loadAsync ? Promise.resolve(sharedMod) : sharedMod; + } +#endif + + // for wasm, we can use fetch for async, but for fs mode we can only imitate it + if (handle) { + var data = {{{ makeGetValue('handle', C_STRUCTS.dso.file_data, '*') }}}; + var dataSize = {{{ makeGetValue('handle', C_STRUCTS.dso.file_data_size, '*') }}}; + if (data && dataSize) { + var libData = HEAP8.slice(data, data + dataSize); + return flags.loadAsync ? Promise.resolve(libData) : libData; + } + } + +#if FILESYSTEM + var f = findLibraryFS(libName, flags.rpath); +#if DYLINK_DEBUG + dbg(`checking filesystem: ${libName}: ${f ? 'found' : 'not found'}`); +#endif + if (f) { + var libData = FS.readFile(f, {encoding: 'binary'}); + return flags.loadAsync ? Promise.resolve(libData) : libData; + } +#endif + + 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() { +#if FILESYSTEM + // lookup preloaded cache first + var preloaded = preloadedWasm[libName]; +#if DYLINK_DEBUG + dbg(`checking preloadedWasm: ${libName}: ${preloaded ? 'found' : 'not found'}`); +#endif + if (preloaded) { + return flags.loadAsync ? Promise.resolve(preloaded) : preloaded; + } +#endif + + // 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); +#if DYNCALLS || !WASM_BIGINT + registerDynCallSymbols(exports); +#endif + } + dso.exports = exports; + } + + if (flags.loadAsync) { +#if DYLINK_DEBUG + dbg("loadDynamicLibrary: done (async)"); +#endif + return getExports().then((exports) => { + moduleLoaded(exports); + return true; + }); + } + + moduleLoaded(getExports()); +#if DYLINK_DEBUG + dbg("loadDynamicLibrary: done"); +#endif + return true; + }, + + $loadDylibs__internal: true, + $loadDylibs__deps: ['$loadDynamicLibrary', '$reportUndefinedSymbols', '$addRunDependency', '$removeRunDependency'], + $loadDylibs: async () => { + if (!dynamicLibraries.length) { +#if DYLINK_DEBUG + dbg('loadDylibs: no libraries to preload'); +#endif + reportUndefinedSymbols(); + return; + } + +#if DYLINK_DEBUG + dbg('loadDylibs:', dynamicLibraries); +#endif + 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(); + +#if DYLINK_DEBUG + dbg('loadDylibs done!'); +#endif + removeRunDependency('loadDylibs'); + }, + + // void* dlopen(const char* filename, int flags); + $dlopenInternal__deps: ['$dlSetError', '$PATH'], + $dlopenInternal: (handle, jsflags) => { + // void *dlopen(const char *file, int mode); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html + var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}}); + var flags = {{{ makeGetValue('handle', C_STRUCTS.dso.flags, 'i32') }}}; +#if DYLINK_DEBUG + dbg('dlopenInternal:', filename); +#endif + filename = PATH.normalize(filename); + var searchpaths = []; + + var global = Boolean(flags & {{{ cDefs.RTLD_GLOBAL }}}); + var localScope = global ? null : {}; + + // We don't care about RTLD_NOW and RTLD_LAZY. + var combinedFlags = { + global, + nodelete: Boolean(flags & {{{ cDefs.RTLD_NODELETE }}}), + loadAsync: jsflags.loadAsync, + } + + if (jsflags.loadAsync) { + return loadDynamicLibrary(filename, combinedFlags, localScope, handle); + } + + try { + return loadDynamicLibrary(filename, combinedFlags, localScope, handle) + } catch (e) { +#if ASSERTIONS + err(`Error in loading dynamic library ${filename}: ${e}`); +#endif + dlSetError(`Could not load dynamic lib: ${filename}\n${e}`); + return 0; + } + }, + + _dlopen_js__deps: ['$dlopenInternal'], +#if ASYNCIFY + _dlopen_js__async: true, +#endif + _dlopen_js: {{{ asyncIf(ASYNCIFY == 2) }}} (handle) => +#if ASYNCIFY + Asyncify.handleSleep((wakeUp) => + dlopenInternal(handle, { loadAsync: true }) + .then(wakeUp) + // Note: this currently relies on being able to catch errors even from `wakeUp` callback itself. + // That's why we can't refactor it to `handleAsync` at the moment. + .catch(() => wakeUp(0)) + ) +#else + dlopenInternal(handle, { loadAsync: false }) +#endif + , + + // Async version of dlopen. + _emscripten_dlopen_js__deps: ['$dlopenInternal', '$callUserCallback', '$dlSetError'], + _emscripten_dlopen_js: (handle, onsuccess, onerror, user_data) => { + /** @param {Object=} e */ + function errorCallback(e) { + var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}}); + dlSetError(`'Could not load dynamic lib: ${filename}\n${e}`); + {{{ runtimeKeepalivePop() }}} + callUserCallback(() => {{{ makeDynCall('vpp', 'onerror') }}}(handle, user_data)); + } + function successCallback() { + {{{ runtimeKeepalivePop() }}} + callUserCallback(() => {{{ makeDynCall('vpp', 'onsuccess') }}}(handle, user_data)); + } + + {{{ runtimeKeepalivePush() }}} + var promise = dlopenInternal(handle, { loadAsync: true }); + if (promise) { + promise.then(successCallback, errorCallback); + } else { + errorCallback(); + } + }, + + _dlsym_catchup_js: (handle, symbolIndex) => { +#if DYLINK_DEBUG + dbg("_dlsym_catchup: handle=" + ptrToString(handle) + " symbolIndex=" + symbolIndex); +#endif + 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); +#if DYLINK_DEBUG + dbg(`_dlsym_catchup: result=${result}`); +#endif + return result; + }, + + // void* dlsym(void* handle, const char* symbol); + _dlsym_js__deps: ['$dlSetError', '$getFunctionAddress', '$addFunction'], + _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); +#if DYLINK_DEBUG + dbg('dlsym_js:', symbol); +#endif + var result; + var newSymIndex; + + var lib = LDSO.loadedLibsByHandle[handle]; +#if ASSERTIONS + assert(lib, `Tried to dlsym() from an unopened handle: ${handle}`); +#endif + 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; + } +#if !WASM_BIGINT + var origSym = 'orig$' + symbol; + result = lib.exports[origSym]; + if (result) { + newSymIndex = Object.keys(lib.exports).indexOf(origSym); + } + else +#endif + result = lib.exports[symbol]; + + if (typeof result == 'function') { +#if DYLINK_DEBUG + dbg(`dlsym_js: ${symbol} getting table slot for: ${result}`); +#endif + +#if ASYNCIFY + // Asyncify wraps exports, and we need to look through those wrappers. + if (result.orig) { + result = result.orig; + } +#endif + var addr = getFunctionAddress(result); + if (addr) { +#if DYLINK_DEBUG + dbg('symbol already exists in table:', symbol); +#endif + 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); +#if DYLINK_DEBUG + dbg('adding symbol to table:', symbol); +#endif + {{{ makeSetValue('symbolIndex', 0, 'newSymIndex', '*') }}}; + } + } +#if DYLINK_DEBUG + dbg(`dlsym_js: ${symbol} -> ${result}`); +#endif + return result; + }, +}; + +addToLibrary(LibraryDylink); diff --git a/src/lib/libegl.js b/src/lib/libegl.js new file mode 100644 index 0000000000000..491f5e4d0b4ed --- /dev/null +++ b/src/lib/libegl.js @@ -0,0 +1,677 @@ +/** + * @license + * Copyright 2012 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +/* + * The EGL implementation supports only one EGLNativeDisplayType, the + * EGL_DEFAULT_DISPLAY. This native display type returns the only supported + * EGLDisplay handle with the magic value 62000. There is only a single + * EGLConfig configuration supported, that has the magic value 62002. The + * implementation only allows a single EGLContext to be created, that has the + * magic value of 62004. (multiple creations silently return this same context) + * The implementation only creates a single EGLSurface, a handle with the magic + * value of 62006. (multiple creations silently return the same surface) + */ + +{{{ +// Magic ID for Emscripten 'default display' +const eglDefaultDisplay = 62000; +// Magic ID for the only EGLConfig supported by Emscripten +const eglDefaultConfig = 62002; +// Magic ID for Emscripten EGLContext +const eglDefaultContext = 62004; +}}} + +var LibraryEGL = { + $EGL__deps: ['$Browser'], + $EGL: { + // This variable tracks the success status of the most recently invoked EGL function call. + errorCode: 0x3000 /* EGL_SUCCESS */, + defaultDisplayInitialized: false, + currentContext: 0 /* EGL_NO_CONTEXT */, + currentReadSurface: 0 /* EGL_NO_SURFACE */, + currentDrawSurface: 0 /* EGL_NO_SURFACE */, + + contextAttributes: { + alpha: false, + depth: false, + stencil: false, + antialias: false + }, + + stringCache: {}, + + setErrorCode(code) { + EGL.errorCode = code; + }, + + chooseConfig(display, attribList, config, config_size, numConfigs) { + if (display != {{{ eglDefaultDisplay }}}) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + + if (attribList) { + // read attribList if it is non-null + for (;;) { + var param = {{{ makeGetValue('attribList', '0', 'i32') }}}; + if (param == 0x3021 /*EGL_ALPHA_SIZE*/) { + var alphaSize = {{{ makeGetValue('attribList', '4', 'i32') }}}; + EGL.contextAttributes.alpha = (alphaSize > 0); + } else if (param == 0x3025 /*EGL_DEPTH_SIZE*/) { + var depthSize = {{{ makeGetValue('attribList', '4', 'i32') }}}; + EGL.contextAttributes.depth = (depthSize > 0); + } else if (param == 0x3026 /*EGL_STENCIL_SIZE*/) { + var stencilSize = {{{ makeGetValue('attribList', '4', 'i32') }}}; + EGL.contextAttributes.stencil = (stencilSize > 0); + } else if (param == 0x3031 /*EGL_SAMPLES*/) { + var samples = {{{ makeGetValue('attribList', '4', 'i32') }}}; + EGL.contextAttributes.antialias = (samples > 0); + } else if (param == 0x3032 /*EGL_SAMPLE_BUFFERS*/) { + var samples = {{{ makeGetValue('attribList', '4', 'i32') }}}; + EGL.contextAttributes.antialias = (samples == 1); + } else if (param == 0x3100 /*EGL_CONTEXT_PRIORITY_LEVEL_IMG*/) { + var requestedPriority = {{{ makeGetValue('attribList', '4', 'i32') }}}; + 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) { + {{{ makeSetValue('numConfigs', '0', '1', 'i32') }}}; // Total number of supported configs: 1. + } + if (config && config_size > 0) { + {{{ makeSetValue('config', '0', eglDefaultConfig /* Magic ID for the only EGLConfig supported by Emscripten */, '*') }}}; + } + + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 1; + }, + }, + + // EGLAPI EGLDisplay EGLAPIENTRY eglGetDisplay(EGLNativeDisplayType display_id); + eglGetDisplay__proxy: 'sync', + 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 {{{ eglDefaultDisplay }}}; + }, + + // EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor); + eglInitialize__proxy: 'sync', + eglInitialize: (display, majorVersion, minorVersion) => { + if (display != {{{ eglDefaultDisplay }}}) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + if (majorVersion) { + {{{ makeSetValue('majorVersion', '0', '1', 'i32') }}}; // Advertise EGL Major version: '1' + } + if (minorVersion) { + {{{ makeSetValue('minorVersion', '0', '4', 'i32') }}}; // Advertise EGL Minor version: '4' + } + EGL.defaultDisplayInitialized = true; + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 1; + }, + + // EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy); + eglTerminate__proxy: 'sync', + eglTerminate: (display) => { + if (display != {{{ eglDefaultDisplay }}}) { + 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; + }, + + // EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config); + eglGetConfigs__proxy: 'sync', + eglGetConfigs: (display, configs, config_size, numConfigs) => + EGL.chooseConfig(display, 0, configs, config_size, numConfigs), + + // EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config); + eglChooseConfig__proxy: 'sync', + eglChooseConfig: (display, attrib_list, configs, config_size, numConfigs) => + EGL.chooseConfig(display, attrib_list, configs, config_size, numConfigs), + + // EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value); + eglGetConfigAttrib__proxy: 'sync', + eglGetConfigAttrib: (display, config, attribute, value) => { + if (display != {{{ eglDefaultDisplay }}}) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + if (config != {{{ eglDefaultConfig }}}) { + 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 + {{{ makeSetValue('value', '0', 'EGL.contextAttributes.alpha ? 32 : 24' /* 8 bits for each R,G,B. 8 bits for alpha if enabled*/, 'i32') }}}; + return 1; + case 0x3021: // EGL_ALPHA_SIZE + {{{ makeSetValue('value', '0', 'EGL.contextAttributes.alpha ? 8 : 0' /* 8 bits for alpha channel if enabled. */, 'i32') }}}; + return 1; + case 0x3022: // EGL_BLUE_SIZE + {{{ makeSetValue('value', '0', '8' /* 8 bits for blue channel. */, 'i32') }}}; + return 1; + case 0x3023: // EGL_GREEN_SIZE + {{{ makeSetValue('value', '0', '8' /* 8 bits for green channel. */, 'i32') }}}; + return 1; + case 0x3024: // EGL_RED_SIZE + {{{ makeSetValue('value', '0', '8' /* 8 bits for red channel. */, 'i32') }}}; + return 1; + case 0x3025: // EGL_DEPTH_SIZE + {{{ makeSetValue('value', '0', 'EGL.contextAttributes.depth ? 24 : 0' /* 24 bits for depth buffer if enabled. */, 'i32') }}}; + return 1; + case 0x3026: // EGL_STENCIL_SIZE + {{{ makeSetValue('value', '0', 'EGL.contextAttributes.stencil ? 8 : 0' /* 8 bits for stencil buffer if enabled. */, 'i32') }}}; + 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). + {{{ makeSetValue('value', '0', '0x3038' /* EGL_NONE */, 'i32') }}}; + return 1; + case 0x3028: // EGL_CONFIG_ID + {{{ makeSetValue('value', '0', eglDefaultConfig, 'i32') }}}; + return 1; + case 0x3029: // EGL_LEVEL + {{{ makeSetValue('value', '0', '0' /* Z order/depth layer for this level. Not applicable for Emscripten. */, 'i32') }}}; + return 1; + case 0x302A: // EGL_MAX_PBUFFER_HEIGHT + {{{ makeSetValue('value', '0', '4096', 'i32') }}}; + return 1; + case 0x302B: // EGL_MAX_PBUFFER_PIXELS + {{{ makeSetValue('value', '0', '16777216' /* 4096 * 4096 */, 'i32') }}}; + return 1; + case 0x302C: // EGL_MAX_PBUFFER_WIDTH + {{{ makeSetValue('value', '0', '4096', 'i32') }}}; + return 1; + case 0x302D: // EGL_NATIVE_RENDERABLE + {{{ makeSetValue('value', '0', '0' /* This config does not allow co-rendering with other 'native' rendering APIs. */, 'i32') }}}; + return 1; + case 0x302E: // EGL_NATIVE_VISUAL_ID + {{{ makeSetValue('value', '0', '0' /* N/A for Emscripten. */, 'i32') }}}; + return 1; + case 0x302F: // EGL_NATIVE_VISUAL_TYPE + {{{ makeSetValue('value', '0', '0x3038' /* EGL_NONE */, 'i32') }}}; + return 1; + case 0x3031: // EGL_SAMPLES + {{{ makeSetValue('value', '0', 'EGL.contextAttributes.antialias ? 4 : 0' /* 2x2 Multisampling */, 'i32') }}}; + return 1; + case 0x3032: // EGL_SAMPLE_BUFFERS + {{{ makeSetValue('value', '0', 'EGL.contextAttributes.antialias ? 1 : 0' /* Multisampling enabled */, 'i32') }}}; + return 1; + case 0x3033: // EGL_SURFACE_TYPE + {{{ makeSetValue('value', '0', '0x4' /* EGL_WINDOW_BIT */, 'i32') }}}; + 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. + {{{ makeSetValue('value', '0', '0x3038' /* EGL_NONE */, 'i32') }}}; + 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." + {{{ makeSetValue('value', '0', '-1' /* Report a "does not apply" value. */, 'i32') }}}; + return 1; + case 0x3039: // EGL_BIND_TO_TEXTURE_RGB + case 0x303A: // EGL_BIND_TO_TEXTURE_RGBA + {{{ makeSetValue('value', '0', '0' /* Only pbuffers would be bindable, but these are not supported. */, 'i32') }}}; + return 1; + case 0x303B: // EGL_MIN_SWAP_INTERVAL + {{{ makeSetValue('value', '0', '0', 'i32') }}}; + return 1; + case 0x303C: // EGL_MAX_SWAP_INTERVAL + {{{ makeSetValue('value', '0', '1' /* TODO: Currently this is not strictly true, since user can specify custom presentation interval in JS requestAnimationFrame/emscripten_set_main_loop. */, 'i32') }}}; + return 1; + case 0x303D: // EGL_LUMINANCE_SIZE + case 0x303E: // EGL_ALPHA_MASK_SIZE + {{{ makeSetValue('value', '0', '0' /* N/A in this config. */, 'i32') }}}; + return 1; + case 0x303F: // EGL_COLOR_BUFFER_TYPE + // EGL has two types of buffers: EGL_RGB_BUFFER and EGL_LUMINANCE_BUFFER. + {{{ makeSetValue('value', '0', '0x308E' /* EGL_RGB_BUFFER */, 'i32') }}}; + 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. + {{{ makeSetValue('value', '0', '0x4' /* EGL_OPENGL_ES2_BIT */, 'i32') }}}; + 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." + {{{ makeSetValue('value', '0', '0' /* EGL_OPENGL_ES2_BIT */, 'i32') }}}; + return 1; + default: + EGL.setErrorCode(0x3004 /* EGL_BAD_ATTRIBUTE */); + return 0; + } + }, + + // EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list); + eglCreateWindowSurface__proxy: 'sync', + eglCreateWindowSurface: (display, config, win, attrib_list) => { + if (display != {{{ eglDefaultDisplay }}}) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + if (config != {{{ eglDefaultConfig }}}) { + 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' */ + }, + + // EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay display, EGLSurface surface); + eglDestroySurface__proxy: 'sync', + eglDestroySurface: (display, surface) => { + if (display != {{{ eglDefaultDisplay }}}) { + 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' */ + }, + + eglCreateContext__deps: ['$GL'], + + // EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list); + eglCreateContext__proxy: 'sync', + eglCreateContext: (display, config, hmm, contextAttribs) => { + if (display != {{{ eglDefaultDisplay }}}) { + 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 = {{{ makeGetValue('contextAttribs', '0', 'i32') }}}; + if (param == 0x3098 /*EGL_CONTEXT_CLIENT_VERSION*/) { + glesContextVersion = {{{ makeGetValue('contextAttribs', '4', 'i32') }}}; + } 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 MAX_WEBGL_VERSION >= 2 + if (glesContextVersion < 2 || glesContextVersion > 3) { +#else + if (glesContextVersion != 2) { +#endif +#if GL_ASSERTIONS + if (glesContextVersion == 3) { + err('When initializing GLES3/WebGL2 via EGL, one must build with -sMAX_WEBGL_VERSION=2!'); + } else { + err(`When initializing GLES2/WebGL1 via EGL, one must pass EGL_CONTEXT_CLIENT_VERSION = 2 to GL context attributes! GLES version ${glesContextVersion} is not supported!`); + } +#endif + 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 {{{ eglDefaultContext }}}; + } 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 */ + } + }, + + eglDestroyContext__deps: ['$GL'], + + // EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay dpy, EGLContext context); + eglDestroyContext__proxy: 'sync', + eglDestroyContext: (display, context) => { + if (display != {{{ eglDefaultDisplay }}}) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + if (context != {{{ eglDefaultContext }}}) { + 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 */; + }, + + // EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value); + eglQuerySurface__proxy: 'sync', + eglQuerySurface: (display, surface, attribute, value) => { + if (display != {{{ eglDefaultDisplay }}}) { + 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 + {{{ makeSetValue('value', '0', eglDefaultConfig, 'i32') }}}; + 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 + {{{ makeSetValue('value', '0', "Browser.getCanvas().width", 'i32') }}}; + return 1; + case 0x3056: // EGL_HEIGHT + {{{ makeSetValue('value', '0', "Browser.getCanvas().height", 'i32') }}}; + return 1; + case 0x3090: // EGL_HORIZONTAL_RESOLUTION + {{{ makeSetValue('value', '0', '-1' /* EGL_UNKNOWN */, 'i32') }}}; + return 1; + case 0x3091: // EGL_VERTICAL_RESOLUTION + {{{ makeSetValue('value', '0', '-1' /* EGL_UNKNOWN */, 'i32') }}}; + return 1; + case 0x3092: // EGL_PIXEL_ASPECT_RATIO + {{{ makeSetValue('value', '0', '-1' /* EGL_UNKNOWN */, 'i32') }}}; + 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. + {{{ makeSetValue('value', '0', '0x3084' /* EGL_BACK_BUFFER */, 'i32') }}}; + return 1; + case 0x3099: // EGL_MULTISAMPLE_RESOLVE + {{{ makeSetValue('value', '0', '0x309A' /* EGL_MULTISAMPLE_RESOLVE_DEFAULT */, 'i32') }}}; + 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. + {{{ makeSetValue('value', '0', '0x3095' /* EGL_BUFFER_DESTROYED */, 'i32') }}}; + 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; + } + }, + + // EGLAPI EGLBoolean EGLAPIENTRY eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value); + eglQueryContext__proxy: 'sync', + eglQueryContext: (display, context, attribute, value) => { + if (display != {{{ eglDefaultDisplay }}}) { + 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 != {{{ eglDefaultContext }}}) { + 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 + {{{ makeSetValue('value', '0', eglDefaultConfig, 'i32') }}}; + return 1; + case 0x3097: // EGL_CONTEXT_CLIENT_TYPE + {{{ makeSetValue('value', '0', '0x30A0' /* EGL_OPENGL_ES_API */, 'i32') }}}; + return 1; + case 0x3098: // EGL_CONTEXT_CLIENT_VERSION + {{{ makeSetValue('value', '0', 'EGL.contextAttributes.majorVersion + 1', 'i32') }}}; + 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. + {{{ makeSetValue('value', '0', '0x3084' /* EGL_BACK_BUFFER */, 'i32') }}}; + return 1; + default: + EGL.setErrorCode(0x3004 /* EGL_BAD_ATTRIBUTE */); + return 0; + } + }, + + // EGLAPI EGLint EGLAPIENTRY eglGetError(void); + eglGetError__proxy: 'sync', + eglGetError: () => EGL.errorCode, + + // EGLAPI const char * EGLAPIENTRY eglQueryString(EGLDisplay dpy, EGLint name); + // The allocated strings are cached and never freed. + eglQueryString__noleakcheck: true, + eglQueryString__deps: ['$stringToNewUTF8'], + eglQueryString__proxy: 'sync', + eglQueryString: (display, name) => { + if (display != {{{ eglDefaultDisplay }}}) { + 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; + }, + + // EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api); + eglBindAPI__proxy: 'sync', + 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; + }, + + // EGLAPI EGLenum EGLAPIENTRY eglQueryAPI(void); + eglQueryAPI__proxy: 'sync', + eglQueryAPI: () => { + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 0x30A0; // EGL_OPENGL_ES_API + }, + + // EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient(void); + eglWaitClient__proxy: 'sync', + eglWaitClient: () => { + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 1; + }, + + // EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative(EGLint engine); + eglWaitNative__proxy: 'sync', + eglWaitNative: (nativeEngineId) => { + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 1; + }, + + + // EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL(void); + eglWaitGL: 'eglWaitClient', + + // EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval(EGLDisplay dpy, EGLint interval); + eglSwapInterval__deps: ['emscripten_set_main_loop_timing'], + eglSwapInterval__proxy: 'sync', + eglSwapInterval: (display, interval) => { + if (display != {{{ eglDefaultDisplay }}}) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + if (interval == 0) _emscripten_set_main_loop_timing({{{ cDefs.EM_TIMING_SETTIMEOUT }}}, 0); + else _emscripten_set_main_loop_timing({{{ cDefs.EM_TIMING_RAF }}}, interval); + + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 1; + }, + + // EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); + eglMakeCurrent__deps: ['$GL'], + eglMakeCurrent__proxy: 'sync', + eglMakeCurrent: (display, draw, read, context) => { + if (display != {{{ eglDefaultDisplay }}}) { + 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 != {{{ eglDefaultContext }}}) { + 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 */; + }, + + // EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext(void); + eglGetCurrentContext__proxy: 'sync', + eglGetCurrentContext: () => EGL.currentContext, + + // EGLAPI EGLSurface EGLAPIENTRY eglGetCurrentSurface(EGLint readdraw); + eglGetCurrentSurface__proxy: 'sync', + 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 */; + } + }, + + // EGLAPI EGLDisplay EGLAPIENTRY eglGetCurrentDisplay(void); + eglGetCurrentDisplay__proxy: 'sync', + eglGetCurrentDisplay: () => EGL.currentContext ? {{{ eglDefaultDisplay }}} : 0, + + // EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers(EGLDisplay dpy, EGLSurface surface); + eglSwapBuffers__deps: ['$GLctx'], + eglSwapBuffers__proxy: 'sync', + eglSwapBuffers: (dpy, surface) => { +#if PROXY_TO_WORKER + if (Browser.doSwapBuffers) Browser.doSwapBuffers(); +#endif + + 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 */; + }, + + eglReleaseThread__proxy: 'sync', + 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 */; + } +}; + +autoAddDeps(LibraryEGL, '$EGL'); + +addToLibrary(LibraryEGL); diff --git a/src/lib/libembind.js b/src/lib/libembind.js new file mode 100644 index 0000000000000..6d1704eacd707 --- /dev/null +++ b/src/lib/libembind.js @@ -0,0 +1,2245 @@ +// Copyright 2012 The Emscripten Authors. All rights reserved. +// Emscripten is available under two separate licenses, the MIT license and the +// University of Illinois/NCSA Open Source License. Both these licenses can be +// found in the LICENSE file. + +#include "libembind_shared.js" + +var LibraryEmbind = { + $UnboundTypeError: class extends Error {}, + $PureVirtualError: class extends Error {}, +#if EMBIND_AOT + $InvokerFunctions: '<<< EMBIND_AOT_INVOKERS >>>', +#endif + // If register_type is used, emval will be registered multiple times for + // different type id's, but only a single type object is needed on the JS side + // for all of them. Store the type for reuse. + $EmValType__deps: ['_emval_decref', '$Emval', '$readPointer'], + $EmValType: `{ + name: 'emscripten::val', + fromWireType: (handle) => { + var rv = Emval.toValue(handle); + __emval_decref(handle); + return rv; + }, + toWireType: (destructors, value) => Emval.toHandle(value), + readValueFromPointer: readPointer, + destructorFunction: null, // This type does not need a destructor + + // TODO: do we need a deleteObject here? write a test where + // emval is passed into JS via an interface + }`, + $EmValOptionalType__deps: ['$EmValType'], + $EmValOptionalType: '=Object.assign({optional: true}, EmValType);', + + $throwUnboundTypeError__deps: ['$registeredTypes', '$typeDependencies', '$UnboundTypeError', '$getTypeName'], + $throwUnboundTypeError: (message, types) => { + var unboundTypes = []; + var seen = {}; + function visit(type) { + if (seen[type]) { + return; + } + if (registeredTypes[type]) { + return; + } + if (typeDependencies[type]) { + typeDependencies[type].forEach(visit); + return; + } + unboundTypes.push(type); + seen[type] = true; + } + types.forEach(visit); + + throw new UnboundTypeError(`${message}: ` + unboundTypes.map(getTypeName).join([', '])); + }, + + // Creates a function overload resolution table to the given method 'methodName' in the given prototype, + // if the overload table doesn't yet exist. + $ensureOverloadTable__deps: ['$throwBindingError'], + $ensureOverloadTable: (proto, methodName, humanName) => { + if (undefined === proto[methodName].overloadTable) { + var prevFunc = proto[methodName]; + // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. + proto[methodName] = function(...args) { + // TODO This check can be removed in -O3 level "unsafe" optimizations. + if (!proto[methodName].overloadTable.hasOwnProperty(args.length)) { + throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`); + } + return proto[methodName].overloadTable[args.length].apply(this, args); + }; + // Move the previous function into the overload table. + proto[methodName].overloadTable = []; + proto[methodName].overloadTable[prevFunc.argCount] = prevFunc; + } + }, + + /* + Registers a symbol (function, class, enum, ...) as part of the Module JS object so that + hand-written code is able to access that symbol via 'Module.name'. + name: The name of the symbol that's being exposed. + value: The object itself to expose (function, class, ...) + numArguments: For functions, specifies the number of arguments the function takes in. For other types, unused and undefined. + + To implement support for multiple overloads of a function, an 'overload selector' function is used. That selector function chooses + the appropriate overload to call from an function overload table. This selector function is only used if multiple overloads are + actually registered, since it carries a slight performance penalty. */ + $exposePublicSymbol__deps: ['$ensureOverloadTable', '$throwBindingError'], + $exposePublicSymbol__docs: '/** @param {number=} numArguments */', + $exposePublicSymbol: (name, value, numArguments) => { + if (Module.hasOwnProperty(name)) { + if (undefined === numArguments || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments])) { + throwBindingError(`Cannot register public name '${name}' twice`); + } + + // We are exposing a function with the same name as an existing function. Create an overload table and a function selector + // that routes between the two. + ensureOverloadTable(Module, name, name); + if (Module[name].overloadTable.hasOwnProperty(numArguments)) { + throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`); + } + // Add the new function into the overload table. + Module[name].overloadTable[numArguments] = value; + } else { + Module[name] = value; + Module[name].argCount = numArguments; + } + }, + + $replacePublicSymbol__deps: ['$throwInternalError'], + $replacePublicSymbol__docs: '/** @param {number=} numArguments */', + $replacePublicSymbol: (name, value, numArguments) => { + if (!Module.hasOwnProperty(name)) { + throwInternalError('Replacing nonexistent public symbol'); + } + // If there's an overload table for this symbol, replace the symbol in the overload table instead. + if (undefined !== Module[name].overloadTable && undefined !== numArguments) { + Module[name].overloadTable[numArguments] = value; + } else { + Module[name] = value; + Module[name].argCount = numArguments; + } + }, + + $embindRepr: (v) => { + if (v === null) { + return 'null'; + } + var t = typeof v; + if (t === 'object' || t === 'array' || t === 'function') { + return v.toString(); + } else { + return '' + v; + } + }, + + // raw pointer -> instance + $registeredInstances: {}, + + $getBasestPointer__deps: ['$throwBindingError'], + $getBasestPointer: (class_, ptr) => { + if (ptr === undefined) { + throwBindingError('ptr should not be undefined'); + } + while (class_.baseClass) { + ptr = class_.upcast(ptr); + class_ = class_.baseClass; + } + return ptr; + }, + + $registerInheritedInstance__deps: ['$registeredInstances', '$getBasestPointer', '$throwBindingError'], + $registerInheritedInstance: (class_, ptr, instance) => { + ptr = getBasestPointer(class_, ptr); + if (registeredInstances.hasOwnProperty(ptr)) { + throwBindingError(`Tried to register registered instance: ${ptr}`); + } else { + registeredInstances[ptr] = instance; + } + }, + + $unregisterInheritedInstance__deps: ['$registeredInstances', '$getBasestPointer', '$throwBindingError'], + $unregisterInheritedInstance: (class_, ptr) => { + ptr = getBasestPointer(class_, ptr); + if (registeredInstances.hasOwnProperty(ptr)) { + delete registeredInstances[ptr]; + } else { + throwBindingError(`Tried to unregister unregistered instance: ${ptr}`); + } + }, + + $getInheritedInstance__deps: ['$registeredInstances', '$getBasestPointer'], + $getInheritedInstance: (class_, ptr) => { + ptr = getBasestPointer(class_, ptr); + return registeredInstances[ptr]; + }, + + $getInheritedInstanceCount__deps: ['$registeredInstances'], + $getInheritedInstanceCount: () => Object.keys(registeredInstances).length, + + $getLiveInheritedInstances__deps: ['$registeredInstances'], + $getLiveInheritedInstances: () => { + var rv = []; + for (var k in registeredInstances) { + if (registeredInstances.hasOwnProperty(k)) { + rv.push(registeredInstances[k]); + } + } + return rv; + }, + + // class typeID -> {pointerType: ..., constPointerType: ...} + $registeredPointers: {}, + + $registerType__deps: ['$sharedRegisterType'], + $registerType__docs: '/** @param {Object=} options */', + $registerType: function(rawType, registeredInstance, options = {}) { + return sharedRegisterType(rawType, registeredInstance, options); + }, + + _embind_register_void__deps: ['$AsciiToString', '$registerType'], + _embind_register_void: (rawType, name) => { + name = AsciiToString(name); + registerType(rawType, { + isVoid: true, // void return values can be optimized out sometimes + name, + fromWireType: () => undefined, + // TODO: assert if anything else is given? + toWireType: (destructors, o) => undefined, + }); + }, + + _embind_register_bool__docs: '/** @suppress {globalThis} */', + _embind_register_bool__deps: ['$AsciiToString', '$registerType'], + _embind_register_bool: (rawType, name, trueValue, falseValue) => { + name = AsciiToString(name); + registerType(rawType, { + name, + fromWireType: function(wt) { + // ambiguous emscripten ABI: sometimes return values are + // true or false, and sometimes integers (0 or 1) + return !!wt; + }, + toWireType: function(destructors, o) { + return o ? trueValue : falseValue; + }, + readValueFromPointer: function(pointer) { + return this.fromWireType(HEAPU8[pointer]); + }, + destructorFunction: null, // This type does not need a destructor + }); + }, + + $integerReadValueFromPointer__deps: [], + $integerReadValueFromPointer: (name, width, signed) => { + // integers are quite common, so generate very specialized functions + switch (width) { + case 1: return signed ? + (pointer) => {{{ makeGetValue('pointer', 0, 'i8') }}} : + (pointer) => {{{ makeGetValue('pointer', 0, 'u8') }}}; + case 2: return signed ? + (pointer) => {{{ makeGetValue('pointer', 0, 'i16') }}} : + (pointer) => {{{ makeGetValue('pointer', 0, 'u16') }}} + case 4: return signed ? + (pointer) => {{{ makeGetValue('pointer', 0, 'i32') }}} : + (pointer) => {{{ makeGetValue('pointer', 0, 'u32') }}} +#if WASM_BIGINT + case 8: return signed ? + (pointer) => {{{ makeGetValue('pointer', 0, 'i64') }}} : + (pointer) => {{{ makeGetValue('pointer', 0, 'u64') }}} +#endif + default: + throw new TypeError(`invalid integer width (${width}): ${name}`); + } + }, + + $enumReadValueFromPointer__deps: [], + $enumReadValueFromPointer: (name, width, signed) => { + switch (width) { + case 1: return signed ? + function(pointer) { return this.fromWireType({{{ makeGetValue('pointer', 0, 'i8') }}}) } : + function(pointer) { return this.fromWireType({{{ makeGetValue('pointer', 0, 'u8') }}}) }; + case 2: return signed ? + function(pointer) { return this.fromWireType({{{ makeGetValue('pointer', 0, 'i16') }}}) } : + function(pointer) { return this.fromWireType({{{ makeGetValue('pointer', 0, 'u16') }}}) }; + case 4: return signed ? + function(pointer) { return this.fromWireType({{{ makeGetValue('pointer', 0, 'i32') }}}) } : + function(pointer) { return this.fromWireType({{{ makeGetValue('pointer', 0, 'u32') }}}) }; + default: + throw new TypeError(`invalid integer width (${width}): ${name}`); + } + }, + + $floatReadValueFromPointer__deps: [], + $floatReadValueFromPointer: (name, width) => { + switch (width) { + case 4: return function(pointer) { + return this.fromWireType({{{ makeGetValue('pointer', 0, 'float') }}}); + }; + case 8: return function(pointer) { + return this.fromWireType({{{ makeGetValue('pointer', 0, 'double') }}}); + }; + default: + throw new TypeError(`invalid float width (${width}): ${name}`); + } + }, + +#if ASSERTIONS + $assertIntegerRange__deps: ['$embindRepr'], + $assertIntegerRange: (typeName, value, minRange, maxRange) => { + if (value < minRange || value > maxRange) { + throw new TypeError(`Passing a number "${embindRepr(value)}" from JS side to C/C++ side to an argument of type "${typeName}", which is outside the valid range [${minRange}, ${maxRange}]!`); + } + }, +#endif + + _embind_register_integer__docs: '/** @suppress {globalThis} */', + // When converting a number from JS to C++ side, the valid range of the number is + // [minRange, maxRange], inclusive. + _embind_register_integer__deps: [ + '$integerReadValueFromPointer', '$AsciiToString', '$registerType', +#if ASSERTIONS + '$embindRepr', + '$assertIntegerRange', +#endif + ], + _embind_register_integer: (primitiveType, name, size, minRange, maxRange) => { + name = AsciiToString(name); + + const isUnsignedType = minRange === 0; + + let fromWireType = (value) => value; + if (isUnsignedType) { + var bitshift = 32 - 8*size; + fromWireType = (value) => (value << bitshift) >>> bitshift; + maxRange = fromWireType(maxRange); + } + + registerType(primitiveType, { + name, + fromWireType: fromWireType, + toWireType: (destructors, value) => { +#if ASSERTIONS + if (typeof value != "number" && typeof value != "boolean") { + throw new TypeError(`Cannot convert "${embindRepr(value)}" to ${name}`); + } + assertIntegerRange(name, value, minRange, maxRange); + #endif + // The VM will perform JS to Wasm value conversion, according to the spec: + // https://www.w3.org/TR/wasm-js-api-1/#towebassemblyvalue + return value; + }, + readValueFromPointer: integerReadValueFromPointer(name, size, minRange !== 0), + destructorFunction: null, // This type does not need a destructor + }); + }, + +#if WASM_BIGINT + _embind_register_bigint__docs: '/** @suppress {globalThis} */', + _embind_register_bigint__deps: [ + '$AsciiToString', '$registerType', '$integerReadValueFromPointer', +#if ASSERTIONS + '$embindRepr', + '$assertIntegerRange', +#endif + ], + _embind_register_bigint: (primitiveType, name, size, minRange, maxRange) => { + name = AsciiToString(name); + + const isUnsignedType = minRange === 0n; + + let fromWireType = (value) => value; + if (isUnsignedType) { + // uint64 get converted to int64 in ABI, fix them up like we do for 32-bit integers. + const bitSize = size * 8; + fromWireType = (value) => { +#if MEMORY64 + // FIXME(https://github.com/emscripten-core/emscripten/issues/16975) + // `size_t` ends up here, but it's transferred in the ABI as a plain number instead of a bigint. + if (typeof value == 'number') { + return value >>> 0; + } +#endif + return BigInt.asUintN(bitSize, value); + } + maxRange = fromWireType(maxRange); + } + + registerType(primitiveType, { + name, + fromWireType: fromWireType, + toWireType: (destructors, value) => { + if (typeof value == "number") { + value = BigInt(value); + } +#if ASSERTIONS + else if (typeof value != "bigint") { + throw new TypeError(`Cannot convert "${embindRepr(value)}" to ${this.name}`); + } + assertIntegerRange(name, value, minRange, maxRange); +#endif + return value; + }, + readValueFromPointer: integerReadValueFromPointer(name, size, !isUnsignedType), + destructorFunction: null, // This type does not need a destructor + }); + }, +#else + _embind_register_bigint__deps: [], + _embind_register_bigint: (primitiveType, name, size, minRange, maxRange) => {}, +#endif + + _embind_register_float__deps: [ + '$floatReadValueFromPointer', '$AsciiToString', '$registerType', +#if ASSERTIONS + '$embindRepr', +#endif + ], + _embind_register_float: (rawType, name, size) => { + name = AsciiToString(name); + registerType(rawType, { + name, + fromWireType: (value) => value, + toWireType: (destructors, value) => { +#if ASSERTIONS + if (typeof value != "number" && typeof value != "boolean") { + throw new TypeError(`Cannot convert ${embindRepr(value)} to ${this.name}`); + } +#endif + // The VM will perform JS to Wasm value conversion, according to the spec: + // https://www.w3.org/TR/wasm-js-api-1/#towebassemblyvalue + return value; + }, + readValueFromPointer: floatReadValueFromPointer(name, size), + destructorFunction: null, // This type does not need a destructor + }); + }, + + $readPointer__docs: '/** @suppress {globalThis} */', + $readPointer: function(pointer) { + return this.fromWireType({{{ makeGetValue('pointer', '0', '*') }}}); + }, + + _embind_register_std_string__deps: [ + '$AsciiToString', '$registerType', + '$readPointer', '$throwBindingError', + '$stringToUTF8', '$lengthBytesUTF8', 'malloc', 'free'], + _embind_register_std_string: (rawType, name) => { + name = AsciiToString(name); + var stdStringIsUTF8 = {{{ EMBIND_STD_STRING_IS_UTF8 }}}; + + registerType(rawType, { + name, + // For some method names we use string keys here since they are part of + // the public/external API and/or used by the runtime-generated code. + fromWireType(value) { + var length = {{{ makeGetValue('value', '0', '*') }}}; + var payload = value + {{{ POINTER_SIZE }}}; + + var str; + if (stdStringIsUTF8) { + str = UTF8ToString(payload, length, true); + } else { + str = ''; + for (var i = 0; i < length; ++i) { + str += String.fromCharCode(HEAPU8[payload + i]); + } + } + + _free(value); + + return str; + }, + toWireType(destructors, value) { + if (value instanceof ArrayBuffer) { + value = new Uint8Array(value); + } + + var length; + var valueIsOfTypeString = (typeof value == 'string'); + + // We accept `string` or array views with single byte elements + if (!(valueIsOfTypeString || (ArrayBuffer.isView(value) && value.BYTES_PER_ELEMENT == 1))) { + throwBindingError('Cannot pass non-string to std::string'); + } + if (stdStringIsUTF8 && valueIsOfTypeString) { + length = lengthBytesUTF8(value); + } else { + length = value.length; + } + + // assumes POINTER_SIZE alignment + var base = _malloc({{{ POINTER_SIZE }}} + length + 1); + var ptr = base + {{{ POINTER_SIZE }}}; + {{{ makeSetValue('base', '0', 'length', SIZE_TYPE) }}}; + if (valueIsOfTypeString) { + if (stdStringIsUTF8) { + stringToUTF8(value, ptr, length + 1); + } else { + for (var i = 0; i < length; ++i) { + var charCode = value.charCodeAt(i); + if (charCode > 255) { + _free(base); + throwBindingError('String has UTF-16 code units that do not fit in 8 bits'); + } + HEAPU8[ptr + i] = charCode; + } + } + } else { + HEAPU8.set(value, ptr); + } + + if (destructors !== null) { + destructors.push(_free, base); + } + return base; + }, + readValueFromPointer: readPointer, + destructorFunction(ptr) { + _free(ptr); + }, + }); + }, + + _embind_register_std_wstring__deps: [ + '$AsciiToString', '$registerType', '$readPointer', + '$UTF16ToString', '$stringToUTF16', '$lengthBytesUTF16', + '$UTF32ToString', '$stringToUTF32', '$lengthBytesUTF32', + ], + _embind_register_std_wstring: (rawType, charSize, name) => { + name = AsciiToString(name); + var decodeString, encodeString, lengthBytesUTF; + if (charSize === 2) { + decodeString = UTF16ToString; + encodeString = stringToUTF16; + lengthBytesUTF = lengthBytesUTF16; + } else { +#if ASSERTIONS + assert(charSize === 4, 'only 2-byte and 4-byte strings are currently supported'); +#endif + decodeString = UTF32ToString; + encodeString = stringToUTF32; + lengthBytesUTF = lengthBytesUTF32; + } + registerType(rawType, { + name, + fromWireType: (value) => { + // Code mostly taken from _embind_register_std_string fromWireType + var length = {{{ makeGetValue('value', 0, '*') }}}; + var str = decodeString(value + {{{ POINTER_SIZE }}}, length * charSize, true); + + _free(value); + + return str; + }, + toWireType: (destructors, value) => { + if (!(typeof value == 'string')) { + throwBindingError(`Cannot pass non-string to C++ string type ${name}`); + } + + // assumes POINTER_SIZE alignment + var length = lengthBytesUTF(value); + var ptr = _malloc({{{ POINTER_SIZE }}} + length + charSize); + {{{ makeSetValue('ptr', '0', 'length / charSize', SIZE_TYPE) }}}; + + encodeString(value, ptr + {{{ POINTER_SIZE }}}, length + charSize); + + if (destructors !== null) { + destructors.push(_free, ptr); + } + return ptr; + }, + readValueFromPointer: readPointer, + destructorFunction(ptr) { + _free(ptr); + } + }); + }, + + _embind_register_emval__deps: [ + '$registerType', '$EmValType'], + _embind_register_emval: (rawType) => registerType(rawType, EmValType), + + _embind_register_user_type__deps: ['_embind_register_emval'], + _embind_register_user_type: (rawType, name) => { + __embind_register_emval(rawType); + }, + + _embind_register_optional__deps: ['$registerType', '$EmValOptionalType'], + _embind_register_optional: (rawOptionalType, rawType) => { + registerType(rawOptionalType, EmValOptionalType); + }, + + _embind_register_memory_view__deps: ['$AsciiToString', '$registerType'], + _embind_register_memory_view: (rawType, dataTypeIndex, name) => { + var typeMapping = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, +#if WASM_BIGINT + BigInt64Array, + BigUint64Array, +#endif + ]; + + var TA = typeMapping[dataTypeIndex]; + + function decodeMemoryView(handle) { + var size = {{{ makeGetValue('handle', 0, '*') }}}; + var data = {{{ makeGetValue('handle', POINTER_SIZE, '*') }}}; + return new TA(HEAP8.buffer, data, size); + } + + name = AsciiToString(name); + registerType(rawType, { + name, + fromWireType: decodeMemoryView, + readValueFromPointer: decodeMemoryView, + }, { + ignoreDuplicateRegistrations: true, + }); + }, + + $runDestructors: (destructors) => { + while (destructors.length) { + var ptr = destructors.pop(); + var del = destructors.pop(); + del(ptr); + } + }, + + // The path to interop from JS code to C++ code: + // (hand-written JS code) -> (autogenerated JS invoker) -> (template-generated C++ invoker) -> (target C++ function) + // craftInvokerFunction generates the JS invoker function for each function exposed to JS through embind. + $craftInvokerFunction__deps: [ + '$createNamedFunction', '$runDestructors', '$throwBindingError', '$usesDestructorStack', +#if DYNAMIC_EXECUTION && !EMBIND_AOT + '$createJsInvoker', +#endif +#if EMBIND_AOT + '$InvokerFunctions', + '$createJsInvokerSignature', +#endif +#if ASYNCIFY == 1 + '$Asyncify', +#endif +#if ASSERTIONS + '$getRequiredArgCount', + '$checkArgCount', +#endif + ], + $craftInvokerFunction: function(humanName, argTypes, classType, cppInvokerFunc, cppTargetFunc, /** boolean= */ isAsync) { + // humanName: a human-readable string name for the function to be generated. + // argTypes: An array that contains the embind type objects for all types in the function signature. + // argTypes[0] is the type object for the function return value. + // argTypes[1] is the type object for function this object/class type, or null if not crafting an invoker for a class method. + // argTypes[2...] are the actual function parameters. + // classType: The embind type object for the class to be bound, or null if this is not a method of a class. + // cppInvokerFunc: JS Function object to the C++-side function that interops into C++ code. + // cppTargetFunc: Function pointer (an integer to FUNCTION_TABLE) to the target C++ function the cppInvokerFunc will end up calling. + // isAsync: Optional. If true, returns an async function. Async bindings are only supported with JSPI. + var argCount = argTypes.length; + + if (argCount < 2) { + throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!"); + } + +#if ASSERTIONS && ASYNCIFY != 2 + assert(!isAsync, 'Async bindings are only supported with JSPI.'); +#endif + var isClassMethodFunc = (argTypes[1] !== null && classType !== null); + + // Free functions with signature "void function()" do not need an invoker that marshalls between wire types. + // TODO: This omits argument count check - enable only at -O3 or similar. + // if (ENABLE_UNSAFE_OPTS && argCount == 2 && argTypes[0].name == "void" && !isClassMethodFunc) { + // return FUNCTION_TABLE[fn]; + // } + + + // Determine if we need to use a dynamic stack to store the destructors for the function parameters. + // TODO: Remove this completely once all function invokers are being dynamically generated. + var needsDestructorStack = usesDestructorStack(argTypes); + + var returns = !argTypes[0].isVoid; + + var expectedArgCount = argCount - 2; +#if ASSERTIONS + var minArgs = getRequiredArgCount(argTypes); +#endif +#if DYNAMIC_EXECUTION == 0 && !EMBIND_AOT + var argsWired = new Array(expectedArgCount); + var invokerFuncArgs = []; + var destructors = []; + var invokerFn = function(...args) { +#if ASSERTIONS + checkArgCount(args.length, minArgs, expectedArgCount, humanName, throwBindingError); +#endif +#if EMSCRIPTEN_TRACING + Module.emscripten_trace_enter_context(`embind::${humanName}`); +#endif + destructors.length = 0; + var thisWired; + invokerFuncArgs.length = isClassMethodFunc ? 2 : 1; + invokerFuncArgs[0] = cppTargetFunc; + if (isClassMethodFunc) { + thisWired = argTypes[1].toWireType(destructors, this); + invokerFuncArgs[1] = thisWired; + } + for (var i = 0; i < expectedArgCount; ++i) { + argsWired[i] = argTypes[i + 2].toWireType(destructors, args[i]); + invokerFuncArgs.push(argsWired[i]); + } + + var rv = cppInvokerFunc(...invokerFuncArgs); + + function onDone(rv) { + if (needsDestructorStack) { + runDestructors(destructors); + } else { + for (var i = isClassMethodFunc ? 1 : 2; i < argTypes.length; i++) { + var param = i === 1 ? thisWired : argsWired[i - 2]; + if (argTypes[i].destructorFunction !== null) { + argTypes[i].destructorFunction(param); + } + } + } + + #if EMSCRIPTEN_TRACING + Module.emscripten_trace_exit_context(); + #endif + + if (returns) { + return argTypes[0].fromWireType(rv); + } + } + +#if ASYNCIFY == 1 + if (Asyncify.currData) { + return Asyncify.whenDone().then(onDone); + } +#elif ASYNCIFY == 2 + if (isAsync) { + return rv.then(onDone); + } +#endif + + return onDone(rv); + }; +#else + // Builld the arguments that will be passed into the closure around the invoker + // function. + var retType = argTypes[0]; + var instType = argTypes[1]; + var closureArgs = [humanName, throwBindingError, cppInvokerFunc, cppTargetFunc, runDestructors, retType.fromWireType.bind(retType), instType?.toWireType.bind(instType)]; +#if EMSCRIPTEN_TRACING + closureArgs.push(Module); +#endif + for (var i = 2; i < argCount; ++i) { + var argType = argTypes[i]; + closureArgs.push(argType.toWireType.bind(argType)); + } +#if ASYNCIFY == 1 + closureArgs.push(Asyncify); +#endif + if (!needsDestructorStack) { + // Skip return value at index 0 - it's not deleted here. Also skip class type if not a method. + for (var i = isClassMethodFunc?1:2; i < argTypes.length; ++i) { + if (argTypes[i].destructorFunction !== null) { + closureArgs.push(argTypes[i].destructorFunction); + } + } + } +#if ASSERTIONS + closureArgs.push(checkArgCount, minArgs, expectedArgCount); +#endif + +#if EMBIND_AOT + var signature = createJsInvokerSignature(argTypes, isClassMethodFunc, returns, isAsync); + var invokerFn = InvokerFunctions[signature](...closureArgs); +#else + + let invokerFactory = createJsInvoker(argTypes, isClassMethodFunc, returns, isAsync); + var invokerFn = invokerFactory(...closureArgs); +#endif +#endif + return createNamedFunction(humanName, invokerFn); + }, + + $embind__requireFunction__deps: ['$AsciiToString', '$throwBindingError' +#if DYNCALLS || !WASM_BIGINT || MEMORY64 || CAN_ADDRESS_2GB + , '$getDynCaller' +#endif + ], + $embind__requireFunction: (signature, rawFunction, isAsync = false) => { +#if ASSERTIONS && ASYNCIFY != 2 + assert(!isAsync, 'Async bindings are only supported with JSPI.'); +#endif + + signature = AsciiToString(signature); + + function makeDynCaller() { +#if DYNCALLS + return getDynCaller(signature, rawFunction); +#else +#if !WASM_BIGINT + if (signature.includes('j')) { + return getDynCaller(signature, rawFunction); + } +#endif +#if MEMORY64 || CAN_ADDRESS_2GB + if (signature.includes('p')) { + return getDynCaller(signature, rawFunction, isAsync); + } +#endif + var rtn = getWasmTableEntry(rawFunction); +#if JSPI + if (isAsync) { + rtn = WebAssembly.promising(rtn); + } +#endif + return rtn; +#endif + } + + var fp = makeDynCaller(); + if (typeof fp != 'function') { + throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`); + } + return fp; + }, + + _embind_register_function__deps: [ + '$craftInvokerFunction', '$exposePublicSymbol', '$heap32VectorToArray', + '$AsciiToString', '$replacePublicSymbol', '$embind__requireFunction', + '$throwUnboundTypeError', '$whenDependentTypesAreResolved', '$getFunctionName'], + _embind_register_function: (name, argCount, rawArgTypesAddr, signature, rawInvoker, fn, isAsync, isNonnullReturn) => { + var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + name = AsciiToString(name); + name = getFunctionName(name); + + rawInvoker = embind__requireFunction(signature, rawInvoker, isAsync); + + exposePublicSymbol(name, function() { + throwUnboundTypeError(`Cannot call ${name} due to unbound types`, argTypes); + }, argCount - 1); + + whenDependentTypesAreResolved([], argTypes, (argTypes) => { + var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); + replacePublicSymbol(name, craftInvokerFunction(name, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn, isAsync), argCount - 1); + return []; + }); + }, + + _embind_register_value_array__deps: [ + '$tupleRegistrations', '$AsciiToString', '$embind__requireFunction'], + _embind_register_value_array: ( + rawType, + name, + constructorSignature, + rawConstructor, + destructorSignature, + rawDestructor + ) => { + tupleRegistrations[rawType] = { + name: AsciiToString(name), + rawConstructor: embind__requireFunction(constructorSignature, rawConstructor), + rawDestructor: embind__requireFunction(destructorSignature, rawDestructor), + elements: [], + }; + }, + + _embind_register_value_array_element__deps: [ + '$tupleRegistrations', '$embind__requireFunction'], + _embind_register_value_array_element: ( + rawTupleType, + getterReturnType, + getterSignature, + getter, + getterContext, + setterArgumentType, + setterSignature, + setter, + setterContext + ) => { + tupleRegistrations[rawTupleType].elements.push({ + getterReturnType, + getter: embind__requireFunction(getterSignature, getter), + getterContext, + setterArgumentType, + setter: embind__requireFunction(setterSignature, setter), + setterContext, + }); + }, + + _embind_finalize_value_array__deps: [ + '$tupleRegistrations', '$runDestructors', + '$readPointer', '$whenDependentTypesAreResolved'], + _embind_finalize_value_array: (rawTupleType) => { + var reg = tupleRegistrations[rawTupleType]; + delete tupleRegistrations[rawTupleType]; + var elements = reg.elements; + var elementsLength = elements.length; + var elementTypes = elements.map((elt) => elt.getterReturnType). + concat(elements.map((elt) => elt.setterArgumentType)); + + var rawConstructor = reg.rawConstructor; + var rawDestructor = reg.rawDestructor; + + whenDependentTypesAreResolved([rawTupleType], elementTypes, (elementTypes) => { + elements.forEach((elt, i) => { + var getterReturnType = elementTypes[i]; + var getter = elt.getter; + var getterContext = elt.getterContext; + var setterArgumentType = elementTypes[i + elementsLength]; + var setter = elt.setter; + var setterContext = elt.setterContext; + elt.read = (ptr) => getterReturnType.fromWireType(getter(getterContext, ptr)); + elt.write = (ptr, o) => { + var destructors = []; + setter(setterContext, ptr, setterArgumentType.toWireType(destructors, o)); + runDestructors(destructors); + }; + }); + + return [{ + name: reg.name, + fromWireType: (ptr) => { + var rv = new Array(elementsLength); + for (var i = 0; i < elementsLength; ++i) { + rv[i] = elements[i].read(ptr); + } + rawDestructor(ptr); + return rv; + }, + toWireType: (destructors, o) => { + if (elementsLength !== o.length) { + throw new TypeError(`Incorrect number of tuple elements for ${reg.name}: expected=${elementsLength}, actual=${o.length}`); + } + var ptr = rawConstructor(); + for (var i = 0; i < elementsLength; ++i) { + elements[i].write(ptr, o[i]); + } + if (destructors !== null) { + destructors.push(rawDestructor, ptr); + } + return ptr; + }, + readValueFromPointer: readPointer, + destructorFunction: rawDestructor, + }]; + }); + }, + + _embind_register_value_object__deps: [ + '$structRegistrations', '$AsciiToString', '$embind__requireFunction'], + _embind_register_value_object: ( + rawType, + name, + constructorSignature, + rawConstructor, + destructorSignature, + rawDestructor + ) => { + structRegistrations[rawType] = { + name: AsciiToString(name), + rawConstructor: embind__requireFunction(constructorSignature, rawConstructor), + rawDestructor: embind__requireFunction(destructorSignature, rawDestructor), + fields: [], + }; + }, + + _embind_register_value_object_field__deps: [ + '$structRegistrations', '$AsciiToString', '$embind__requireFunction'], + _embind_register_value_object_field: ( + structType, + fieldName, + getterReturnType, + getterSignature, + getter, + getterContext, + setterArgumentType, + setterSignature, + setter, + setterContext + ) => { + structRegistrations[structType].fields.push({ + fieldName: AsciiToString(fieldName), + getterReturnType, + getter: embind__requireFunction(getterSignature, getter), + getterContext, + setterArgumentType, + setter: embind__requireFunction(setterSignature, setter), + setterContext, + }); + }, + + _embind_finalize_value_object__deps: [ + '$structRegistrations', '$runDestructors', + '$readPointer', '$whenDependentTypesAreResolved'], + _embind_finalize_value_object: (structType) => { + var reg = structRegistrations[structType]; + delete structRegistrations[structType]; + + var rawConstructor = reg.rawConstructor; + var rawDestructor = reg.rawDestructor; + var fieldRecords = reg.fields; + var fieldTypes = fieldRecords.map((field) => field.getterReturnType). + concat(fieldRecords.map((field) => field.setterArgumentType)); + whenDependentTypesAreResolved([structType], fieldTypes, (fieldTypes) => { + var fields = {}; + fieldRecords.forEach((field, i) => { + var fieldName = field.fieldName; + var getterReturnType = fieldTypes[i]; + var optional = fieldTypes[i].optional; + var getter = field.getter; + var getterContext = field.getterContext; + var setterArgumentType = fieldTypes[i + fieldRecords.length]; + var setter = field.setter; + var setterContext = field.setterContext; + fields[fieldName] = { + read: (ptr) => getterReturnType.fromWireType(getter(getterContext, ptr)), + write: (ptr, o) => { + var destructors = []; + setter(setterContext, ptr, setterArgumentType.toWireType(destructors, o)); + runDestructors(destructors); + }, + optional, + }; + }); + + return [{ + name: reg.name, + fromWireType: (ptr) => { + var rv = {}; + for (var i in fields) { + rv[i] = fields[i].read(ptr); + } + rawDestructor(ptr); + return rv; + }, + toWireType: (destructors, o) => { + // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: + // assume all fields are present without checking. + for (var fieldName in fields) { + if (!(fieldName in o) && !fields[fieldName].optional) { + throw new TypeError(`Missing field: "${fieldName}"`); + } + } + var ptr = rawConstructor(); + for (fieldName in fields) { + fields[fieldName].write(ptr, o[fieldName]); + } + if (destructors !== null) { + destructors.push(rawDestructor, ptr); + } + return ptr; + }, + readValueFromPointer: readPointer, + destructorFunction: rawDestructor, + }]; + }); + }, + + $genericPointerToWireType__docs: '/** @suppress {globalThis} */', + $genericPointerToWireType__deps: ['$throwBindingError', '$upcastPointer'], + $genericPointerToWireType: function(destructors, handle) { + var ptr; + if (handle === null) { + if (this.isReference) { + throwBindingError(`null is not a valid ${this.name}`); + } + + if (this.isSmartPointer) { + ptr = this.rawConstructor(); + if (destructors !== null) { + destructors.push(this.rawDestructor, ptr); + } + return ptr; + } else { + return 0; + } + } + + if (!handle || !handle.$$) { + throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`); + } + if (!handle.$$.ptr) { + throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`); + } + if (!this.isConst && handle.$$.ptrType.isConst) { + throwBindingError(`Cannot convert argument of type ${(handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name)} to parameter type ${this.name}`); + } + var handleClass = handle.$$.ptrType.registeredClass; + ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); + + if (this.isSmartPointer) { + // TODO: this is not strictly true + // We could support BY_EMVAL conversions from raw pointers to smart pointers + // because the smart pointer can hold a reference to the handle + if (undefined === handle.$$.smartPtr) { + throwBindingError('Passing raw pointer to smart pointer is illegal'); + } + + switch (this.sharingPolicy) { + case 0: // NONE + // no upcasting + if (handle.$$.smartPtrType === this) { + ptr = handle.$$.smartPtr; + } else { + throwBindingError(`Cannot convert argument of type ${(handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name)} to parameter type ${this.name}`); + } + break; + + case 1: // INTRUSIVE + ptr = handle.$$.smartPtr; + break; + + case 2: // BY_EMVAL + if (handle.$$.smartPtrType === this) { + ptr = handle.$$.smartPtr; + } else { + var clonedHandle = handle['clone'](); + ptr = this.rawShare( + ptr, + Emval.toHandle(() => clonedHandle['delete']()) + ); + if (destructors !== null) { + destructors.push(this.rawDestructor, ptr); + } + } + break; + + default: + throwBindingError('Unsupporting sharing policy'); + } + } + return ptr; + }, + + $constNoSmartPtrRawPointerToWireType__docs: '/** @suppress {globalThis} */', + // If we know a pointer type is not going to have SmartPtr logic in it, we can + // special-case optimize it a bit (compare to genericPointerToWireType) + $constNoSmartPtrRawPointerToWireType__deps: ['$throwBindingError', '$upcastPointer', '$embindRepr'], + $constNoSmartPtrRawPointerToWireType: function(destructors, handle) { + if (handle === null) { + if (this.isReference) { + throwBindingError(`null is not a valid ${this.name}`); + } + return 0; + } + + if (!handle.$$) { + throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`); + } + if (!handle.$$.ptr) { + throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`); + } + var handleClass = handle.$$.ptrType.registeredClass; + var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); + return ptr; + }, + + $nonConstNoSmartPtrRawPointerToWireType__docs: '/** @suppress {globalThis} */', + // An optimized version for non-const method accesses - there we must additionally restrict that + // the pointer is not a const-pointer. + $nonConstNoSmartPtrRawPointerToWireType__deps: ['$throwBindingError', '$upcastPointer', '$embindRepr'], + $nonConstNoSmartPtrRawPointerToWireType: function(destructors, handle) { + if (handle === null) { + if (this.isReference) { + throwBindingError(`null is not a valid ${this.name}`); + } + return 0; + } + + if (!handle.$$) { + throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`); + } + if (!handle.$$.ptr) { + throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`); + } + if (handle.$$.ptrType.isConst) { + throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`); + } + var handleClass = handle.$$.ptrType.registeredClass; + var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); + return ptr; + }, + + $init_RegisteredPointer__deps: [ + '$RegisteredPointer', + '$readPointer', + '$RegisteredPointer_fromWireType', + ], + $init_RegisteredPointer: () => { + Object.assign(RegisteredPointer.prototype, { + getPointee(ptr) { + if (this.rawGetPointee) { + ptr = this.rawGetPointee(ptr); + } + return ptr; + }, + destructor(ptr) { + this.rawDestructor?.(ptr); + }, + readValueFromPointer: readPointer, + fromWireType: RegisteredPointer_fromWireType, + }); + }, + + $RegisteredPointer__docs: `/** @constructor + @param {*=} pointeeType, + @param {*=} sharingPolicy, + @param {*=} rawGetPointee, + @param {*=} rawConstructor, + @param {*=} rawShare, + @param {*=} rawDestructor, + */`, + $RegisteredPointer__deps: [ + '$constNoSmartPtrRawPointerToWireType', '$genericPointerToWireType', + '$nonConstNoSmartPtrRawPointerToWireType', '$init_RegisteredPointer'], + $RegisteredPointer__postset: 'init_RegisteredPointer()', + $RegisteredPointer: function( + name, + registeredClass, + isReference, + isConst, + + // smart pointer properties + isSmartPointer, + pointeeType, + sharingPolicy, + rawGetPointee, + rawConstructor, + rawShare, + rawDestructor + ) { + this.name = name; + this.registeredClass = registeredClass; + this.isReference = isReference; + this.isConst = isConst; + + // smart pointer properties + this.isSmartPointer = isSmartPointer; + this.pointeeType = pointeeType; + this.sharingPolicy = sharingPolicy; + this.rawGetPointee = rawGetPointee; + this.rawConstructor = rawConstructor; + this.rawShare = rawShare; + this.rawDestructor = rawDestructor; + + if (!isSmartPointer && registeredClass.baseClass === undefined) { + if (isConst) { + this.toWireType = constNoSmartPtrRawPointerToWireType; + this.destructorFunction = null; + } else { + this.toWireType = nonConstNoSmartPtrRawPointerToWireType; + this.destructorFunction = null; + } + } else { + this.toWireType = genericPointerToWireType; + // Here we must leave this.destructorFunction undefined, since whether genericPointerToWireType returns + // a pointer that needs to be freed up is runtime-dependent, and cannot be evaluated at registration time. + // TODO: Create an alternative mechanism that allows removing the use of var destructors = []; array in + // craftInvokerFunction altogether. + } + }, + + $RegisteredPointer_fromWireType__docs: '/** @suppress {globalThis} */', + $RegisteredPointer_fromWireType__deps: [ + '$downcastPointer', '$registeredPointers', + '$getInheritedInstance', '$makeClassHandle', +#if MEMORY64 + '$bigintToI53Checked' +#endif + ], + $RegisteredPointer_fromWireType: function(ptr) { + // ptr is a raw pointer (or a raw smartpointer) +#if MEMORY64 + ptr = bigintToI53Checked(ptr); +#if ASSERTIONS + assert(Number.isSafeInteger(ptr)); +#endif +#endif + + // rawPointer is a maybe-null raw pointer + var rawPointer = this.getPointee(ptr); + if (!rawPointer) { + this.destructor(ptr); + return null; + } + + var registeredInstance = getInheritedInstance(this.registeredClass, rawPointer); + if (undefined !== registeredInstance) { + // JS object has been neutered, time to repopulate it + if (0 === registeredInstance.$$.count.value) { + registeredInstance.$$.ptr = rawPointer; + registeredInstance.$$.smartPtr = ptr; + return registeredInstance['clone'](); + } else { + // else, just increment reference count on existing object + // it already has a reference to the smart pointer + var rv = registeredInstance['clone'](); + this.destructor(ptr); + return rv; + } + } + + function makeDefaultHandle() { + if (this.isSmartPointer) { + return makeClassHandle(this.registeredClass.instancePrototype, { + ptrType: this.pointeeType, + ptr: rawPointer, + smartPtrType: this, + smartPtr: ptr, + }); + } else { + return makeClassHandle(this.registeredClass.instancePrototype, { + ptrType: this, + ptr, + }); + } + } + + var actualType = this.registeredClass.getActualType(rawPointer); + var registeredPointerRecord = registeredPointers[actualType]; + if (!registeredPointerRecord) { + return makeDefaultHandle.call(this); + } + + var toType; + if (this.isConst) { + toType = registeredPointerRecord.constPointerType; + } else { + toType = registeredPointerRecord.pointerType; + } + var dp = downcastPointer( + rawPointer, + this.registeredClass, + toType.registeredClass); + if (dp === null) { + return makeDefaultHandle.call(this); + } + if (this.isSmartPointer) { + return makeClassHandle(toType.registeredClass.instancePrototype, { + ptrType: toType, + ptr: dp, + smartPtrType: this, + smartPtr: ptr, + }); + } else { + return makeClassHandle(toType.registeredClass.instancePrototype, { + ptrType: toType, + ptr: dp, + }); + } + }, + + $runDestructor: ($$) => { + if ($$.smartPtr) { + $$.smartPtrType.rawDestructor($$.smartPtr); + } else { + $$.ptrType.registeredClass.rawDestructor($$.ptr); + } + }, + + $releaseClassHandle__deps: ['$runDestructor'], + $releaseClassHandle: ($$) => { + $$.count.value -= 1; + var toDelete = 0 === $$.count.value; + if (toDelete) { + runDestructor($$); + } + }, + + $finalizationRegistry: false, + + $detachFinalizer_deps: ['$finalizationRegistry'], + $detachFinalizer: (handle) => {}, + + $attachFinalizer__deps: [ + '$finalizationRegistry', '$detachFinalizer', '$releaseClassHandle', +#if ASSERTIONS + '$RegisteredPointer_fromWireType' +#endif + ], + $attachFinalizer: (handle) => { + if (!globalThis.FinalizationRegistry) { + attachFinalizer = (handle) => handle; + return handle; + } + // If the running environment has a FinalizationRegistry (see + // https://github.com/tc39/proposal-weakrefs), then attach finalizers + // for class handles. We check for the presence of FinalizationRegistry + // at run-time, not build-time. + finalizationRegistry = new FinalizationRegistry((info) => { +#if ASSERTIONS + console.warn(info.leakWarning); +#endif + releaseClassHandle(info.$$); + }); + attachFinalizer = (handle) => { + var $$ = handle.$$; + var hasSmartPtr = !!$$.smartPtr; + if (hasSmartPtr) { + // We should not call the destructor on raw pointers in case other code expects the pointee to live + var info = { $$: $$ }; +#if ASSERTIONS + // Create a warning as an Error instance in advance so that we can store + // the current stacktrace and point to it when / if a leak is detected. + // This is more useful than the empty stacktrace of `FinalizationRegistry` + // callback. + var cls = $$.ptrType.registeredClass; + var err = new Error(`Embind found a leaked C++ instance ${cls.name} <${ptrToString($$.ptr)}>.\n` + + "We'll free it automatically in this case, but this functionality is not reliable across various environments.\n" + + "Make sure to invoke .delete() manually once you're done with the instance instead.\n" + + "Originally allocated"); // `.stack` will add "at ..." after this sentence + if ('captureStackTrace' in Error) { + Error.captureStackTrace(err, RegisteredPointer_fromWireType); + } + info.leakWarning = err.stack.replace(/^Error: /, ''); +#endif + finalizationRegistry.register(handle, info, handle); + } + return handle; + }; + detachFinalizer = (handle) => finalizationRegistry.unregister(handle); + return attachFinalizer(handle); + }, + + $makeClassHandle__deps: ['$throwInternalError', '$attachFinalizer'], + $makeClassHandle: (prototype, record) => { + if (!record.ptrType || !record.ptr) { + throwInternalError('makeClassHandle requires ptr and ptrType'); + } + var hasSmartPtrType = !!record.smartPtrType; + var hasSmartPtr = !!record.smartPtr; + if (hasSmartPtrType !== hasSmartPtr) { + throwInternalError('Both smartPtrType and smartPtr must be specified'); + } + record.count = { value: 1 }; + return attachFinalizer(Object.create(prototype, { + $$: { + value: record, + writable: true, + }, + })); + }, + + $init_ClassHandle__deps: [ + '$ClassHandle', + '$shallowCopyInternalPointer', + '$throwInstanceAlreadyDeleted', + '$attachFinalizer', + '$releaseClassHandle', + '$throwBindingError', + '$detachFinalizer', + '$flushPendingDeletes', + '$delayFunction', + ], + $init_ClassHandle: () => { + let proto = ClassHandle.prototype; + + Object.assign(proto, { + "isAliasOf"(other) { + if (!(this instanceof ClassHandle)) { + return false; + } + if (!(other instanceof ClassHandle)) { + return false; + } + + var leftClass = this.$$.ptrType.registeredClass; + var left = this.$$.ptr; + other.$$ = /** @type {Object} */ (other.$$); + var rightClass = other.$$.ptrType.registeredClass; + var right = other.$$.ptr; + + while (leftClass.baseClass) { + left = leftClass.upcast(left); + leftClass = leftClass.baseClass; + } + + while (rightClass.baseClass) { + right = rightClass.upcast(right); + rightClass = rightClass.baseClass; + } + + return leftClass === rightClass && left === right; + }, + + "clone"() { + if (!this.$$.ptr) { + throwInstanceAlreadyDeleted(this); + } + + if (this.$$.preservePointerOnDelete) { + this.$$.count.value += 1; + return this; + } else { + var clone = attachFinalizer(Object.create(Object.getPrototypeOf(this), { + $$: { + value: shallowCopyInternalPointer(this.$$), + } + })); + + clone.$$.count.value += 1; + clone.$$.deleteScheduled = false; + return clone; + } + }, + + "delete"() { + if (!this.$$.ptr) { + throwInstanceAlreadyDeleted(this); + } + + if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) { + throwBindingError('Object already scheduled for deletion'); + } + + detachFinalizer(this); + releaseClassHandle(this.$$); + + if (!this.$$.preservePointerOnDelete) { + this.$$.smartPtr = undefined; + this.$$.ptr = undefined; + } + }, + + "isDeleted"() { + return !this.$$.ptr; + }, + + "deleteLater"() { + if (!this.$$.ptr) { + throwInstanceAlreadyDeleted(this); + } + if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) { + throwBindingError('Object already scheduled for deletion'); + } + deletionQueue.push(this); + if (deletionQueue.length === 1 && delayFunction) { + delayFunction(flushPendingDeletes); + } + this.$$.deleteScheduled = true; + return this; + }, + }); + + // Support `using ...` from https://github.com/tc39/proposal-explicit-resource-management. + const symbolDispose = Symbol.dispose; + if (symbolDispose) { + proto[symbolDispose] = proto['delete']; + } + }, + + $ClassHandle__docs: '/** @constructor */', + $ClassHandle__deps: ['$init_ClassHandle'], + $ClassHandle__postset: 'init_ClassHandle()', + // root of all pointer and smart pointer handles in embind + $ClassHandle: function() { + }, + + $throwInstanceAlreadyDeleted__deps: ['$throwBindingError'], + $throwInstanceAlreadyDeleted: (obj) => { + function getInstanceTypeName(handle) { + return handle.$$.ptrType.registeredClass.name; + } + throwBindingError(getInstanceTypeName(obj) + ' instance already deleted'); + }, + + $deletionQueue: [], + + $flushPendingDeletes__deps: ['$deletionQueue'], + $flushPendingDeletes: () => { + while (deletionQueue.length) { + var obj = deletionQueue.pop(); + obj.$$.deleteScheduled = false; + obj['delete'](); + } + }, + + $delayFunction: undefined, + + $setDelayFunction__deps: ['$delayFunction', '$deletionQueue', '$flushPendingDeletes'], + $setDelayFunction: (fn) => { + delayFunction = fn; + if (deletionQueue.length && delayFunction) { + delayFunction(flushPendingDeletes); + } + }, + + $RegisteredClass__docs: '/** @constructor */', + $RegisteredClass: function(name, + constructor, + instancePrototype, + rawDestructor, + baseClass, + getActualType, + upcast, + downcast) { + this.name = name; + this.constructor = constructor; + this.instancePrototype = instancePrototype; + this.rawDestructor = rawDestructor; + this.baseClass = baseClass; + this.getActualType = getActualType; + this.upcast = upcast; + this.downcast = downcast; + this.pureVirtualFunctions = []; + }, + + $shallowCopyInternalPointer: (o) => { + return { + count: o.count, + deleteScheduled: o.deleteScheduled, + preservePointerOnDelete: o.preservePointerOnDelete, + ptr: o.ptr, + ptrType: o.ptrType, + smartPtr: o.smartPtr, + smartPtrType: o.smartPtrType, + }; + }, + + _embind_register_class__deps: [ + '$BindingError', '$ClassHandle', '$createNamedFunction', + '$registeredPointers', '$exposePublicSymbol', + '$makeLegalFunctionName', '$AsciiToString', + '$RegisteredClass', '$RegisteredPointer', '$replacePublicSymbol', + '$embind__requireFunction', '$throwUnboundTypeError', + '$whenDependentTypesAreResolved'], + _embind_register_class: (rawType, + rawPointerType, + rawConstPointerType, + baseClassRawType, + getActualTypeSignature, + getActualType, + upcastSignature, + upcast, + downcastSignature, + downcast, + name, + destructorSignature, + rawDestructor) => { + name = AsciiToString(name); + getActualType = embind__requireFunction(getActualTypeSignature, getActualType); + upcast &&= embind__requireFunction(upcastSignature, upcast); + downcast &&= embind__requireFunction(downcastSignature, downcast); + rawDestructor = embind__requireFunction(destructorSignature, rawDestructor); + var legalFunctionName = makeLegalFunctionName(name); + + exposePublicSymbol(legalFunctionName, function() { + // this code cannot run if baseClassRawType is zero + throwUnboundTypeError(`Cannot construct ${name} due to unbound types`, [baseClassRawType]); + }); + + whenDependentTypesAreResolved( + [rawType, rawPointerType, rawConstPointerType], + baseClassRawType ? [baseClassRawType] : [], + (base) => { + base = base[0]; + + var baseClass; + var basePrototype; + if (baseClassRawType) { + baseClass = base.registeredClass; + basePrototype = baseClass.instancePrototype; + } else { + basePrototype = ClassHandle.prototype; + } + + var constructor = createNamedFunction(name, function(...args) { + if (Object.getPrototypeOf(this) !== instancePrototype) { + throw new BindingError(`Use 'new' to construct ${name}`); + } + if (undefined === registeredClass.constructor_body) { + throw new BindingError(`${name} has no accessible constructor`); + } + var body = registeredClass.constructor_body[args.length]; + if (undefined === body) { + throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`); + } + return body.apply(this, args); + }); + + var instancePrototype = Object.create(basePrototype, { + constructor: { value: constructor }, + }); + + constructor.prototype = instancePrototype; + + var registeredClass = new RegisteredClass(name, + constructor, + instancePrototype, + rawDestructor, + baseClass, + getActualType, + upcast, + downcast); + + if (registeredClass.baseClass) { + // Keep track of class hierarchy. Used to allow sub-classes to inherit class functions. + registeredClass.baseClass.__derivedClasses ??= []; + + registeredClass.baseClass.__derivedClasses.push(registeredClass); + } + + var referenceConverter = new RegisteredPointer(name, + registeredClass, + true, + false, + false); + + var pointerConverter = new RegisteredPointer(name + '*', + registeredClass, + false, + false, + false); + + var constPointerConverter = new RegisteredPointer(name + ' const*', + registeredClass, + false, + true, + false); + + registeredPointers[rawType] = { + pointerType: pointerConverter, + constPointerType: constPointerConverter + }; + + replacePublicSymbol(legalFunctionName, constructor); + + return [referenceConverter, pointerConverter, constPointerConverter]; + } + ); + }, + + _embind_register_class_constructor__deps: [ + '$heap32VectorToArray', '$embind__requireFunction', + '$whenDependentTypesAreResolved', + '$craftInvokerFunction'], + _embind_register_class_constructor: ( + rawClassType, + argCount, + rawArgTypesAddr, + invokerSignature, + invoker, + rawConstructor + ) => { +#if ASSERTIONS + assert(argCount > 0); +#endif + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + invoker = embind__requireFunction(invokerSignature, invoker); + var args = [rawConstructor]; + var destructors = []; + + whenDependentTypesAreResolved([], [rawClassType], (classType) => { + classType = classType[0]; + var humanName = `constructor ${classType.name}`; + + if (undefined === classType.registeredClass.constructor_body) { + classType.registeredClass.constructor_body = []; + } + if (undefined !== classType.registeredClass.constructor_body[argCount - 1]) { + throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class '${classType.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`); + } + classType.registeredClass.constructor_body[argCount - 1] = () => { + throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`, rawArgTypes); + }; + + whenDependentTypesAreResolved([], rawArgTypes, (argTypes) => { + // Insert empty slot for context type (argTypes[1]). + argTypes.splice(1, 0, null); + classType.registeredClass.constructor_body[argCount - 1] = craftInvokerFunction(humanName, argTypes, null, invoker, rawConstructor); + return []; + }); + return []; + }); + }, + + $downcastPointer: (ptr, ptrClass, desiredClass) => { + if (ptrClass === desiredClass) { + return ptr; + } + if (undefined === desiredClass.baseClass) { + return null; // no conversion + } + + var rv = downcastPointer(ptr, ptrClass, desiredClass.baseClass); + if (rv === null) { + return null; + } + return desiredClass.downcast(rv); + }, + + $upcastPointer__deps: ['$throwBindingError'], + $upcastPointer: (ptr, ptrClass, desiredClass) => { + while (ptrClass !== desiredClass) { + if (!ptrClass.upcast) { + throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`); + } + ptr = ptrClass.upcast(ptr); + ptrClass = ptrClass.baseClass; + } + return ptr; + }, + + $validateThis__deps: ['$throwBindingError', '$upcastPointer'], + $validateThis: (this_, classType, humanName) => { + if (!(this_ instanceof Object)) { + throwBindingError(`${humanName} with invalid "this": ${this_}`); + } + if (!(this_ instanceof classType.registeredClass.constructor)) { + throwBindingError(`${humanName} incompatible with "this" of type ${this_.constructor.name}`); + } + if (!this_.$$.ptr) { + throwBindingError(`cannot call emscripten binding method ${humanName} on deleted object`); + } + + // todo: kill this + return upcastPointer(this_.$$.ptr, + this_.$$.ptrType.registeredClass, + classType.registeredClass); + }, + + _embind_register_class_function__deps: [ + '$craftInvokerFunction', '$heap32VectorToArray', '$AsciiToString', + '$embind__requireFunction', '$throwUnboundTypeError', + '$whenDependentTypesAreResolved', '$getFunctionName'], + _embind_register_class_function: (rawClassType, + methodName, + argCount, + rawArgTypesAddr, // [ReturnType, ThisType, Args...] + invokerSignature, + rawInvoker, + context, + isPureVirtual, + isAsync, + isNonnullReturn) => { + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + methodName = AsciiToString(methodName); + methodName = getFunctionName(methodName); + rawInvoker = embind__requireFunction(invokerSignature, rawInvoker, isAsync); + + whenDependentTypesAreResolved([], [rawClassType], (classType) => { + classType = classType[0]; + var humanName = `${classType.name}.${methodName}`; + + if (methodName.startsWith("@@")) { + methodName = Symbol[methodName.substring(2)]; + } + + if (isPureVirtual) { + classType.registeredClass.pureVirtualFunctions.push(methodName); + } + + function unboundTypesHandler() { + throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`, rawArgTypes); + } + + var proto = classType.registeredClass.instancePrototype; + var method = proto[methodName]; + if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount - 2)) { + // This is the first overload to be registered, OR we are replacing a + // function in the base class with a function in the derived class. + unboundTypesHandler.argCount = argCount - 2; + unboundTypesHandler.className = classType.name; + proto[methodName] = unboundTypesHandler; + } else { + // There was an existing function with the same name registered. Set up + // a function overload routing table. + ensureOverloadTable(proto, methodName, humanName); + proto[methodName].overloadTable[argCount - 2] = unboundTypesHandler; + } + + whenDependentTypesAreResolved([], rawArgTypes, (argTypes) => { + var memberFunction = craftInvokerFunction(humanName, argTypes, classType, rawInvoker, context, isAsync); + + // Replace the initial unbound-handler-stub function with the + // appropriate member function, now that all types are resolved. If + // multiple overloads are registered for this function, the function + // goes into an overload table. + if (undefined === proto[methodName].overloadTable) { + // Set argCount in case an overload is registered later + memberFunction.argCount = argCount - 2; + proto[methodName] = memberFunction; + } else { + proto[methodName].overloadTable[argCount - 2] = memberFunction; + } + + return []; + }); + return []; + }); + }, + + _embind_register_class_property__deps: [ + '$AsciiToString', '$embind__requireFunction', '$runDestructors', + '$throwBindingError', '$throwUnboundTypeError', + '$whenDependentTypesAreResolved', '$validateThis'], + _embind_register_class_property: (classType, + fieldName, + getterReturnType, + getterSignature, + getter, + getterContext, + setterArgumentType, + setterSignature, + setter, + setterContext) => { + fieldName = AsciiToString(fieldName); + getter = embind__requireFunction(getterSignature, getter); + + whenDependentTypesAreResolved([], [classType], (classType) => { + classType = classType[0]; + var humanName = `${classType.name}.${fieldName}`; + var desc = { + get() { + throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`, [getterReturnType, setterArgumentType]); + }, + enumerable: true, + configurable: true + }; + if (setter) { + desc.set = () => throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`, [getterReturnType, setterArgumentType]); + } else { + desc.set = (v) => throwBindingError(humanName + ' is a read-only property'); + } + + Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, desc); + + whenDependentTypesAreResolved( + [], + (setter ? [getterReturnType, setterArgumentType] : [getterReturnType]), + (types) => { + var getterReturnType = types[0]; + var desc = { + get() { + var ptr = validateThis(this, classType, humanName + ' getter'); + return getterReturnType.fromWireType(getter(getterContext, ptr)); + }, + enumerable: true + }; + + if (setter) { + setter = embind__requireFunction(setterSignature, setter); + var setterArgumentType = types[1]; + desc.set = function(v) { + var ptr = validateThis(this, classType, humanName + ' setter'); + var destructors = []; + setter(setterContext, ptr, setterArgumentType.toWireType(destructors, v)); + runDestructors(destructors); + }; + } + + Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, desc); + return []; + }); + + return []; + }); + }, + + _embind_register_class_class_function__deps: [ + '$craftInvokerFunction', '$ensureOverloadTable', '$heap32VectorToArray', + '$AsciiToString', '$embind__requireFunction', '$throwUnboundTypeError', + '$whenDependentTypesAreResolved', '$getFunctionName'], + _embind_register_class_class_function: (rawClassType, + methodName, + argCount, + rawArgTypesAddr, + invokerSignature, + rawInvoker, + fn, + isAsync, + isNonnullReturn) => { + var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + methodName = AsciiToString(methodName); + methodName = getFunctionName(methodName); + rawInvoker = embind__requireFunction(invokerSignature, rawInvoker, isAsync); + whenDependentTypesAreResolved([], [rawClassType], (classType) => { + classType = classType[0]; + var humanName = `${classType.name}.${methodName}`; + + function unboundTypesHandler() { + throwUnboundTypeError(`Cannot call ${humanName} due to unbound types`, rawArgTypes); + } + + if (methodName.startsWith('@@')) { + methodName = Symbol[methodName.substring(2)]; + } + + var proto = classType.registeredClass.constructor; + if (undefined === proto[methodName]) { + // This is the first function to be registered with this name. + unboundTypesHandler.argCount = argCount-1; + proto[methodName] = unboundTypesHandler; + } else { + // There was an existing function with the same name registered. Set up + // a function overload routing table. + ensureOverloadTable(proto, methodName, humanName); + proto[methodName].overloadTable[argCount-1] = unboundTypesHandler; + } + + whenDependentTypesAreResolved([], rawArgTypes, (argTypes) => { + // Replace the initial unbound-types-handler stub with the proper + // function. If multiple overloads are registered, the function handlers + // go into an overload table. + var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); + var func = craftInvokerFunction(humanName, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn, isAsync); + if (undefined === proto[methodName].overloadTable) { + func.argCount = argCount-1; + proto[methodName] = func; + } else { + proto[methodName].overloadTable[argCount-1] = func; + } + + if (classType.registeredClass.__derivedClasses) { + for (const derivedClass of classType.registeredClass.__derivedClasses) { + if (!derivedClass.constructor.hasOwnProperty(methodName)) { + // TODO: Add support for overloads + derivedClass.constructor[methodName] = func; + } + } + } + + return []; + }); + return []; + }); + }, + + _embind_register_class_class_property__deps: [ + '$AsciiToString', '$embind__requireFunction', '$runDestructors', + '$throwBindingError', '$throwUnboundTypeError', + '$whenDependentTypesAreResolved'], + _embind_register_class_class_property: (rawClassType, + fieldName, + rawFieldType, + rawFieldPtr, + getterSignature, + getter, + setterSignature, + setter) => { + fieldName = AsciiToString(fieldName); + getter = embind__requireFunction(getterSignature, getter); + + whenDependentTypesAreResolved([], [rawClassType], (classType) => { + classType = classType[0]; + var humanName = `${classType.name}.${fieldName}`; + var desc = { + get() { + throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`, [rawFieldType]); + }, + enumerable: true, + configurable: true + }; + if (setter) { + desc.set = () => { + throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`, [rawFieldType]); + }; + } else { + desc.set = (v) => { + throwBindingError(`${humanName} is a read-only property`); + }; + } + + Object.defineProperty(classType.registeredClass.constructor, fieldName, desc); + + whenDependentTypesAreResolved([], [rawFieldType], (fieldType) => { + fieldType = fieldType[0]; + var desc = { + get() { + return fieldType.fromWireType(getter(rawFieldPtr)); + }, + enumerable: true + }; + + if (setter) { + setter = embind__requireFunction(setterSignature, setter); + desc.set = (v) => { + var destructors = []; + setter(rawFieldPtr, fieldType.toWireType(destructors, v)); + runDestructors(destructors); + }; + } + + Object.defineProperty(classType.registeredClass.constructor, fieldName, desc); + return []; + }); + + return []; + }); + }, + + _embind_create_inheriting_constructor__deps: [ + '$createNamedFunction', '$Emval', + '$PureVirtualError', '$AsciiToString', + '$registerInheritedInstance', + '$requireRegisteredType', '$throwBindingError', + '$unregisterInheritedInstance', '$detachFinalizer', '$attachFinalizer'], + _embind_create_inheriting_constructor: (constructorName, wrapperType, properties) => { + constructorName = AsciiToString(constructorName); + wrapperType = requireRegisteredType(wrapperType, 'wrapper'); + properties = Emval.toValue(properties); + + var registeredClass = wrapperType.registeredClass; + var wrapperPrototype = registeredClass.instancePrototype; + var baseClass = registeredClass.baseClass; + var baseClassPrototype = baseClass.instancePrototype; + var baseConstructor = registeredClass.baseClass.constructor; + var ctor = createNamedFunction(constructorName, function(...args) { + registeredClass.baseClass.pureVirtualFunctions.forEach(function(name) { + if (this[name] === baseClassPrototype[name]) { + throw new PureVirtualError(`Pure virtual function ${name} must be implemented in JavaScript`); + } + }.bind(this)); + + Object.defineProperty(this, '__parent', { + value: wrapperPrototype + }); + this['__construct'](...args); + }); + + // It's a little nasty that we're modifying the wrapper prototype here. + + wrapperPrototype['__construct'] = function __construct(...args) { + if (this === wrapperPrototype) { + throwBindingError("Pass correct 'this' to __construct"); + } + + var inner = baseConstructor['implement'](this, ...args); + detachFinalizer(inner); + var $$ = inner.$$; + inner['notifyOnDestruction'](); + $$.preservePointerOnDelete = true; + Object.defineProperties(this, { $$: { + value: $$ + }}); + attachFinalizer(this); + registerInheritedInstance(registeredClass, $$.ptr, this); + }; + + wrapperPrototype['__destruct'] = function __destruct() { + if (this === wrapperPrototype) { + throwBindingError("Pass correct 'this' to __destruct"); + } + + detachFinalizer(this); + unregisterInheritedInstance(registeredClass, this.$$.ptr); + }; + + ctor.prototype = Object.create(wrapperPrototype); + Object.assign(ctor.prototype, properties); + return Emval.toHandle(ctor); + }, + + $char_0: '0'.charCodeAt(0), + $char_9: '9'.charCodeAt(0), + $makeLegalFunctionName__deps: ['$char_0', '$char_9'], + $makeLegalFunctionName: (name) => { +#if ASSERTIONS + assert(typeof name === 'string'); +#endif + name = name.replace(/[^a-zA-Z0-9_]/g, '$'); + var f = name.charCodeAt(0); + if (f >= char_0 && f <= char_9) { + return `_${name}`; + } + return name; + }, + + _embind_register_smart_ptr__deps: ['$RegisteredPointer', '$embind__requireFunction', '$whenDependentTypesAreResolved'], + _embind_register_smart_ptr: (rawType, + rawPointeeType, + name, + sharingPolicy, + getPointeeSignature, + rawGetPointee, + constructorSignature, + rawConstructor, + shareSignature, + rawShare, + destructorSignature, + rawDestructor) => { + name = AsciiToString(name); + rawGetPointee = embind__requireFunction(getPointeeSignature, rawGetPointee); + rawConstructor = embind__requireFunction(constructorSignature, rawConstructor); + rawShare = embind__requireFunction(shareSignature, rawShare); + rawDestructor = embind__requireFunction(destructorSignature, rawDestructor); + + whenDependentTypesAreResolved([rawType], [rawPointeeType], (pointeeType) => { + pointeeType = pointeeType[0]; + + var registeredPointer = new RegisteredPointer(name, + pointeeType.registeredClass, + false, + false, + // smart pointer properties + true, + pointeeType, + sharingPolicy, + rawGetPointee, + rawConstructor, + rawShare, + rawDestructor); + return [registeredPointer]; + }); + }, + + _embind_register_enum__docs: '/** @suppress {globalThis} */', + _embind_register_enum__deps: ['$exposePublicSymbol', '$enumReadValueFromPointer', + '$AsciiToString', '$registerType'], + _embind_register_enum: (rawType, name, size, isSigned) => { + name = AsciiToString(name); + + function ctor() {} + ctor.values = {}; + + registerType(rawType, { + name, + constructor: ctor, + fromWireType: function(c) { + return this.constructor.values[c]; + }, + toWireType: (destructors, c) => c.value, + readValueFromPointer: enumReadValueFromPointer(name, size, isSigned), + destructorFunction: null, + }); + exposePublicSymbol(name, ctor); + }, + + _embind_register_enum_value__deps: ['$createNamedFunction', '$AsciiToString', '$requireRegisteredType'], + _embind_register_enum_value: (rawEnumType, name, enumValue) => { + var enumType = requireRegisteredType(rawEnumType, 'enum'); + name = AsciiToString(name); + + var Enum = enumType.constructor; + + var Value = Object.create(enumType.constructor.prototype, { + value: {value: enumValue}, + constructor: {value: createNamedFunction(`${enumType.name}_${name}`, function() {})}, + }); + Enum.values[enumValue] = Value; + Enum[name] = Value; + }, + + _embind_register_constant__deps: ['$AsciiToString', '$whenDependentTypesAreResolved'], + _embind_register_constant: (name, type, value) => { + name = AsciiToString(name); + whenDependentTypesAreResolved([], [type], (type) => { + type = type[0]; + Module[name] = type.fromWireType(value); + return []; + }); + }, +}; + +addToLibrary(LibraryEmbind); diff --git a/src/lib/libembind_gen.js b/src/lib/libembind_gen.js new file mode 100644 index 0000000000000..7651a51ada158 --- /dev/null +++ b/src/lib/libembind_gen.js @@ -0,0 +1,880 @@ +// Copyright 2023 The Emscripten Authors. All rights reserved. +// Emscripten is available under two separate licenses, the MIT license and the +// University of Illinois/NCSA Open Source License. Both these licenses can be +// found in the LICENSE file. +#include "libembind_shared.js" + +var LibraryEmbind = { + + $moduleDefinitions: [], + // Function signatures that have already been generated for JS generation. + $emittedFunctions: 'new Set()', + + $PrimitiveType: class { + constructor(typeId, name, destructorType) { + this.typeId = typeId; + this.name = name; + this.destructorType = destructorType; + } + }, + $IntegerType: class { + constructor(typeId) { + this.typeId = typeId; + this.destructorType = 'none'; + } + }, + $Argument: class { + constructor(name, type) { + this.name = name; + this.type = type; + } + }, + $UserType: class { + constructor(typeId, name) { + this.typeId = typeId; + this.name = name; + this.destructorType = 'none'; // Same as emval. + } + }, + $OptionalType: class { + constructor(type) { + this.type = type; + this.destructorType = 'none'; // Same as emval. + } + }, + $FunctionDefinition__deps: ['$createJsInvoker', '$createJsInvokerSignature', '$emittedFunctions'], + $FunctionDefinition: class { + hasPublicSymbol = true; + constructor(name, returnType, argumentTypes, functionIndex, thisType = null, isNonnullReturn = false, isAsync = false) { + this.name = name; + this.returnType = returnType; + this.argumentTypes = argumentTypes; + this.functionIndex = functionIndex; + this.thisType = thisType; + this.isNonnullReturn = isNonnullReturn; + this.isAsync = isAsync; + } + + printSignature(nameMap, out) { + out.push('('); + const argOut = []; + // Work backwards on the arguments, so optional types can be replaced + // with TS optional params until we see the first non-optional argument. + let seenNonOptional = false; + for (let i = this.argumentTypes.length - 1; i >= 0; i--) { + const arg = this.argumentTypes[i]; + let argType; + let argName; + if (arg.type instanceof OptionalType && !seenNonOptional) { + argType = nameMap(arg.type.type); + argName = arg.name + '?'; + } else { + seenNonOptional = true; + argType = nameMap(arg.type); + argName = arg.name; + } + argOut.unshift(`${argName}: ${argType}`); + } + + out.push(argOut.join(', ')); + let returnType = this.returnType; + // Constructors can return a pointer, but it will be a non-null pointer. + // Change the return type to the class type so the TS output doesn't + // have `| null`. + if (this.isNonnullReturn && this.returnType instanceof PointerDefinition) { + returnType = this.returnType.classType; + } + returnType = nameMap(returnType, true); + if (this.isAsync) { + returnType = `Promise<${returnType}>`; + } + out.push(`): ${returnType}`); + } + + printFunction(nameMap, out) { + out.push(`${this.name}`); + this.printSignature(nameMap, out); + } + + printModuleEntry(nameMap, out) { + out.push(' '); + this.printFunction(nameMap, out); + out.push(';\n'); + } + + // Convert a type definition in this file to something that matches the type + // object in embind.js `registerType()`. + convertToEmbindType(type) { + const ret = { + name: type.name, + }; + switch (type.destructorType) { + case 'none': + ret.destructorFunction = null; + break; + case 'function': + ret.destructorFunction = true; + break; + case 'stack': + // Intentionally empty since embind uses `undefined` for this type. + break; + default: + throw new Error(`Bad destructor type '${type.destructorType}'`); + } + return ret; + } + + printJs(out) { + const argTypes = [this.convertToEmbindType(this.returnType)]; + if (this.thisType) { + argTypes.push(this.convertToEmbindType(this.thisType)); + } else { + argTypes.push(null); + } + for (const argType of this.argumentTypes) { + argTypes.push(this.convertToEmbindType(argType.type)); + } + const signature = createJsInvokerSignature(argTypes, !!this.thisType, !this.returnType.isVoid, this.isAsync) + if (emittedFunctions.has(signature)) { + return; + } + emittedFunctions.add(signature); + let invokerFactory = createJsInvoker(argTypes, !!this.thisType, !this.returnType.isVoid, this.isAsync); + out.push(`'${signature}': ${invokerFactory},`); + } + }, + $PointerDefinition: class { + constructor(classType, isConst, isSmartPointer) { + this.classType = classType; + this.isConst = isConst; + this.isSmartPointer = isSmartPointer; + this.destructorType = 'none'; + if (isSmartPointer || classType.base) { + this.destructorType = 'stack'; + } + } + }, + $ClassDefinition: class { + hasPublicSymbol = true; + constructor(typeId, name, base = null) { + this.typeId = typeId; + this.name = name; + this.methods = []; + this.staticMethods = []; + this.staticProperties = []; + this.constructors = []; + this.base = base; + this.properties = []; + this.destructorType = 'none'; + if (base) { + this.destructorType = 'stack'; + } + } + + print(nameMap, out) { + out.push(`export interface ${this.name}`); + if (this.base) { + out.push(` extends ${this.base.name}`); + } else { + out.push(' extends ClassHandle'); + } + out.push(' {\n'); + for (const property of this.properties) { + const props = []; + property.print(nameMap, props); + for (const formattedProp of props) { + out.push(` ${formattedProp};\n`); + } + } + for (const method of this.methods) { + out.push(' '); + method.printFunction(nameMap, out); + out.push(';\n'); + } + out.push('}\n\n'); + } + + printModuleEntry(nameMap, out) { + out.push(` ${this.name}: {`); + const entries = []; + for (const construct of this.constructors) { + const entry = []; + entry.push('new'); + construct.printSignature(nameMap, entry); + entries.push(entry.join('')); + } + for (const method of this.staticMethods) { + const entry = []; + method.printFunction(nameMap, entry); + entries.push(entry.join('')); + } + for (const prop of this.staticProperties) { + const entry = []; + prop.print(nameMap, entry); + entries.push(...entry); + } + if (entries.length) { + out.push('\n'); + for (const entry of entries) { + out.push(` ${entry};\n`); + } + out.push(' '); + } + out.push('};\n'); + } + + printJs(out) { + out.push(`// class ${this.name}\n`); + if (this.constructors.length) { + out.push(`// constructors\n`); + for (const construct of this.constructors) { + construct.printJs(out); + } + } + if (this.staticMethods.length) { + out.push(`// static methods\n`); + for (const method of this.staticMethods) { + method.printJs(out); + } + } + if (this.methods.length) { + out.push(`// methods\n`); + for (const method of this.methods) { + method.printJs(out); + } + } + out.push('\n'); + } + + }, + $ClassProperty: class { + constructor(type, name, readonly) { + this.type = type; + this.name = name; + this.readonly = readonly; + } + + print(nameMap, out) { + const setType = nameMap(this.type, false); + const getType = nameMap(this.type, true); + if (this.readonly || setType === getType) { + out.push(`${this.readonly ? 'readonly ' : ''}${this.name}: ${getType}`); + return; + } + // The getter/setter types don't match, so generate each get/set definition. + out.push(`get ${this.name}(): ${getType}`); + out.push(`set ${this.name}(value: ${setType})`); + } + }, + $ConstantDefinition: class { + hasPublicSymbol = true; + constructor(type, name) { + this.type = type; + this.name = name; + } + + printModuleEntry(nameMap, out) { + out.push(` ${this.name}: ${nameMap(this.type)};\n`); + } + }, + $EnumDefinition: class { + hasPublicSymbol = true; + constructor(typeId, name) { + this.typeId = typeId; + this.name = name; + this.items = []; + this.destructorType = 'none'; + } + + print(nameMap, out) { + out.push(`export interface ${this.name}Value {\n`); + out.push(' value: T;\n}\n'); + out.push(`export type ${this.name} = `); + if (this.items.length === 0) { + out.push('never/* Empty Enumerator */'); + } else { + const outItems = []; + for (const [name, value] of this.items) { + outItems.push(`${this.name}Value<${value}>`); + } + out.push(outItems.join('|')); + } + out.push(';\n\n'); + } + + printModuleEntry(nameMap, out) { + out.push(` ${this.name}: {`); + const outItems = []; + for (const [name, value] of this.items) { + outItems.push(`${name}: ${this.name}Value<${value}>`); + } + out.push(outItems.join(', ')); + out.push('};\n'); + } + }, + $ValueArrayDefinition: class { + constructor(typeId, name) { + this.typeId = typeId; + this.name = name; + this.elementTypeIds = []; + this.elements = []; + this.destructorType = 'function'; + } + + print(nameMap, out) { + out.push(`export type ${this.name} = [ `); + const outElements = []; + for (const type of this.elements) { + outElements.push(nameMap(type)); + } + out.push(outElements.join(', ')) + out.push(' ];\n\n'); + } + }, + $ValueObjectDefinition: class { + constructor(typeId, name) { + this.typeId = typeId; + this.name = name; + this.fieldTypeIds = []; + this.fieldNames = []; + this.fields = []; + this.destructorType = 'function'; + } + + print(nameMap, out) { + out.push(`export type ${this.name} = {\n`); + const outFields = []; + for (const {name, type} of this.fields) { + outFields.push(` ${name}: ${nameMap(type)}`); + } + out.push(outFields.join(',\n')) + out.push('\n};\n\n'); + } + }, + $TsPrinter__deps: ['$OptionalType', '$ClassDefinition'], + $TsPrinter: class { + constructor(definitions) { + this.definitions = definitions; + const jsString = 'EmbindString'; // Type alias for multiple types. + // The mapping is in the format of '' => ['toWireType', 'fromWireType'] + // or if the to/from wire types are the same use a single element. + this.builtInToJsName = new Map([ + ['bool', ['boolean']], + ['float', ['number']], + ['double', ['number']], +#if MEMORY64 + ['long', ['bigint']], + ['unsigned long', ['bigint']], +#endif +#if WASM_BIGINT + ['long long', ['bigint']], + ['unsigned long long', ['bigint']], +#endif + ['void', ['void']], + ['std::string', [jsString, 'string']], + ['std::basic_string', [jsString, 'string']], + ['std::wstring', ['string']], + ['std::u16string', ['string']], + ['std::u32string', ['string']], + ['emscripten::val', ['any']], + ]); + // Signal that the type alias for EmbindString is needed. + this.usedEmbindString = false; + } + + typeToJsName(type, isFromWireType = false) { + if (type instanceof IntegerType) { + return 'number'; + } + if (type instanceof PrimitiveType) { + if (!this.builtInToJsName.has(type.name)) { + throw new Error(`Missing primitive type to TS type for '${type.name}'`); + } + const [toWireType, fromWireType = toWireType] = this.builtInToJsName.get(type.name); + const tsName = isFromWireType ? fromWireType : toWireType; + if (tsName === 'EmbindString') { + this.usedEmbindString = true; + } + return tsName; + } + if (type instanceof PointerDefinition) { + return `${this.typeToJsName(type.classType, isFromWireType)} | null`; + } + if (type instanceof OptionalType) { + return `${this.typeToJsName(type.type, isFromWireType)} | undefined`; + } + return type.name; + } + + print() { + const out = []; + let hadClass = false; + for (const def of this.definitions) { + if (def instanceof ClassDefinition) { + hadClass = true; + break; + } + } + if (hadClass) { + out.push( + 'export interface ClassHandle {\n', + ' isAliasOf(other: ClassHandle): boolean;\n', + ' delete(): void;\n', + ' deleteLater(): this;\n', + ' isDeleted(): boolean;\n', + ' // @ts-ignore - If targeting lower than ESNext, this symbol might not exist.\n', + ' [Symbol.dispose](): void;\n', + ' clone(): this;\n', + '}\n', + ); + } + for (const def of this.definitions) { + if (!def.print) { + continue; + } + def.print(this.typeToJsName.bind(this), out); + } + // Print module definitions + out.push('interface EmbindModule {\n'); + for (const def of this.definitions) { + if (!def.printModuleEntry) { + continue; + } + def.printModuleEntry(this.typeToJsName.bind(this), out); + } + out.push('}\n'); + if (this.usedEmbindString) { + out.unshift('type EmbindString = ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string;\n'); + } + return out.join(''); + } + }, + + $JsPrinter: class { + constructor(definitions) { + this.definitions = definitions; + } + + print() { + const out = ['{\n']; + const publicSymbols = []; + for (const def of this.definitions) { + if (def.hasPublicSymbol) { + publicSymbols.push(def.name); + } + if (!def.printJs) { + continue; + } + def.printJs(out); + } + out.push('}\n'); + return JSON.stringify({ + 'invokers': out.join(''), + publicSymbols, + }); + } + }, + + $registerType__deps: ['$sharedRegisterType'], + $registerType: function(rawType, registeredInstance, options = {}) { + return sharedRegisterType(rawType, registeredInstance, options); + }, + $registerPrimitiveType__deps: ['$registerType', '$PrimitiveType'], + $registerPrimitiveType: (id, name, destructorType) => { + name = AsciiToString(name); + registerType(id, new PrimitiveType(id, name, destructorType)); + }, + $registerIntegerType__deps: ['$registerType', '$IntegerType'], + $registerIntegerType: (id) => { + registerType(id, new IntegerType(id)); + }, + $createFunctionDefinition__deps: ['$FunctionDefinition', '$heap32VectorToArray', '$AsciiToString', '$Argument', '$whenDependentTypesAreResolved', '$getFunctionName', '$getFunctionArgsName', '$PointerDefinition', '$ClassDefinition'], + $createFunctionDefinition: (name, argCount, rawArgTypesAddr, functionIndex, hasThis, isNonnullReturn, isAsync, cb) => { + const argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); + name = typeof name === 'string' ? name : AsciiToString(name); + + whenDependentTypesAreResolved([], argTypes, function (argTypes) { + const argsName = getFunctionArgsName(name); + name = getFunctionName(name); + const returnType = argTypes[0]; + let thisType = null; + let argStart = 1; + if (hasThis) { + thisType = argTypes[1]; + if (thisType instanceof PointerDefinition) { + thisType = argTypes[1].classType; + } + if (!(thisType instanceof ClassDefinition)) { + throw new Error('This type must be class definition for: ' + name); + } + argStart = 2; + } + if (argsName && argsName.length != (argTypes.length - hasThis - 1)) { + throw new Error('Argument names should match number of parameters.'); + } + + const args = []; + for (let i = argStart, x = 0; i < argTypes.length; i++) { + if (argsName) { + args.push(new Argument(argsName[x++], argTypes[i])); + } else { + args.push(new Argument(`_${i - argStart}`, argTypes[i])); + } + } + const funcDef = new FunctionDefinition(name, returnType, args, functionIndex, thisType, isNonnullReturn, isAsync); + cb(funcDef); + return []; + }); + }, + _embind_register_void__deps: ['$registerPrimitiveType'], + _embind_register_void: (rawType, name) => { + const voidType = new PrimitiveType(rawType, 'void', 'none'); + voidType.isVoid = true; // Match the marker property from the non-AOT mode. + registerType(rawType, voidType); + }, + _embind_register_bool__deps: ['$registerPrimitiveType'], + _embind_register_bool: (rawType, name, trueValue, falseValue) => { + registerPrimitiveType(rawType, name, 'none'); + }, + _embind_register_integer__deps: ['$registerIntegerType'], + _embind_register_integer: (primitiveType, name, size, minRange, maxRange) => { + registerIntegerType(primitiveType, name); + }, + _embind_register_bigint: (primitiveType, name, size, minRange, maxRange) => { + registerPrimitiveType(primitiveType, name, 'none'); + }, + _embind_register_float__deps: ['$registerPrimitiveType'], + _embind_register_float: (rawType, name, size) => { + registerPrimitiveType(rawType, name, 'none'); + }, + _embind_register_std_string__deps: ['$registerPrimitiveType'], + _embind_register_std_string: (rawType, name) => { + registerPrimitiveType(rawType, name, 'function'); + }, + _embind_register_std_wstring: (rawType, charSize, name) => { + registerPrimitiveType(rawType, name, 'function'); + }, + _embind_register_emval__deps: ['$registerType', '$PrimitiveType'], + _embind_register_emval: (rawType) => { + registerType(rawType, new PrimitiveType(rawType, 'emscripten::val', 'none')); + }, + _embind_register_user_type__deps: ['$registerType', '$AsciiToString', '$UserType'], + _embind_register_user_type: (rawType, name) => { + name = AsciiToString(name); + registerType(rawType, new UserType(rawType, name)); + }, + _embind_register_optional__deps: ['$OptionalType'], + _embind_register_optional: (rawOptionalType, rawType) => { + whenDependentTypesAreResolved([rawOptionalType], [rawType], function(type) { + type = type[0]; + return [new OptionalType(type)]; + }); + }, + _embind_register_memory_view: (rawType, dataTypeIndex, name) => { + // TODO + }, + _embind_register_function__deps: ['$moduleDefinitions', '$createFunctionDefinition'], + _embind_register_function: (name, argCount, rawArgTypesAddr, signature, rawInvoker, fn, isAsync, isNonnullReturn) => { + createFunctionDefinition(name, argCount, rawArgTypesAddr, fn, false, isNonnullReturn, isAsync, (funcDef) => { + moduleDefinitions.push(funcDef); + }); + }, + _embind_register_class__deps: ['$AsciiToString', '$ClassDefinition', '$whenDependentTypesAreResolved', '$moduleDefinitions', '$PointerDefinition'], + _embind_register_class: function(rawType, + rawPointerType, + rawConstPointerType, + baseClassRawType, + getActualTypeSignature, + getActualType, + upcastSignature, + upcast, + downcastSignature, + downcast, + name, + destructorSignature, + rawDestructor) { + name = AsciiToString(name); + whenDependentTypesAreResolved( + [rawType, rawPointerType, rawConstPointerType], + baseClassRawType ? [baseClassRawType] : [], + function(base) { + const hasBase = base.length; + const classDef = new ClassDefinition(rawType, name, hasBase ? base[0] : null); + moduleDefinitions.push(classDef); + + const pointer = new PointerDefinition(classDef, false, false); + const constPointer = new PointerDefinition(classDef, true, false); + return [classDef, pointer, constPointer]; + } + ); + + }, + _embind_register_class_constructor__deps: ['$whenDependentTypesAreResolved', '$createFunctionDefinition'], + _embind_register_class_constructor: function( + rawClassType, + argCount, + rawArgTypesAddr, + invokerSignature, + invoker, + rawConstructor + ) { + whenDependentTypesAreResolved([], [rawClassType], function(classType) { + classType = classType[0]; + createFunctionDefinition(`constructor ${classType.name}`, argCount, rawArgTypesAddr, rawConstructor, false, true, false, (funcDef) => { + classType.constructors.push(funcDef); + }); + return []; + }); + }, + _embind_register_class_function__deps: ['$createFunctionDefinition'], + _embind_register_class_function: function(rawClassType, + methodName, + argCount, + rawArgTypesAddr, // [ReturnType, ThisType, Args...] + invokerSignature, + rawInvoker, + context, + isPureVirtual, + isAsync, + isNonnullReturn) { + createFunctionDefinition(methodName, argCount, rawArgTypesAddr, context, true, isNonnullReturn, isAsync, (funcDef) => { + const classDef = funcDef.thisType; + classDef.methods.push(funcDef); + }); + }, + _embind_register_class_property__deps: [ + '$AsciiToString', '$whenDependentTypesAreResolved', '$ClassProperty'], + _embind_register_class_property: function(classType, + fieldName, + getterReturnType, + getterSignature, + getter, + getterContext, + setterArgumentType, + setterSignature, + setter, + setterContext) { + fieldName = AsciiToString(fieldName); + const readonly = setter === 0; + if (!(readonly || getterReturnType === setterArgumentType)) { + throw new error('Mismatched getter and setter types are not supported.'); + } + + whenDependentTypesAreResolved([], [classType], function(classType) { + classType = classType[0]; + whenDependentTypesAreResolved([], [getterReturnType], function(types) { + const prop = new ClassProperty(types[0], fieldName, readonly); + classType.properties.push(prop); + return []; + }); + return []; + }); + }, + _embind_register_class_class_function__deps: ['$createFunctionDefinition'], + _embind_register_class_class_function: function(rawClassType, + methodName, + argCount, + rawArgTypesAddr, + invokerSignature, + rawInvoker, + fn, + isAsync, + isNonnullReturn) { + whenDependentTypesAreResolved([], [rawClassType], function(classType) { + classType = classType[0]; + createFunctionDefinition(methodName, argCount, rawArgTypesAddr, fn, false, isNonnullReturn, isAsync, (funcDef) => { + classType.staticMethods.push(funcDef); + }); + return []; + }); + }, + _embind_register_class_class_property__deps: [ + '$AsciiToString', '$whenDependentTypesAreResolved', '$ClassProperty'], + _embind_register_class_class_property: (rawClassType, + fieldName, + rawFieldType, + rawFieldPtr, + getterSignature, + getter, + setterSignature, + setter) => { + fieldName = AsciiToString(fieldName); + whenDependentTypesAreResolved([], [rawClassType], function(classType) { + classType = classType[0]; + whenDependentTypesAreResolved([], [rawFieldType], function(types) { + const prop = new ClassProperty(types[0], fieldName); + classType.staticProperties.push(prop); + return []; + }); + return []; + }); + }, + // Stub function. This is called a when extending an object and not needed for TS generation. + _embind_create_inheriting_constructor: (constructorName, wrapperType, properties) => {}, + _embind_register_enum__deps: ['$AsciiToString', '$EnumDefinition', '$moduleDefinitions'], + _embind_register_enum: function(rawType, name, size, isSigned) { + name = AsciiToString(name); + const enumDef = new EnumDefinition(rawType, name); + registerType(rawType, enumDef); + moduleDefinitions.push(enumDef); + }, + _embind_register_enum_value__deps: ['$AsciiToString', '$requireRegisteredType'], + _embind_register_enum_value: function(rawEnumType, name, enumValue) { + name = AsciiToString(name); + const enumDef = requireRegisteredType(rawEnumType, name); + enumDef.items.push([name, enumValue]); + }, + _embind_register_constant__deps: ['$AsciiToString', '$ConstantDefinition', '$whenDependentTypesAreResolved', '$moduleDefinitions'], + _embind_register_constant: function(name, typeId, value) { + name = AsciiToString(name); + whenDependentTypesAreResolved([], [typeId], function(types) { + const def = new ConstantDefinition(types[0], name); + moduleDefinitions.push(def); + return []; + }); + }, + _embind_register_value_array__deps: [ + '$AsciiToString', '$ValueArrayDefinition', '$tupleRegistrations'], + _embind_register_value_array: function( + rawType, + name, + constructorSignature, + rawConstructor, + destructorSignature, + rawDestructor + ) { + name = AsciiToString(name); + const valueArray = new ValueArrayDefinition(rawType, name); + tupleRegistrations[rawType] = valueArray; + }, + _embind_register_value_array_element__deps: ['$tupleRegistrations'], + _embind_register_value_array_element: function( + rawTupleType, + getterReturnType, + getterSignature, + getter, + getterContext, + setterArgumentType, + setterSignature, + setter, + setterContext + ) { + const valueArray = tupleRegistrations[rawTupleType]; + if (getterReturnType !== setterArgumentType) { + throw new Error('Mismatched getter and setter types are not supported.'); + } + + valueArray.elementTypeIds.push(getterReturnType); + }, + _embind_finalize_value_array__deps: ['$whenDependentTypesAreResolved', '$moduleDefinitions', '$tupleRegistrations'], + _embind_finalize_value_array: function(rawTupleType) { + const valueArray = tupleRegistrations[rawTupleType]; + delete tupleRegistrations[rawTupleType]; + whenDependentTypesAreResolved([rawTupleType], valueArray.elementTypeIds, function(types) { + moduleDefinitions.push(valueArray); + valueArray.elements = types; + return [valueArray]; + }); + }, + _embind_register_value_object__deps: ['$AsciiToString', '$ValueObjectDefinition', '$structRegistrations'], + _embind_register_value_object: function( + rawType, + name, + constructorSignature, + rawConstructor, + destructorSignature, + rawDestructor + ) { + name = AsciiToString(name); + const valueObject = new ValueObjectDefinition(rawType, name); + structRegistrations[rawType] = valueObject; + }, + _embind_register_value_object_field__deps: [ + '$AsciiToString', '$structRegistrations'], + _embind_register_value_object_field: function( + structType, + fieldName, + getterReturnType, + getterSignature, + getter, + getterContext, + setterArgumentType, + setterSignature, + setter, + setterContext + ) { + const valueObject = structRegistrations[structType]; + if (getterReturnType !== setterArgumentType) { + throw new Error('Mismatched getter and setter types are not supported.'); + } + + valueObject.fieldTypeIds.push(getterReturnType); + valueObject.fieldNames.push(AsciiToString(fieldName)); + }, + _embind_finalize_value_object__deps: ['$moduleDefinitions', '$whenDependentTypesAreResolved', '$structRegistrations'], + _embind_finalize_value_object: function(structType) { + const valueObject = structRegistrations[structType]; + delete structRegistrations[structType]; + whenDependentTypesAreResolved([structType], valueObject.fieldTypeIds, function(types) { + moduleDefinitions.push(valueObject); + for (let i = 0; i < types.length; i++) { + valueObject.fields.push({ + name: valueObject.fieldNames[i], + type: types[i], + }); + } + return [valueObject]; + }); + }, + _embind_register_smart_ptr__deps: ['$whenDependentTypesAreResolved'], + _embind_register_smart_ptr: function(rawType, + rawPointeeType, + name, + sharingPolicy, + getPointeeSignature, + rawGetPointee, + constructorSignature, + rawConstructor, + shareSignature, + rawShare, + destructorSignature, + rawDestructor) { + whenDependentTypesAreResolved([rawType], [rawPointeeType], function(pointeeType) { + const smartPointer = new PointerDefinition(pointeeType[0], false, true); + return [smartPointer]; + }); + }, + + $emitOutput__deps: ['$awaitingDependencies', '$throwBindingError', '$getTypeName', '$moduleDefinitions', +#if EMBIND_AOT + '$JsPrinter', +#else + '$TsPrinter', +#endif + ], + $emitOutput__postset: () => { addAtPostCtor('emitOutput()'); }, + $emitOutput: () => { + for (const typeId in awaitingDependencies) { + throwBindingError(`Missing binding for type: '${getTypeName(typeId)}' typeId: ${typeId}`); + } +#if EMBIND_AOT + const printer = new JsPrinter(moduleDefinitions); +#else + const printer = new TsPrinter(moduleDefinitions); +#endif + const output = printer.print(); + var fs = require('fs'); + fs.writeFileSync(process.argv[2], output + '\n'); + }, + + // Stub functions used by eval, but not needed for TS generation: + $makeLegalFunctionName: () => { throw new Error('stub function should not be called'); }, + $runDestructors: () => { throw new Error('stub function should not be called'); }, + $flushPendingDeletes: () => { throw new Error('stub function should not be called'); }, + $setDelayFunction: () => { throw new Error('stub function should not be called'); }, + $PureVirtualError: () => { throw new Error('stub function should not be called'); }, +}; + +extraLibraryFuncs.push('$emitOutput'); + +addToLibrary(LibraryEmbind); diff --git a/src/lib/libembind_shared.js b/src/lib/libembind_shared.js new file mode 100644 index 0000000000000..fcc4fe1a672c4 --- /dev/null +++ b/src/lib/libembind_shared.js @@ -0,0 +1,300 @@ +// Copyright 2023 The Emscripten Authors. All rights reserved. +// Emscripten is available under two separate licenses, the MIT license and the +// University of Illinois/NCSA Open Source License. Both these licenses can be +// found in the LICENSE file. +var LibraryEmbindShared = { + $InternalError: "= class InternalError extends Error { constructor(message) { super(message); this.name = 'InternalError'; }}", + $BindingError: "= class BindingError extends Error { constructor(message) { super(message); this.name = 'BindingError'; }}", + + $throwInternalError__deps: ['$InternalError'], + $throwInternalError: (message) => { throw new InternalError(message); }, + + $throwBindingError__deps: ['$BindingError'], + $throwBindingError: (message) => { throw new BindingError(message); }, + + // typeID -> { toWireType: ..., fromWireType: ... } + $registeredTypes: {}, + + // typeID -> [callback] + $awaitingDependencies: {}, + + // typeID -> [dependentTypes] + $typeDependencies: {}, + + $tupleRegistrations: {}, + + $structRegistrations: {}, + + $sharedRegisterType__deps: [ + '$awaitingDependencies', '$registeredTypes', + '$typeDependencies', '$throwBindingError' ], + $sharedRegisterType__docs: '/** @param {Object=} options */', + $sharedRegisterType: function(rawType, registeredInstance, options = {}) { + var name = registeredInstance.name; + if (!rawType) { + throwBindingError(`type "${name}" must have a positive integer typeid pointer`); + } + if (registeredTypes.hasOwnProperty(rawType)) { + if (options.ignoreDuplicateRegistrations) { + return; + } else { + throwBindingError(`Cannot register type '${name}' twice`); + } + } + + registeredTypes[rawType] = registeredInstance; + delete typeDependencies[rawType]; + + if (awaitingDependencies.hasOwnProperty(rawType)) { + var callbacks = awaitingDependencies[rawType]; + delete awaitingDependencies[rawType]; + callbacks.forEach((cb) => cb()); + } + }, + + $whenDependentTypesAreResolved__deps: [ + '$awaitingDependencies', '$registeredTypes', + '$typeDependencies', '$throwInternalError'], + $whenDependentTypesAreResolved: (myTypes, dependentTypes, getTypeConverters) => { + myTypes.forEach((type) => typeDependencies[type] = dependentTypes); + + function onComplete(typeConverters) { + var myTypeConverters = getTypeConverters(typeConverters); + if (myTypeConverters.length !== myTypes.length) { + throwInternalError('Mismatched type converter count'); + } + for (var i = 0; i < myTypes.length; ++i) { + registerType(myTypes[i], myTypeConverters[i]); + } + } + + var typeConverters = new Array(dependentTypes.length); + var unregisteredTypes = []; + var registered = 0; + dependentTypes.forEach((dt, i) => { + if (registeredTypes.hasOwnProperty(dt)) { + typeConverters[i] = registeredTypes[dt]; + } else { + unregisteredTypes.push(dt); + if (!awaitingDependencies.hasOwnProperty(dt)) { + awaitingDependencies[dt] = []; + } + awaitingDependencies[dt].push(() => { + typeConverters[i] = registeredTypes[dt]; + ++registered; + if (registered === unregisteredTypes.length) { + onComplete(typeConverters); + } + }); + } + }); + if (0 === unregisteredTypes.length) { + onComplete(typeConverters); + } + }, + + $getTypeName__deps: ['$AsciiToString', '__getTypeName', 'free'], + $getTypeName: (type) => { + var ptr = ___getTypeName(type); + var rv = AsciiToString(ptr); + _free(ptr); + return rv; + }, + $getFunctionName__deps: [], + $getFunctionName: (signature) => { + signature = signature.trim(); + const argsIndex = signature.indexOf("("); + if (argsIndex === -1) return signature; +#if ASSERTIONS + assert(signature.endsWith(")"), "Parentheses for argument names should match."); +#endif + return signature.slice(0, argsIndex); + }, + $getFunctionArgsName__deps: [], + $getFunctionArgsName: (signature) => { + signature = signature.trim(); + const argsIndex = signature.indexOf("("); + if (argsIndex == -1) return; // Return undefined to mean we don't have any argument names +#if ASSERTIONS + assert(signature.endsWith(")"), "Parentheses for argument names should match."); +#endif + return signature.slice(argsIndex + 1, -1).replaceAll(" ", "").split(",").filter(n => n.length); + }, + $heap32VectorToArray: (count, firstElement) => { + var array = []; + for (var i = 0; i < count; i++) { + // TODO(https://github.com/emscripten-core/emscripten/issues/17310): + // Find a way to hoist the `>> 2` or `>> 3` out of this loop. + array.push({{{ makeGetValue('firstElement', `i * ${POINTER_SIZE}`, '*') }}}); + } + return array; + }, + + $requireRegisteredType__deps: [ + '$registeredTypes', '$getTypeName', '$throwBindingError'], + $requireRegisteredType: (rawType, humanName) => { + var impl = registeredTypes[rawType]; + if (undefined === impl) { + throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`); + } + return impl; + }, + + $usesDestructorStack(argTypes) { + // Skip return value at index 0 - it's not deleted here. + for (var i = 1; i < argTypes.length; ++i) { + // The type does not define a destructor function - must use dynamic stack + if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) { + return true; + } + } + return false; + }, + + // Many of the JS invoker functions are generic and can be reused for multiple + // function bindings. This function needs to match createJsInvoker and create + // a unique signature for any inputs that will create different invoker + // function outputs. + $createJsInvokerSignature(argTypes, isClassMethodFunc, returns, isAsync) { + const signature = [ + isClassMethodFunc ? 't' : 'f', + returns ? 't' : 'f', + isAsync ? 't' : 'f' + ]; + for (let i = isClassMethodFunc ? 1 : 2; i < argTypes.length; ++i) { + const arg = argTypes[i]; + let destructorSig = ''; + if (arg.destructorFunction === undefined) { + destructorSig = 'u'; + } else if (arg.destructorFunction === null) { + destructorSig = 'n'; + } else { + destructorSig = 't'; + } + signature.push(destructorSig); + } + return signature.join(''); + }, + + $checkArgCount(numArgs, minArgs, maxArgs, humanName, throwBindingError) { + if (numArgs < minArgs || numArgs > maxArgs) { + var argCountMessage = minArgs == maxArgs ? minArgs : `${minArgs} to ${maxArgs}`; + throwBindingError(`function ${humanName} called with ${numArgs} arguments, expected ${argCountMessage}`); + } + }, + + $getRequiredArgCount(argTypes) { + var requiredArgCount = argTypes.length - 2; + for (var i = argTypes.length - 1; i >= 2; --i) { + if (!argTypes[i].optional) { + break; + } + requiredArgCount--; + } + return requiredArgCount; + }, + + $createJsInvoker__deps: ['$usesDestructorStack', +#if ASSERTIONS + '$checkArgCount', +#endif + ], + $createJsInvoker(argTypes, isClassMethodFunc, returns, isAsync) { + var needsDestructorStack = usesDestructorStack(argTypes); + var argCount = argTypes.length - 2; + var argsList = []; + var argsListWired = ['fn']; + if (isClassMethodFunc) { + argsListWired.push('thisWired'); + } + for (var i = 0; i < argCount; ++i) { + argsList.push(`arg${i}`) + argsListWired.push(`arg${i}Wired`) + } + argsList = argsList.join(',') + argsListWired = argsListWired.join(',') + + var invokerFnBody = `return function (${argsList}) {\n`; + +#if ASSERTIONS + invokerFnBody += "checkArgCount(arguments.length, minArgs, maxArgs, humanName, throwBindingError);\n"; +#endif + +#if EMSCRIPTEN_TRACING + invokerFnBody += `Module.emscripten_trace_enter_context('embind::' + humanName );\n`; +#endif + + if (needsDestructorStack) { + invokerFnBody += "var destructors = [];\n"; + } + + var dtorStack = needsDestructorStack ? "destructors" : "null"; + var args1 = ["humanName", "throwBindingError", "invoker", "fn", "runDestructors", "fromRetWire", "toClassParamWire"]; + +#if EMSCRIPTEN_TRACING + args1.push("Module"); +#endif + + if (isClassMethodFunc) { + invokerFnBody += `var thisWired = toClassParamWire(${dtorStack}, this);\n`; + } + + for (var i = 0; i < argCount; ++i) { + var argName = `toArg${i}Wire`; + invokerFnBody += `var arg${i}Wired = ${argName}(${dtorStack}, arg${i});\n`; + args1.push(argName); + } + + invokerFnBody += (returns || isAsync ? "var rv = ":"") + `invoker(${argsListWired});\n`; + + var returnVal = returns ? "rv" : ""; +#if ASYNCIFY == 1 + args1.push("Asyncify"); +#endif +#if ASYNCIFY + invokerFnBody += `function onDone(${returnVal}) {\n`; +#endif + + if (needsDestructorStack) { + invokerFnBody += "runDestructors(destructors);\n"; + } else { + for (var i = isClassMethodFunc?1:2; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here. Also skip class type if not a method. + var paramName = (i === 1 ? "thisWired" : ("arg"+(i - 2)+"Wired")); + if (argTypes[i].destructorFunction !== null) { + invokerFnBody += `${paramName}_dtor(${paramName});\n`; + args1.push(`${paramName}_dtor`); + } + } + } + + if (returns) { + invokerFnBody += "var ret = fromRetWire(rv);\n" + +#if EMSCRIPTEN_TRACING + "Module.emscripten_trace_exit_context();\n" + +#endif + "return ret;\n"; + } else { +#if EMSCRIPTEN_TRACING + invokerFnBody += "Module.emscripten_trace_exit_context();\n"; +#endif + } + +#if ASYNCIFY == 1 + invokerFnBody += "}\n"; + invokerFnBody += `return Asyncify.currData ? Asyncify.whenDone().then(onDone) : onDone(${returnVal});\n` +#elif ASYNCIFY == 2 + invokerFnBody += "}\n"; + invokerFnBody += "return " + (isAsync ? "rv.then(onDone)" : `onDone(${returnVal})`) + ";"; +#endif + + invokerFnBody += "}\n"; + +#if ASSERTIONS + args1.push('checkArgCount', 'minArgs', 'maxArgs'); + invokerFnBody = `if (arguments.length !== ${args1.length}){ throw new Error(humanName + "Expected ${args1.length} closure arguments " + arguments.length + " given."); }\n${invokerFnBody}`; +#endif + return new Function(args1, invokerFnBody); + } +}; + +addToLibrary(LibraryEmbindShared); diff --git a/src/lib/libemval.js b/src/lib/libemval.js new file mode 100644 index 0000000000000..e9e8553cd49d4 --- /dev/null +++ b/src/lib/libemval.js @@ -0,0 +1,455 @@ +// Copyright 2012 The Emscripten Authors. All rights reserved. +// Emscripten is available under two separate licenses, the MIT license and the +// University of Illinois/NCSA Open Source License. Both these licenses can be +// found in the LICENSE file. + +// Number of handles reserved for non-use (0) or common values w/o refcount. +{{{ + const EMVAL_RESERVED_HANDLES = 5; + const EMVAL_LAST_RESERVED_HANDLE = EMVAL_RESERVED_HANDLES * 2 - 1; +}}} +var LibraryEmVal = { + // Stack of handles available for reuse. + $emval_freelist: [], + // Array of alternating pairs (value, refcount). + // reserve 0 and some special values. These never get de-allocated. + $emval_handles: [ + 0, 1, + undefined, 1, + null, 1, + true, 1, + false, 1, + ], +#if ASSERTIONS + $emval_handles__postset: 'assert(emval_handles.length === {{{ EMVAL_RESERVED_HANDLES }}} * 2)', +#endif + $emval_symbols: {}, // address -> string + + $count_emval_handles__deps: ['$emval_freelist', '$emval_handles'], + $count_emval_handles: () => { + return emval_handles.length / 2 - {{{ EMVAL_RESERVED_HANDLES }}} - emval_freelist.length; + }, + + _emval_register_symbol__deps: ['$emval_symbols', '$AsciiToString'], + _emval_register_symbol: (address) => { + emval_symbols[address] = AsciiToString(address); + }, + + $getStringOrSymbol__deps: ['$emval_symbols', '$AsciiToString'], + $getStringOrSymbol: (address) => { + var symbol = emval_symbols[address]; + if (symbol === undefined) { + return AsciiToString(address); + } + return symbol; + }, + + $Emval__deps: ['$emval_freelist', '$emval_handles', '$throwBindingError'], + $Emval: { + toValue: (handle) => { + if (!handle) { + throwBindingError(`Cannot use deleted val. handle = ${handle}`); + } + #if ASSERTIONS + // handle 2 is supposed to be `undefined`. + assert(handle === 2 || emval_handles[handle] !== undefined && handle % 2 === 0, `invalid handle: ${handle}`); + #endif + return emval_handles[handle]; + }, + + toHandle: (value) => { + switch (value) { + case undefined: return 2; + case null: return 4; + case true: return 6; + case false: return 8; + default:{ + const handle = emval_freelist.pop() || emval_handles.length; + emval_handles[handle] = value; + emval_handles[handle + 1] = 1; + return handle; + } + } + } + }, + + _emval_incref__deps: ['$emval_handles'], + _emval_incref: (handle) => { + if (handle > {{{ EMVAL_LAST_RESERVED_HANDLE }}}) { + emval_handles[handle + 1] += 1; + } + }, + + _emval_decref__deps: ['$emval_freelist', '$emval_handles'], + _emval_decref: (handle) => { + if (handle > {{{ EMVAL_LAST_RESERVED_HANDLE }}} && 0 === --emval_handles[handle + 1]) { + #if ASSERTIONS + assert(emval_handles[handle] !== undefined, `Decref for unallocated handle.`); + #endif + emval_handles[handle] = undefined; + emval_freelist.push(handle); + } + }, + + _emval_run_destructors__deps: ['_emval_decref', '$Emval', '$runDestructors'], + _emval_run_destructors: (handle) => { + var destructors = Emval.toValue(handle); + runDestructors(destructors); + __emval_decref(handle); + }, + + _emval_new_array__deps: ['$Emval'], + _emval_new_array: () => Emval.toHandle([]), + +#if !SUPPORT_BIG_ENDIAN + _emval_new_array_from_memory_view__deps: ['$Emval'], + _emval_new_array_from_memory_view: (view) => { + view = Emval.toValue(view); + // using for..loop is faster than Array.from + var a = new Array(view.length); + for (var i = 0; i < view.length; i++) a[i] = view[i]; + return Emval.toHandle(a); + }, + _emval_array_to_memory_view__deps: ['$Emval'], + _emval_array_to_memory_view: (dst, src) => { + dst = Emval.toValue(dst); + src = Emval.toValue(src); + dst.set(src); + }, +#else + _emval_new_array_from_memory_view__deps: ['$Emval'], + _emval_new_array_from_memory_view: (view) => { + view = Emval.toValue(view); + const dv = new DataView(view.buffer, view.byteOffset); + const reader = { + Int8Array: dv.getInt8, + Uint8Array: dv.getUint8, + Int16Array: dv.getInt16, + Uint16Array: dv.getUint16, + Int32Array: dv.getInt32, + Uint32Array: dv.getUint32, + BigInt64Array: dv.getBigInt64, + BigUint64Array: dv.getBigUint64, + Float32Array: dv.getFloat32, + Float64Array: dv.getFloat64, + }[view[Symbol.toStringTag]]; + var a = new Array(view.length); + for (var i = 0; i < view.length; i++) a[i] = reader.call(dv, i * view.BYTES_PER_ELEMENT, true); + return Emval.toHandle(a); + }, + _emval_array_to_memory_view__deps: ['$Emval'], + _emval_array_to_memory_view: (dst, src) => { + dst = Emval.toValue(dst); + src = Emval.toValue(src); + const dv = new DataView(dst.buffer, dst.byteOffset); + const writer = { + Int8Array: dv.setInt8, + Uint8Array: dv.setUint8, + Int16Array: dv.setInt16, + Uint16Array: dv.setUint16, + Int32Array: dv.setInt32, + Uint32Array: dv.setUint32, + BigInt64Array: dv.setBigInt64, + BigUint64Array: dv.setBigUint64, + Float32Array: dv.setFloat32, + Float64Array: dv.setFloat64, + }[dst[Symbol.toStringTag]]; + for (var i = 0; i < src.length; i++) writer.call(dv, i * dst.BYTES_PER_ELEMENT, src[i], true); + }, +#endif + + _emval_new_object__deps: ['$Emval'], + _emval_new_object: () => Emval.toHandle({}), + + _emval_new_cstring__deps: ['$getStringOrSymbol', '$Emval'], + _emval_new_cstring: (v) => Emval.toHandle(getStringOrSymbol(v)), + + _emval_new_u8string__deps: ['$Emval'], + _emval_new_u8string: (v) => Emval.toHandle(UTF8ToString(v)), + + _emval_new_u16string__deps: ['$Emval'], + _emval_new_u16string: (v) => Emval.toHandle(UTF16ToString(v)), + + _emval_get_global__deps: ['$Emval', '$getStringOrSymbol'], + _emval_get_global: (name) => { + if (!name) { + return Emval.toHandle(globalThis); + } + name = getStringOrSymbol(name); + return Emval.toHandle(globalThis[name]); + }, + + _emval_get_module_property__deps: ['$getStringOrSymbol', '$Emval'], + _emval_get_module_property: (name) => { + name = getStringOrSymbol(name); + return Emval.toHandle(Module[name]); + }, + + _emval_get_property__deps: ['$Emval'], + _emval_get_property: (handle, key) => { + handle = Emval.toValue(handle); + key = Emval.toValue(key); + return Emval.toHandle(handle[key]); + }, + + _emval_set_property__deps: ['$Emval'], + _emval_set_property: (handle, key, value) => { + handle = Emval.toValue(handle); + key = Emval.toValue(key); + value = Emval.toValue(value); + handle[key] = value; + }, + + $emval_returnValue__deps: ['$Emval'], + $emval_returnValue: (toReturnWire, destructorsRef, handle) => { + var destructors = []; + var result = toReturnWire(destructors, handle); + if (destructors.length) { + // void, primitives and any other types w/o destructors don't need to allocate a handle + {{{ makeSetValue('destructorsRef', '0', 'Emval.toHandle(destructors)', '*') }}}; + } + return result; + }, + + _emval_equals__deps: ['$Emval'], + _emval_equals: (first, second) => { + first = Emval.toValue(first); + second = Emval.toValue(second); + return first == second; + }, + + _emval_strictly_equals__deps: ['$Emval'], + _emval_strictly_equals: (first, second) => { + first = Emval.toValue(first); + second = Emval.toValue(second); + return first === second; + }, + + _emval_greater_than__deps: ['$Emval'], + _emval_greater_than: (first, second) => { + first = Emval.toValue(first); + second = Emval.toValue(second); + return first > second; + }, + + _emval_less_than__deps: ['$Emval'], + _emval_less_than: (first, second) => { + first = Emval.toValue(first); + second = Emval.toValue(second); + return first < second; + }, + + _emval_not__deps: ['$Emval'], + _emval_not: (object) => { + object = Emval.toValue(object); + return !object; + }, + + $emval_lookupTypes__deps: ['$requireRegisteredType'], + $emval_lookupTypes: (argCount, argTypes) => { + var a = new Array(argCount); + for (var i = 0; i < argCount; ++i) { + a[i] = requireRegisteredType({{{ makeGetValue('argTypes', `i*${POINTER_SIZE}`, '*') }}}, + `parameter ${i}`); + } + return a; + }, + + // Leave id 0 undefined. It's not a big deal, but might be confusing + // to have null be a valid method caller. + $emval_methodCallers: [undefined], + + $emval_addMethodCaller__deps: ['$emval_methodCallers'], + $emval_addMethodCaller: (caller) => { + var id = emval_methodCallers.length; + emval_methodCallers.push(caller); + return id; + }, + + _emval_create_invoker__deps: [ + '$emval_addMethodCaller', '$emval_lookupTypes', + '$createNamedFunction', '$emval_returnValue', + '$Emval', '$getStringOrSymbol', + ], + _emval_create_invoker: (argCount, argTypesPtr, kind) => { + var GenericWireTypeSize = {{{ 2 * POINTER_SIZE }}}; + + var [retType, ...argTypes] = emval_lookupTypes(argCount, argTypesPtr); + var toReturnWire = retType.toWireType.bind(retType); + var argFromPtr = argTypes.map(type => type.readValueFromPointer.bind(type)); + argCount--; // remove the extracted return type + +#if DYNAMIC_EXECUTION + var captures = {'toValue': Emval.toValue}; + var args = argFromPtr.map((argFromPtr, i) => { + var captureName = `argFromPtr${i}`; + captures[captureName] = argFromPtr; + return `${captureName}(args${i ? '+' + i * GenericWireTypeSize : ''})`; + }); + var functionBody; + switch (kind){ + case {{{ cDefs['internal::EM_INVOKER_KIND::FUNCTION'] }}}: + functionBody = 'toValue(handle)'; + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::CONSTRUCTOR'] }}}: + functionBody = 'new (toValue(handle))'; + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::CAST'] }}}: + functionBody = ''; + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::METHOD'] }}}: + captures['getStringOrSymbol'] = getStringOrSymbol; + functionBody = 'toValue(handle)[getStringOrSymbol(methodName)]'; + break; + } + functionBody += `(${args})`; + if (!retType.isVoid) { + captures['toReturnWire'] = toReturnWire; + captures['emval_returnValue'] = emval_returnValue; + functionBody = `return emval_returnValue(toReturnWire, destructorsRef, ${functionBody})`; + } + functionBody = `return function (handle, methodName, destructorsRef, args) { +${functionBody} +}`; + + var invokerFunction = new Function(Object.keys(captures), functionBody)(...Object.values(captures)); +#else + var argN = new Array(argCount); + var invokerFunction = (handle, methodName, destructorsRef, args) => { + var offset = 0; + for (var i = 0; i < argCount; ++i) { + argN[i] = argFromPtr[i](args + offset); + offset += GenericWireTypeSize; + } + var rv; + switch (kind) { + case {{{ cDefs['internal::EM_INVOKER_KIND::FUNCTION'] }}}: + rv = Emval.toValue(handle).apply(null, argN); + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::CONSTRUCTOR'] }}}: + rv = Reflect.construct(Emval.toValue(handle), argN); + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::CAST'] }}}: + // no-op, just return the argument + rv = argN[0]; + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::METHOD'] }}}: + rv = Emval.toValue(handle)[getStringOrSymbol(methodName)](...argN); + break; + } + return emval_returnValue(toReturnWire, destructorsRef, rv); + }; +#endif + var functionName = `methodCaller<(${argTypes.map(t => t.name)}) => ${retType.name}>`; + return emval_addMethodCaller(createNamedFunction(functionName, invokerFunction)); + }, + + _emval_invoke__deps: ['$getStringOrSymbol', '$emval_methodCallers', '$Emval'], + _emval_invoke: (caller, handle, methodName, destructorsRef, args) => { + return emval_methodCallers[caller](handle, methodName, destructorsRef, args); + }, + + // Same as `_emval_invoke`, just imported into Wasm under a different return type. + // TODO: remove this if/when https://github.com/emscripten-core/emscripten/issues/20478 is fixed. + _emval_invoke_i64__deps: ['_emval_invoke'], + _emval_invoke_i64: '=__emval_invoke', + + _emval_typeof__deps: ['$Emval'], + _emval_typeof: (handle) => { + handle = Emval.toValue(handle); + return Emval.toHandle(typeof handle); + }, + + _emval_instanceof__deps: ['$Emval'], + _emval_instanceof: (object, constructor) => { + object = Emval.toValue(object); + constructor = Emval.toValue(constructor); + return object instanceof constructor; + }, + + _emval_is_number__deps: ['$Emval'], + _emval_is_number: (handle) => { + handle = Emval.toValue(handle); + return typeof handle == 'number'; + }, + + _emval_is_string__deps: ['$Emval'], + _emval_is_string: (handle) => { + handle = Emval.toValue(handle); + return typeof handle == 'string'; + }, + + _emval_in__deps: ['$Emval'], + _emval_in: (item, object) => { + item = Emval.toValue(item); + object = Emval.toValue(object); + return item in object; + }, + + _emval_delete__deps: ['$Emval'], + _emval_delete: (object, property) => { + object = Emval.toValue(object); + property = Emval.toValue(property); + return delete object[property]; + }, + + _emval_throw__deps: ['$Emval'], + _emval_throw: (object) => { + object = Emval.toValue(object); + throw object; + }, + +#if ASYNCIFY + _emval_await__deps: ['$Emval', '$Asyncify'], + _emval_await__async: true, + _emval_await: (promise) => { + return Asyncify.handleAsync(async () => { + var value = await Emval.toValue(promise); + return Emval.toHandle(value); + }); + }, +#endif + + _emval_iter_begin__deps: ['$Emval'], + _emval_iter_begin: (iterable) => { + iterable = Emval.toValue(iterable); + return Emval.toHandle(iterable[Symbol.iterator]()); + }, + + _emval_iter_next__deps: ['$Emval'], + _emval_iter_next: (iterator) => { + iterator = Emval.toValue(iterator); + var result = iterator.next(); + return result.done ? 0 : Emval.toHandle(result.value); + }, + + _emval_coro_suspend__deps: ['$Emval', '_emval_coro_resume', '_emval_coro_reject'], + _emval_coro_suspend: (promiseHandle, awaiterPtr) => { + Emval.toValue(promiseHandle) + .then((result) => __emval_coro_resume(awaiterPtr, Emval.toHandle(result)), + (error) => __emval_coro_reject(awaiterPtr, Emval.toHandle(error))); + }, + + _emval_coro_make_promise__deps: ['$Emval'], + _emval_coro_make_promise: (resolveHandlePtr, rejectHandlePtr) => { + return Emval.toHandle(new Promise((resolve, reject) => { + {{{ makeSetValue('resolveHandlePtr', '0', 'Emval.toHandle(resolve)', '*') }}}; + {{{ makeSetValue('rejectHandlePtr', '0', 'Emval.toHandle(reject)', '*') }}}; + })); + }, + + _emval_from_current_cxa_exception__deps: ['$Emval', '__cxa_rethrow'], + _emval_from_current_cxa_exception: () => { + try { + // Use __cxa_rethrow which already has mechanism for generating + // user-friendly error message and stacktrace from C++ exception + // if EXCEPTION_STACK_TRACES is enabled and numeric exception + // with metadata optimised out otherwise. + ___cxa_rethrow(); + } catch (e) { + return Emval.toHandle(e); + } + }, +}; + +addToLibrary(LibraryEmVal); diff --git a/src/lib/libeventloop.js b/src/lib/libeventloop.js new file mode 100644 index 0000000000000..a92f1415911d8 --- /dev/null +++ b/src/lib/libeventloop.js @@ -0,0 +1,541 @@ +/** + * @license + * Copyright 2010 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +// Implementation of functions from emscripten/eventloop.h. + +LibraryJSEventLoop = { + emscripten_unwind_to_js_event_loop: () => { + throw 'unwind'; + }, + + $safeSetTimeout__deps: ['$callUserCallback'], + $safeSetTimeout__docs: '/** @param {number=} timeout */', + $safeSetTimeout: (func, timeout) => { + {{{ runtimeKeepalivePush() }}} + return setTimeout(() => { + {{{ runtimeKeepalivePop() }}} + callUserCallback(func); + }, timeout); + }, + + // Just like setImmediate but returns an i32 that can be passed back + // to wasm rather than a JS object. + $setImmediateWrapped: (func) => { + setImmediateWrapped.mapping ||= []; + var id = setImmediateWrapped.mapping.length; + setImmediateWrapped.mapping[id] = setImmediate(() => { + setImmediateWrapped.mapping[id] = undefined; + func(); + }); + return id; + }, + + $safeRequestAnimationFrame__deps: ['$MainLoop'], + $safeRequestAnimationFrame: (func) => { + {{{ runtimeKeepalivePush() }}} + return MainLoop.requestAnimationFrame(() => { + {{{ runtimeKeepalivePop() }}} + callUserCallback(func); + }); + }, + + // Just like clearImmediate but takes an i32 rather than an object. + $clearImmediateWrapped: (id) => { +#if ASSERTIONS + assert(id); + assert(setImmediateWrapped.mapping[id]); +#endif + clearImmediate(setImmediateWrapped.mapping[id]); + setImmediateWrapped.mapping[id] = undefined; + }, + + $emSetImmediate__deps: ['$setImmediateWrapped', '$clearImmediateWrapped', '$emClearImmediate'], + $emSetImmediate__postset: ` + if (globalThis.setImmediate) { + emSetImmediate = setImmediateWrapped; + emClearImmediate = clearImmediateWrapped; + } else if (globalThis.addEventListener) { + var __setImmediate_id_counter = 0; + var __setImmediate_queue = []; + var __setImmediate_message_id = "_si"; + /** @param {Event} e */ + var __setImmediate_cb = (e) => { + if (e.data === __setImmediate_message_id) { + e.stopPropagation(); + __setImmediate_queue.shift()(); + ++__setImmediate_id_counter; + } + } + addEventListener("message", __setImmediate_cb, true); + emSetImmediate = (func) => { + postMessage(__setImmediate_message_id, "*"); + return __setImmediate_id_counter + __setImmediate_queue.push(func) - 1; + } + emClearImmediate = /**@type{function(number=)}*/((id) => { + var index = id - __setImmediate_id_counter; + // must preserve the order and count of elements in the queue, so replace the pending callback with an empty function + if (index >= 0 && index < __setImmediate_queue.length) __setImmediate_queue[index] = () => {}; + }) + }`, + $emSetImmediate: undefined, + + $emClearImmediate_deps: ['$emSetImmediate'], + $emClearImmediate: undefined, + + emscripten_set_immediate__deps: ['$emSetImmediate', '$callUserCallback'], + emscripten_set_immediate: (cb, userData) => { + {{{ runtimeKeepalivePush(); }}} + return emSetImmediate(() => { + {{{ runtimeKeepalivePop(); }}} + callUserCallback(() => {{{ makeDynCall('vp', 'cb') }}}(userData)); + }); + }, + + emscripten_clear_immediate__deps: ['$emClearImmediate'], + emscripten_clear_immediate: (id) => { + {{{ runtimeKeepalivePop(); }}} + emClearImmediate(id); + }, + + emscripten_set_immediate_loop__deps: ['$emSetImmediate', '$callUserCallback'], + emscripten_set_immediate_loop: (cb, userData) => { + function tick() { + callUserCallback(() => { + if ({{{ makeDynCall('ip', 'cb') }}}(userData)) { + emSetImmediate(tick); + } else { + {{{ runtimeKeepalivePop(); }}} + } + }); + } + {{{ runtimeKeepalivePush(); }}} + emSetImmediate(tick); + }, + + emscripten_set_timeout__deps: ['$safeSetTimeout'], + emscripten_set_timeout: (cb, msecs, userData) => + safeSetTimeout(() => {{{ makeDynCall('vp', 'cb') }}}(userData), msecs), + +#if AUDIO_WORKLET + // Use a wrapper function here since simply aliasing `clearTimeout` would + // cause the module to fail to load in the audio worklet context. + emscripten_clear_timeout: (id) => clearTimeout(id), +#else + emscripten_clear_timeout: 'clearTimeout', +#endif + + emscripten_set_timeout_loop__deps: ['$callUserCallback', 'emscripten_get_now'], + emscripten_set_timeout_loop: (cb, msecs, userData) => { + function tick() { + var t = _emscripten_get_now(); + var n = t + msecs; + {{{ runtimeKeepalivePop() }}} + callUserCallback(() => { + if ({{{ makeDynCall('idp', 'cb') }}}(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(); +#if ENVIRONMENT_MAY_BE_NODE + // Recent revsions of node, however, give TimeoutNegativeWarning + remaining = Math.max(0, remaining); +#endif + setTimeout(tick, remaining); + } + }); + } + {{{ runtimeKeepalivePush() }}} + return setTimeout(tick, 0); + }, + + emscripten_set_interval__deps: ['$callUserCallback'], + emscripten_set_interval: (cb, msecs, userData) => { + {{{ runtimeKeepalivePush() }}} + return setInterval(() => { + callUserCallback(() => {{{ makeDynCall('vp', 'cb') }}}(userData)); + }, msecs); + }, + + emscripten_clear_interval: (id) => { + {{{ runtimeKeepalivePop() }}} + clearInterval(id); + }, + + emscripten_async_call__deps: ['$safeSetTimeout', '$safeRequestAnimationFrame'], + emscripten_async_call: (func, arg, millis) => { + var wrapper = () => {{{ makeDynCall('vp', 'func') }}}(arg); + + if (millis >= 0 +#if ENVIRONMENT_MAY_BE_NODE + // node does not support requestAnimationFrame + || ENVIRONMENT_IS_NODE +#endif + ) { + safeSetTimeout(wrapper, millis); + } else { + safeRequestAnimationFrame(wrapper); + } + }, + + $registerPostMainLoop: (f) => { + // Does nothing unless $MainLoop is included/used. + typeof MainLoop != 'undefined' && MainLoop.postMainLoop.push(f); + }, + + $registerPreMainLoop: (f) => { + // Does nothing unless $MainLoop is included/used. + typeof MainLoop != 'undefined' && MainLoop.preMainLoop.push(f); + }, + + $MainLoop__internal: true, + $MainLoop__deps: ['$setMainLoop', '$callUserCallback', 'emscripten_set_main_loop_timing'], + $MainLoop__postset: ` + Module['requestAnimationFrame'] = MainLoop.requestAnimationFrame; + Module['pauseMainLoop'] = MainLoop.pause; + Module['resumeMainLoop'] = MainLoop.resume; + MainLoop.init();`, + $MainLoop: { + running: false, + scheduler: null, + method: '', + // Each main loop is numbered with a ID in sequence order. Only one main + // loop can run at a time. This variable stores the ordinal number of the + // main loop that is currently allowed to run. All previous main loops + // will quit themselves. This is incremented whenever a new main loop is + // created. + currentlyRunningMainloop: 0, + // The main loop tick function that will be called at each iteration. + func: null, + // The argument that will be passed to the main loop. (of type void*) + 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 expectToReceiveOnModule('setStatus') + 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'](''); + } + } +#endif + }, + + init() { +#if expectToReceiveOnModule('preMainLoop') + Module['preMainLoop'] && MainLoop.preMainLoop.push(Module['preMainLoop']); +#endif +#if expectToReceiveOnModule('postMainLoop') + Module['postMainLoop'] && MainLoop.postMainLoop.push(Module['postMainLoop']); +#endif + }, + + 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(); + } +#if STACK_OVERFLOW_CHECK + checkStackCookie(); +#endif + }, + + 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); + } + }, + }, + + emscripten_get_main_loop_timing__deps: ['$MainLoop'], + emscripten_get_main_loop_timing: (mode, value) => { + if (mode) {{{ makeSetValue('mode', 0, 'MainLoop.timingMode', 'i32') }}}; + if (value) {{{ makeSetValue('value', 0, 'MainLoop.timingValue', 'i32') }}}; + }, + + emscripten_set_main_loop_timing__deps: ['$MainLoop'], + emscripten_set_main_loop_timing: (mode, value) => { + MainLoop.timingMode = mode; + MainLoop.timingValue = value; + + if (!MainLoop.func) { +#if ASSERTIONS + err('emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.'); +#endif + 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 == {{{ cDefs.EM_TIMING_SETTIMEOUT }}}) { + 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 == {{{ cDefs.EM_TIMING_RAF }}}) { + MainLoop.scheduler = function MainLoop_scheduler_rAF() { + MainLoop.requestAnimationFrame(MainLoop.runner); + }; + MainLoop.method = 'rAF'; + } else if (mode == {{{ cDefs.EM_TIMING_SETIMMEDIATE}}}) { + 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__deps: ['$setMainLoop'], + emscripten_set_main_loop: (func, fps, simulateInfiniteLoop) => { + var iterFunc = {{{ makeDynCall('v', 'func') }}}; + setMainLoop(iterFunc, fps, simulateInfiniteLoop); + }, + + $setMainLoop__internal: true, + $setMainLoop__deps: [ + '$MainLoop', + 'emscripten_set_main_loop_timing', 'emscripten_get_now', +#if !MINIMAL_RUNTIME + '$maybeExit', +#endif + ], + $setMainLoop__docs: ` + /** + * @param {number=} arg + * @param {boolean=} noSetTiming + */`, + $setMainLoop: (iterFunc, fps, simulateInfiniteLoop, arg, noSetTiming) => { +#if ASSERTIONS + assert(!MainLoop.func, 'emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.'); +#endif + MainLoop.func = iterFunc; + MainLoop.arg = arg; + + var thisMainLoopId = MainLoop.currentlyRunningMainloop; + function checkIsRunning() { + if (thisMainLoopId < MainLoop.currentlyRunningMainloop) { +#if RUNTIME_DEBUG + dbg('main loop exiting'); +#endif + {{{ runtimeKeepalivePop() }}} +#if !MINIMAL_RUNTIME + maybeExit(); +#endif + 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; + } + } +#if RUNTIME_DEBUG + dbg(`main loop blocker "${blocker.name}" took '${Date.now() - start} ms`); //, left: ' + MainLoop.remainingBlockers); +#endif + 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 == {{{ cDefs.EM_TIMING_RAF }}} && MainLoop.timingValue > 1 && MainLoop.currentFrameNumber % MainLoop.timingValue != 0) { + // Not the scheduled time to render this frame - skip. + MainLoop.scheduler(); + return; + } else if (MainLoop.timingMode == {{{ cDefs.EM_TIMING_SETTIMEOUT }}}) { + MainLoop.tickStartTime = _emscripten_get_now(); + } + +#if ASSERTIONS + if (MainLoop.method === 'timeout' && Module['ctx']) { + warnOnce('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!'); + MainLoop.method = ''; // just warn once per call to set main loop + } +#endif + + 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({{{ cDefs.EM_TIMING_SETTIMEOUT }}}, 1000.0 / fps); + } else { + // Do rAF by rendering each frame (no decimating) + _emscripten_set_main_loop_timing({{{ cDefs.EM_TIMING_RAF }}}, 1); + } + + MainLoop.scheduler(); + } + + if (simulateInfiniteLoop) { + throw 'unwind'; + } + }, + + emscripten_set_main_loop_arg__deps: ['$setMainLoop'], + emscripten_set_main_loop_arg: (func, arg, fps, simulateInfiniteLoop) => { + var iterFunc = () => {{{ makeDynCall('vp', 'func') }}}(arg); + setMainLoop(iterFunc, fps, simulateInfiniteLoop, arg); + }, + + emscripten_cancel_main_loop__deps: ['$MainLoop'], + emscripten_cancel_main_loop: () => { + MainLoop.pause(); + MainLoop.func = null; + }, + + emscripten_pause_main_loop__deps: ['$MainLoop'], + emscripten_pause_main_loop: () => MainLoop.pause(), + + emscripten_resume_main_loop__deps: ['$MainLoop'], + emscripten_resume_main_loop: () => MainLoop.resume(), + + _emscripten_push_main_loop_blocker__deps: ['$MainLoop'], + _emscripten_push_main_loop_blocker: (func, arg, name) => { + MainLoop.queue.push({ func: () => { + {{{ makeDynCall('vp', 'func') }}}(arg); + }, name: UTF8ToString(name), counted: true }); + MainLoop.updateStatus(); + }, + + _emscripten_push_uncounted_main_loop_blocker__deps: ['$MainLoop'], + _emscripten_push_uncounted_main_loop_blocker: (func, arg, name) => { + MainLoop.queue.push({ func: () => { + {{{ makeDynCall('vp', 'func') }}}(arg); + }, name: UTF8ToString(name), counted: false }); + MainLoop.updateStatus(); + }, + + emscripten_set_main_loop_expected_blockers__deps: ['$MainLoop'], + emscripten_set_main_loop_expected_blockers: (num) => { + MainLoop.expectedBlockers = num; + MainLoop.remainingBlockers = num; + MainLoop.updateStatus(); + }, + +}; + +addToLibrary(LibraryJSEventLoop); diff --git a/src/lib/libexceptions.js b/src/lib/libexceptions.js new file mode 100644 index 0000000000000..6d55a107eee4d --- /dev/null +++ b/src/lib/libexceptions.js @@ -0,0 +1,384 @@ +/** + * @license + * Copyright 2010 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +var LibraryExceptions = { +#if !WASM_EXCEPTIONS + $uncaughtExceptionCount: '0', + $exceptionLast: '0', + $exceptionCaught: ' []', + + // This class is the exception metadata which is prepended to each thrown object (in WASM memory). + // It is allocated in one block among with a thrown object in __cxa_allocate_exception and freed + // in ___cxa_free_exception. It roughly corresponds to __cxa_exception structure in libcxxabi. The + // class itself is just a native pointer wrapper, and contains all the necessary accessors for the + // fields in the native structure. + // TODO: Unfortunately this approach still cannot be considered thread-safe because single + // exception object can be simultaneously thrown in several threads and its state (except + // reference counter) is not protected from that. Also protection is not enough, separate state + // should be allocated. libcxxabi has concept of dependent exception which is used for that + // purpose, it references the primary exception. + $ExceptionInfo: class { + // excPtr - Thrown object pointer to wrap. Metadata pointer is calculated from it. + constructor(excPtr) { + this.excPtr = excPtr; + this.ptr = excPtr - {{{ C_STRUCTS.__cxa_exception.__size__ }}}; + } + + set_type(type) { + {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.exceptionType, 'type', '*') }}}; + } + + get_type() { + return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.exceptionType, '*') }}}; + } + + set_destructor(destructor) { + {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.exceptionDestructor, 'destructor', '*') }}}; + } + + get_destructor() { + return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.exceptionDestructor, '*') }}}; + } + + set_caught(caught) { + caught = caught ? 1 : 0; + {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.caught, 'caught', 'i8') }}}; + } + + get_caught() { + return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.caught, 'i8') }}} != 0; + } + + set_rethrown(rethrown) { + rethrown = rethrown ? 1 : 0; + {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.rethrown, 'rethrown', 'i8') }}}; + } + + get_rethrown() { + return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.rethrown, 'i8') }}} != 0; + } + + // Initialize native structure fields. Should be called once after allocated. + init(type, destructor) { +#if EXCEPTION_DEBUG + dbg('ExceptionInfo init: ' + [type, destructor]); +#endif + this.set_adjusted_ptr(0); + this.set_type(type); + this.set_destructor(destructor); + } + + set_adjusted_ptr(adjustedPtr) { + {{{ makeSetValue('this.ptr', C_STRUCTS.__cxa_exception.adjustedPtr, 'adjustedPtr', '*') }}}; + } + + get_adjusted_ptr() { + return {{{ makeGetValue('this.ptr', C_STRUCTS.__cxa_exception.adjustedPtr, '*') }}}; + } + }, + + // Here, we throw an exception after recording a couple of values that we need to remember + // We also remember that it was the last exception thrown as we need to know that later. + __cxa_throw__deps: ['$ExceptionInfo', '$exceptionLast', '$uncaughtExceptionCount'], + __cxa_throw: (ptr, type, destructor) => { +#if EXCEPTION_DEBUG + dbg('__cxa_throw: ' + [ptrToString(ptr), type, ptrToString(destructor)]); +#endif + var info = new ExceptionInfo(ptr); + // Initialize ExceptionInfo content after it was allocated in __cxa_allocate_exception. + info.init(type, destructor); + {{{ storeException('exceptionLast', 'ptr') }}} + uncaughtExceptionCount++; + {{{ makeThrow('exceptionLast') }}} + }, + + // This exception will be caught twice, but while begin_catch runs twice, + // we early-exit from end_catch when the exception has been rethrown, so + // pop that here from the caught exceptions. + __cxa_rethrow__deps: ['$exceptionCaught', '$exceptionLast', '$uncaughtExceptionCount'], + __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++; + } +#if EXCEPTION_DEBUG + dbg('__cxa_rethrow, popped ' + + [ptrToString(ptr), exceptionLast, 'stack', exceptionCaught]); +#endif + {{{ storeException('exceptionLast', 'ptr') }}} + {{{ makeThrow('exceptionLast') }}} + }, + + llvm_eh_typeid_for: (type) => type, + + __cxa_begin_catch__deps: ['$exceptionCaught', '__cxa_increment_exception_refcount', + '__cxa_get_exception_ptr', + '$uncaughtExceptionCount'], + __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); +#if EXCEPTION_DEBUG + dbg('__cxa_begin_catch ' + [ptrToString(ptr), 'stack', exceptionCaught]); +#endif + ___cxa_increment_exception_refcount(ptr); + return ___cxa_get_exception_ptr(ptr); + }, + + // We're done with a catch. Now, we can run the destructor if there is one + // and free the exception. Note that if the dynCall on the destructor fails + // due to calling apply on undefined, that means that the destructor is + // an invalid index into the FUNCTION_TABLE, so something has gone wrong. + __cxa_end_catch__deps: ['$exceptionCaught', '$exceptionLast', '__cxa_decrement_exception_refcount', 'setThrew'], + __cxa_end_catch: () => { + // Clear state flag. + _setThrew(0, 0); +#if ASSERTIONS + assert(exceptionCaught.length > 0); +#endif + // Call destructor if one is registered then clear it. + var info = exceptionCaught.pop(); + +#if EXCEPTION_DEBUG + dbg('__cxa_end_catch popped ' + [info, exceptionLast, 'stack', exceptionCaught]); +#endif + ___cxa_decrement_exception_refcount(info.excPtr); + exceptionLast = 0; // XXX in decRef? + }, + + __cxa_uncaught_exceptions__deps: ['$uncaughtExceptionCount'], + __cxa_uncaught_exceptions: () => uncaughtExceptionCount, + + __cxa_call_unexpected: (exception) => abort('Unexpected exception thrown, this is not properly supported - aborting'), + + __cxa_current_primary_exception__deps: ['$exceptionCaught', '__cxa_increment_exception_refcount'], + __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_exception_type() { + if (!exceptionCaught.length) { + return 0; + } + var info = exceptionCaught[exceptionCaught.length - 1]; + return info.get_type(); + }, + + __cxa_rethrow_primary_exception__deps: ['$ExceptionInfo', '$exceptionCaught', '__cxa_rethrow'], + __cxa_rethrow_primary_exception: (ptr) => { + if (!ptr) return; + var info = new ExceptionInfo(ptr); + exceptionCaught.push(info); + info.set_rethrown(true); + ___cxa_rethrow(); + }, + + // Finds a suitable catch clause for when an exception is thrown. + // In normal compilers, this functionality is handled by the C++ + // 'personality' routine. This is passed a fairly complex structure + // relating to the context of the exception and makes judgements + // about how to handle it. Some of it is about matching a suitable + // catch clause, and some of it is about unwinding. We already handle + // unwinding using 'if' blocks around each function, so the remaining + // functionality boils down to picking a suitable 'catch' block. + // We'll do that here, instead, to keep things simpler. + $findMatchingCatch__deps: ['$exceptionLast', '$ExceptionInfo', '__cxa_can_catch', '$setTempRet0'], + $findMatchingCatch: (args) => { + var thrown = +#if EXCEPTION_STACK_TRACES + exceptionLast?.excPtr; +#else + exceptionLast; +#endif + 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 +#if EXCEPTION_DEBUG + dbg("findMatchingCatch on " + ptrToString(thrown)); +#endif + // 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 + {{{ C_STRUCTS.__cxa_exception.adjustedPtr }}}; + if (___cxa_can_catch(caughtType, thrownType, adjusted_ptr_addr)) { +#if EXCEPTION_DEBUG + dbg(" findMatchingCatch found " + [ptrToString(info.get_adjusted_ptr()), caughtType]); +#endif + setTempRet0(caughtType); + return thrown; + } + } + setTempRet0(thrownType); + return thrown; + }, + + __resumeException__deps: ['$exceptionLast'], + __resumeException: (ptr) => { +#if EXCEPTION_DEBUG + dbg("__resumeException " + [ptrToString(ptr), exceptionLast]); +#endif + if (!exceptionLast) { + {{{ storeException('exceptionLast', 'ptr') }}} + } + {{{ makeThrow('exceptionLast') }}} + }, + +#endif +#if WASM_EXCEPTIONS || !DISABLE_EXCEPTION_CATCHING + $getExceptionMessageCommon__deps: ['__get_exception_message', 'free', '$stackSave', '$stackRestore', '$stackAlloc'], + $getExceptionMessageCommon: (ptr) => { + var sp = stackSave(); + var type_addr_addr = stackAlloc({{{ POINTER_SIZE }}}); + var message_addr_addr = stackAlloc({{{ POINTER_SIZE }}}); + ___get_exception_message(ptr, type_addr_addr, message_addr_addr); + var type_addr = {{{ makeGetValue('type_addr_addr', 0, '*') }}}; + var message_addr = {{{ makeGetValue('message_addr_addr', 0, '*') }}}; + var type = UTF8ToString(type_addr); + _free(type_addr); + var message; + if (message_addr) { + message = UTF8ToString(message_addr); + _free(message_addr); + } + stackRestore(sp); + return [type, message]; + }, +#endif +#if WASM_EXCEPTIONS + $getCppExceptionTag__deps: ['__cpp_exception'], + // In static linking, tags are defined within the wasm module and are + // exported, whereas in dynamic linking, tags are defined in libcore.js in + // JS code and wasm modules import them. + $getCppExceptionTag: () => ___cpp_exception, + +#if EXCEPTION_STACK_TRACES + // Throw a WebAssembly.Exception object with the C++ tag with a stack trace + // embedded. WebAssembly.Exception is a JS object representing a Wasm + // exception, provided by Wasm JS API: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Exception + // In release builds, this function is not needed and the native + // _Unwind_RaiseException in libunwind is used instead. + __throw_exception_with_stack_trace__deps: ['$getCppExceptionTag', '$getExceptionMessage'], + __throw_exception_with_stack_trace: (ex) => { + var e = new WebAssembly.Exception(getCppExceptionTag(), [ex], {traceStack: true}); + e.message = getExceptionMessage(e); + throw e; + }, +#endif + + // Given an WebAssembly.Exception object, returns the actual user-thrown + // C++ object address in the Wasm memory. + $getCppExceptionThrownObjectFromWebAssemblyException__deps: ['$getCppExceptionTag', '__thrown_object_from_unwind_exception'], + $getCppExceptionThrownObjectFromWebAssemblyException: (ex) => { + // In Wasm EH, the value extracted from WebAssembly.Exception is a pointer + // to the unwind header. Convert it to the actual thrown value. + var unwind_header = ex.getArg(getCppExceptionTag(), 0); + return ___thrown_object_from_unwind_exception(unwind_header); + }, + + $incrementExceptionRefcount__deps: ['__cxa_increment_exception_refcount', '$getCppExceptionThrownObjectFromWebAssemblyException'], + $incrementExceptionRefcount: (ex) => { + var ptr = getCppExceptionThrownObjectFromWebAssemblyException(ex); + ___cxa_increment_exception_refcount(ptr); + }, + + $decrementExceptionRefcount__deps: ['__cxa_decrement_exception_refcount', '$getCppExceptionThrownObjectFromWebAssemblyException'], + $decrementExceptionRefcount: (ex) => { + var ptr = getCppExceptionThrownObjectFromWebAssemblyException(ex); + ___cxa_decrement_exception_refcount(ptr); + }, + + $getExceptionMessage__deps: ['$getCppExceptionThrownObjectFromWebAssemblyException', '$getExceptionMessageCommon'], + $getExceptionMessage: (ex) => { + var ptr = getCppExceptionThrownObjectFromWebAssemblyException(ex); + return getExceptionMessageCommon(ptr); + }, + +#elif !DISABLE_EXCEPTION_CATCHING + $incrementExceptionRefcount__deps: ['__cxa_increment_exception_refcount'], + $incrementExceptionRefcount: (ptr) => ___cxa_increment_exception_refcount(ptr), + + $decrementExceptionRefcount__deps: ['__cxa_decrement_exception_refcount'], + $decrementExceptionRefcount: (ptr) => ___cxa_decrement_exception_refcount(ptr), + + $getExceptionMessage__deps: ['$getExceptionMessageCommon'], + $getExceptionMessage: (ptr) => getExceptionMessageCommon(ptr), + +#endif +}; + +#if !WASM_EXCEPTIONS +// In LLVM, exceptions generate a set of functions of form +// __cxa_find_matching_catch_2(), __cxa_find_matching_catch_3(), etc. where the +// number specifies the number of arguments. In Emscripten, route all these to +// a single function '__cxa_find_matching_catch' that variadically processes all +// of these functions using JS 'arguments' object. +addCxaCatch = (n) => { + const args = []; + // Confusingly, the actual number of asrgument is n - 2. According to the llvm + // code in WebAssemblyLowerEmscriptenEHSjLj.cpp: + // This is because a landingpad instruction contains two more arguments, a + // personality function and a cleanup bit, and __cxa_find_matching_catch_N + // functions are named after the number of arguments in the original landingpad + // instruction. + let sig = 'p'; + for (let i = 0; i < n - 2; i++) { + args.push(`arg${i}`); + sig += 'p'; + } + const argString = args.join(','); + LibraryManager.library[`__cxa_find_matching_catch_${n}__sig`] = sig; + LibraryManager.library[`__cxa_find_matching_catch_${n}__deps`] = ['$findMatchingCatch']; + LibraryManager.library[`__cxa_find_matching_catch_${n}`] = eval(`(${args}) => findMatchingCatch([${argString}])`); +}; + +// Add the first 2-5 catch handlers preemptively. Others get added on demand in +// jsifier. This is done here primarily so that these symbols end up with the +// correct deps in the stub library that we pass to wasm-ld. +// Note: __cxa_find_matching_catch_N function uses N = NumClauses + 2 so +// __cxa_find_matching_catch_2 is the first such function with zero clauses. +// See WebAssemblyLowerEmscriptenEHSjLj.cpp. +for (let i = 2; i < 5; i++) { + addCxaCatch(i) +} +#endif + +addToLibrary(LibraryExceptions); diff --git a/src/lib/libexceptions_stub.js b/src/lib/libexceptions_stub.js new file mode 100644 index 0000000000000..8cb2693e6c416 --- /dev/null +++ b/src/lib/libexceptions_stub.js @@ -0,0 +1,32 @@ +/** + * @license + * Copyright 2019 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +var LibraryExceptions = {}; + +[ + '__cxa_throw', + '__cxa_rethrow', + 'llvm_eh_typeid_for', + '__cxa_begin_catch', + '__cxa_end_catch', + '__cxa_get_exception_ptr', + '_ZSt18uncaught_exceptionv', + '__cxa_call_unexpected', + '__cxa_current_primary_exception', + '__cxa_rethrow_primary_exception', + '__cxa_find_matching_catch', + '__resumeException', +].forEach((name) => { + LibraryExceptions[name] = function() { abort(); }; +#if !INCLUDE_FULL_LIBRARY + // This method of link-time error generation is not compatible with INCLUDE_FULL_LIBRARY + LibraryExceptions[name + '__deps'] = [function() { + error(`DISABLE_EXCEPTION_THROWING was set (likely due to -fno-exceptions), which means no C++ exception throwing support code is linked in, but such support is required by symbol '${name}'. Either do not set DISABLE_EXCEPTION_THROWING (if you do want exception throwing) or compile all source files with -fno-exceptions (so that no exceptions support code is required); also make sure DISABLE_EXCEPTION_CATCHING is set to the right value - if you want exceptions, it should be off, and vice versa.`); + }]; +#endif +}); + +addToLibrary(LibraryExceptions); diff --git a/src/lib/libexports.js b/src/lib/libexports.js new file mode 100644 index 0000000000000..0ccf93e436249 --- /dev/null +++ b/src/lib/libexports.js @@ -0,0 +1,24 @@ +/** + * @license + * Copyright 2020 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + emscripten_get_exported_function__deps: ['$addFunction', '$UTF8ToString'], + emscripten_get_exported_function: (name) => { + name = UTF8ToString(name); + // Wasm backend does not use C name mangling on exports, + // so adjust for that manually. + if (name[0] == '_') name = name.slice(1); + var exportedFunc = wasmExports[name]; + if (exportedFunc) { + // Note: addFunction automatically caches the created function pointer. + return addFunction(exportedFunc); + } +#if ASSERTIONS + err(`No exported function found by name "{exportedFunc}"`); +#endif + // implicit return 0; + } +}); diff --git a/src/lib/libfetch.js b/src/lib/libfetch.js new file mode 100644 index 0000000000000..539ddc0f02fca --- /dev/null +++ b/src/lib/libfetch.js @@ -0,0 +1,44 @@ +/** + * @license + * Copyright 2016 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +#include Fetch.js + +var LibraryFetch = { + $Fetch__postset: 'Fetch.init();', + $Fetch__deps: ['$HandleAllocator'], + $Fetch: Fetch, + _emscripten_fetch_get_response_headers_length__deps: ['$lengthBytesUTF8'], + _emscripten_fetch_get_response_headers_length: fetchGetResponseHeadersLength, + _emscripten_fetch_get_response_headers__deps: ['$lengthBytesUTF8', '$stringToUTF8'], + _emscripten_fetch_get_response_headers: fetchGetResponseHeaders, + emscripten_fetch_free: fetchFree, + +#if FETCH_SUPPORT_INDEXEDDB + $fetchDeleteCachedData: fetchDeleteCachedData, + $fetchLoadCachedData: fetchLoadCachedData, + $fetchCacheData: fetchCacheData, +#endif + $fetchXHR: fetchXHR, + + emscripten_start_fetch: startFetch, + emscripten_start_fetch__deps: [ + 'malloc', + 'free', + '$Fetch', + '$fetchXHR', + '$callUserCallback', + '$writeI53ToI64', + '$stringToUTF8', + '$stringToNewUTF8', +#if FETCH_SUPPORT_INDEXEDDB + '$fetchCacheData', + '$fetchLoadCachedData', + '$fetchDeleteCachedData', +#endif + ] +}; + +addToLibrary(LibraryFetch); diff --git a/src/lib/libfetchfs.js b/src/lib/libfetchfs.js new file mode 100644 index 0000000000000..d0b704034d5f6 --- /dev/null +++ b/src/lib/libfetchfs.js @@ -0,0 +1,23 @@ +/** + * @license + * Copyright 2023 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + $FETCHFS__deps: ['$stringToUTF8OnStack', 'wasmfs_create_fetch_backend'], + $FETCHFS: { + createBackend(opts) { + return withStackSave( + () => _wasmfs_create_fetch_backend( + stringToUTF8OnStack(opts.base_url ?? ""), + opts.chunkSize | 0 + ) + ); + }, + }, +}); + +if (!WASMFS) { + error("using -lfetchfs.js requires using WasmFS (-sWASMFS)"); +} diff --git a/src/lib/libfs.js b/src/lib/libfs.js new file mode 100644 index 0000000000000..e091494baa415 --- /dev/null +++ b/src/lib/libfs.js @@ -0,0 +1,1941 @@ +/** + * @license + * Copyright 2013 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +var LibraryFS = { + $FS__deps: ['$randomFill', '$PATH', '$PATH_FS', '$TTY', '$MEMFS', + '$FS_modeStringToFlags', + '$FS_getMode', + '$intArrayFromString', +#if LibraryManager.has('libidbfs.js') + '$IDBFS', +#endif +#if LibraryManager.has('libnodefs.js') + '$NODEFS', +#endif +#if LibraryManager.has('libworkerfs.js') + '$WORKERFS', +#endif +#if LibraryManager.has('libnoderawfs.js') + '$NODERAWFS', +#endif +#if LibraryManager.has('libproxyfs.js') + '$PROXYFS', +#endif +#if ASSERTIONS + '$strError', '$ERRNO_CODES', +#endif +#if !MINIMAL_RUNTIME + '$FS_createPreloadedFile', +#endif + ], + $FS__postset: () => { + // TODO: do we need noFSInit? + addAtInit(`if (!Module['noFSInit'] && !FS.initialized) FS.init();`); + addAtPostCtor('FS.ignorePermissions = false;'); + addAtExit('FS.quit();'); + return ` +#if !MINIMAL_RUNTIME +FS.createPreloadedFile = FS_createPreloadedFile; +FS.preloadFile = FS_preloadFile; +#endif +FS.staticInit();`; + }, + $FS: { + root: null, + mounts: [], + devices: {}, + streams: [], + nextInode: 1, + nameTable: null, + currentPath: '/', + initialized: false, + // Whether we are currently ignoring permissions. Useful when preparing the + // filesystem and creating files inside read-only folders. + // This is set to false during `preInit`, allowing you to modify the + // filesystem freely up until that point (e.g. during `preRun`). + ignorePermissions: true, +#if FS_DEBUG + trackingDelegate: {}, +#endif + filesystems: null, + syncFSRequests: 0, // we warn if there are multiple in flight at once +#if expectToReceiveOnModule('logReadFiles') + readFiles: {}, +#endif +#if ASSERTIONS + ErrnoError: class extends Error { +#else + ErrnoError: class { +#endif + 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) { +#if ASSERTIONS + super(runtimeInitialized ? strError(errno) : ''); +#endif + this.errno = errno; +#if ASSERTIONS + for (var key in ERRNO_CODES) { + if (ERRNO_CODES[key] === errno) { + this.code = key; + break; + } + } +#endif + } + }, + + FSStream: class { + shared = {}; +#if USE_CLOSURE_COMPILER + // Closure compiler requires us to declare all properties ahead of time + node = null; +#endif + get object() { + return this.node; + } + set object(val) { + this.node = val; + } + get isRead() { + return (this.flags & {{{ cDefs.O_ACCMODE }}}) !== {{{ cDefs.O_WRONLY }}}; + } + get isWrite() { + return (this.flags & {{{ cDefs.O_ACCMODE }}}) !== {{{ cDefs.O_RDONLY }}}; + } + get isAppend() { + return (this.flags & {{{ cDefs.O_APPEND }}}); + } + 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 = {{{ cDefs.S_IRUGO }}} | {{{ cDefs.S_IXUGO }}}; + writeMode = {{{ cDefs.S_IWUGO }}}; + 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); + } + }, + + // + // paths + // + lookupPath(path, opts = {}) { + if (!path) { + throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); + } + 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 === {{{ cDefs.ENOENT }}}) && 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({{{ cDefs.ENOSYS }}}); + } + 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({{{ cDefs.ELOOP }}}); + }, + 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; + } + }, + + // + // nodes + // + hashName(parentid, name) { + var hash = 0; + +#if CASE_INSENSITIVE_FS + name = name.toLowerCase(); +#endif + + 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); +#if CASE_INSENSITIVE_FS + name = name.toLowerCase(); +#endif + for (var node = FS.nameTable[hash]; node; node = node.name_next) { + var nodeName = node.name; +#if CASE_INSENSITIVE_FS + nodeName = nodeName.toLowerCase(); +#endif + 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) { +#if ASSERTIONS + assert(typeof parent == 'object') +#endif + 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 & {{{ cDefs.S_IFMT }}}) === {{{ cDefs.S_IFREG }}}; + }, + isDir(mode) { + return (mode & {{{ cDefs.S_IFMT }}}) === {{{ cDefs.S_IFDIR }}}; + }, + isLink(mode) { + return (mode & {{{ cDefs.S_IFMT }}}) === {{{ cDefs.S_IFLNK }}}; + }, + isChrdev(mode) { + return (mode & {{{ cDefs.S_IFMT }}}) === {{{ cDefs.S_IFCHR }}}; + }, + isBlkdev(mode) { + return (mode & {{{ cDefs.S_IFMT }}}) === {{{ cDefs.S_IFBLK }}}; + }, + isFIFO(mode) { + return (mode & {{{ cDefs.S_IFMT }}}) === {{{ cDefs.S_IFIFO }}}; + }, + isSocket(mode) { + return (mode & {{{ cDefs.S_IFSOCK }}}) === {{{ cDefs.S_IFSOCK }}}; + }, + + // + // permissions + // + // convert O_* bitmask to a string for nodePermissions + flagsToPermissionString(flag) { + var perms = ['r', 'w', 'rw'][flag & 3]; + if ((flag & {{{ cDefs.O_TRUNC }}})) { + 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 & {{{ cDefs.S_IRUGO }}})) { + return {{{ cDefs.EACCES }}}; + } else if (perms.includes('w') && !(node.mode & {{{ cDefs.S_IWUGO }}})) { + return {{{ cDefs.EACCES }}}; + } else if (perms.includes('x') && !(node.mode & {{{ cDefs.S_IXUGO }}})) { + return {{{ cDefs.EACCES }}}; + } + return 0; + }, + mayLookup(dir) { + if (!FS.isDir(dir.mode)) return {{{ cDefs.ENOTDIR }}}; + var errCode = FS.nodePermissions(dir, 'x'); + if (errCode) return errCode; + if (!dir.node_ops.lookup) return {{{ cDefs.EACCES }}}; + return 0; + }, + mayCreate(dir, name) { + if (!FS.isDir(dir.mode)) { + return {{{ cDefs.ENOTDIR }}}; + } + try { + var node = FS.lookupNode(dir, name); + return {{{ cDefs.EEXIST }}}; + } 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 {{{ cDefs.ENOTDIR }}}; + } + if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { + return {{{ cDefs.EBUSY }}}; + } + } else { + if (FS.isDir(node.mode)) { + return {{{ cDefs.EISDIR }}}; + } + } + return 0; + }, + mayOpen(node, flags) { + if (!node) { + return {{{ cDefs.ENOENT }}}; + } + if (FS.isLink(node.mode)) { + return {{{ cDefs.ELOOP }}}; + } else if (FS.isDir(node.mode)) { + if (FS.flagsToPermissionString(flags) !== 'r' // opening for write + || (flags & ({{{ cDefs.O_TRUNC }}} | {{{ cDefs.O_CREAT }}}))) { // TODO: check for O_SEARCH? (== search for dir only) + return {{{ cDefs.EISDIR }}}; + } + } + return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + }, + checkOpExists(op, err) { + if (!op) { + throw new FS.ErrnoError(err); + } + return op; + }, + + // + // streams + // + 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({{{ cDefs.EMFILE }}}); + }, + getStreamChecked(fd) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError({{{ cDefs.EBADF }}}); + } + return stream; + }, + getStream: (fd) => FS.streams[fd], + // TODO parameterize this function such that a stream + // object isn't directly passed in. not possible until + // SOCKFS is completed. + createStream(stream, fd = -1) { +#if ASSERTIONS + assert(fd >= -1); +#endif + + // 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, {{{ cDefs.EPERM }}}) + setattr(arg, attr); + }, + + // + // devices + // + // each character device consists of a device id + stream operations. + // when a character device node is created (e.g. /dev/stdin) it is + // assigned a device id that lets us map back to the actual device. + // by default, each character device stream (e.g. _stdin) uses chrdev_stream_ops. + // however, once opened, the stream's operations are overridden with + // the operations of the device its underlying node maps back to. + 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({{{ cDefs.ESPIPE }}}); + } + }, + 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], + + // + // core + // + 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) { +#if ASSERTIONS + assert(FS.syncFSRequests > 0); +#endif + FS.syncFSRequests--; + return callback(errCode); + } + + function done(errCode) { + if (errCode) { + if (!done.errored) { + done.errored = true; + return doCallback(errCode); + } + return; + } + if (++completed >= mounts.length) { + doCallback(null); + } + }; + + // sync all mounts + mounts.forEach((mount) => { + if (!mount.type.syncfs) { + return done(null); + } + mount.type.syncfs(mount, populate, done); + }); + }, + mount(type, opts, mountpoint) { +#if ASSERTIONS + if (typeof type == 'string') { + // The filesystem was not included, and instead we have an error + // message stored in the variable. + throw type; + } +#endif + var root = mountpoint === '/'; + var pseudo = !mountpoint; + var node; + + if (root && FS.root) { + throw new FS.ErrnoError({{{ cDefs.EBUSY }}}); + } 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({{{ cDefs.EBUSY }}}); + } + + if (!FS.isDir(node.mode)) { + throw new FS.ErrnoError({{{ cDefs.ENOTDIR }}}); + } + } + + 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({{{ cDefs.EINVAL }}}); + } + + // destroy the nodes for this mount, and all its child mounts + var node = lookup.node; + var mount = node.mounted; + var mounts = FS.getMounts(mount); + + Object.keys(FS.nameTable).forEach((hash) => { + var current = FS.nameTable[hash]; + + while (current) { + var next = current.name_next; + + if (mounts.includes(current.mount)) { + FS.destroyNode(current); + } + + current = next; + } + }); + + // no longer a mountpoint + node.mounted = null; + + // remove this mount from the child mounts + var idx = node.mount.mounts.indexOf(mount); +#if ASSERTIONS + assert(idx !== -1); +#endif + node.mount.mounts.splice(idx, 1); + }, + lookup(parent, name) { + return parent.node_ops.lookup(parent, name); + }, + // generic function for all node creation + 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({{{ cDefs.EINVAL }}}); + } + if (name === '.' || name === '..') { + throw new FS.ErrnoError({{{ cDefs.EEXIST }}}); + } + var errCode = FS.mayCreate(parent, name); + if (errCode) { + throw new FS.ErrnoError(errCode); + } + if (!parent.node_ops.mknod) { + throw new FS.ErrnoError({{{ cDefs.EPERM }}}); + } + 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; + }, + // helpers to create specific types of nodes + create(path, mode = 0o666) { + mode &= {{{ cDefs.S_IALLUGO }}}; + mode |= {{{ cDefs.S_IFREG }}}; + return FS.mknod(path, mode, 0); + }, + mkdir(path, mode = 0o777) { + mode &= {{{ cDefs.S_IRWXUGO }}} | {{{ cDefs.S_ISVTX }}}; + mode |= {{{ cDefs.S_IFDIR }}}; +#if FS_DEBUG + if (FS.trackingDelegate['onMakeDirectory']) { + FS.trackingDelegate['onMakeDirectory'](path, mode); + } +#endif + return FS.mknod(path, mode, 0); + }, + // Creates a whole directory tree chain if it doesn't yet exist + 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 != {{{ cDefs.EEXIST }}}) throw e; + } + } + }, + mkdev(path, mode, dev) { + if (typeof dev == 'undefined') { + dev = mode; + mode = 0o666; + } + mode |= {{{ cDefs.S_IFCHR }}}; + return FS.mknod(path, mode, dev); + }, + symlink(oldpath, newpath) { + if (!PATH_FS.resolve(oldpath)) { + throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); + } + var lookup = FS.lookupPath(newpath, { parent: true }); + var parent = lookup.node; + if (!parent) { + throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); + } + 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({{{ cDefs.EPERM }}}); + } +#if FS_DEBUG + if (FS.trackingDelegate['onMakeSymlink']) { + FS.trackingDelegate['onMakeSymlink'](oldpath, newpath); + } +#endif + 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({{{ cDefs.ENOENT }}}); + // need to be part of the same mount + if (old_dir.mount !== new_dir.mount) { + throw new FS.ErrnoError({{{ cDefs.EXDEV }}}); + } + // 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({{{ cDefs.EINVAL }}}); + } + // 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({{{ cDefs.ENOTEMPTY }}}); + } + // 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({{{ cDefs.EPERM }}}); + } + if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { + throw new FS.ErrnoError({{{ cDefs.EBUSY }}}); + } + // 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); + } + } +#if FS_DEBUG + if (FS.trackingDelegate['willMovePath']) { + FS.trackingDelegate['willMovePath'](old_path, new_path); + } +#endif + // 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); + } +#if FS_DEBUG + if (FS.trackingDelegate['onMovePath']) { + FS.trackingDelegate['onMovePath'](old_path, new_path); + } +#endif + }, + 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({{{ cDefs.EPERM }}}); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError({{{ cDefs.EBUSY }}}); + } +#if FS_DEBUG + if (FS.trackingDelegate['willDeletePath']) { + FS.trackingDelegate['willDeletePath'](path); + } +#endif + parent.node_ops.rmdir(parent, name); + FS.destroyNode(node); +#if FS_DEBUG + if (FS.trackingDelegate['onDeletePath']) { + FS.trackingDelegate['onDeletePath'](path); + } +#endif + }, + readdir(path) { + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + var readdir = FS.checkOpExists(node.node_ops.readdir, {{{ cDefs.ENOTDIR }}}); + return readdir(node); + }, + unlink(path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + if (!parent) { + throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); + } + 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({{{ cDefs.EPERM }}}); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError({{{ cDefs.EBUSY }}}); + } +#if FS_DEBUG + if (FS.trackingDelegate['willDeletePath']) { + FS.trackingDelegate['willDeletePath'](path); + } +#endif + parent.node_ops.unlink(parent, name); + FS.destroyNode(node); +#if FS_DEBUG + if (FS.trackingDelegate['onDeletePath']) { + FS.trackingDelegate['onDeletePath'](path); + } +#endif + }, + readlink(path) { + var lookup = FS.lookupPath(path); + var link = lookup.node; + if (!link) { + throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); + } + if (!link.node_ops.readlink) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + 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, {{{ cDefs.EPERM }}}); + 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, {{{ cDefs.EPERM }}}) + return getattr(arg); + }, + lstat(path) { + return FS.stat(path, true); + }, + doChmod(stream, node, mode, dontFollow) { + FS.doSetAttr(stream, node, { + mode: (mode & {{{ cDefs.S_IALLUGO }}}) | (node.mode & ~{{{ cDefs.S_IALLUGO }}}), + 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({{{ cDefs.EISDIR }}}); + } + if (!FS.isFile(node.mode)) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + 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({{{ cDefs.EINVAL }}}); + } + 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 & {{{ cDefs.O_ACCMODE }}}) === {{{ cDefs.O_RDONLY}}}) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + 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, {{{ cDefs.EPERM }}}); + setattr(node, { + atime: atime, + mtime: mtime + }); + }, + open(path, flags, mode = 0o666) { + if (path === "") { + throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); + } + flags = typeof flags == 'string' ? FS_modeStringToFlags(flags) : flags; + if ((flags & {{{ cDefs.O_CREAT }}})) { + mode = (mode & {{{ cDefs.S_IALLUGO }}}) | {{{ cDefs.S_IFREG }}}; + } 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 & {{{ cDefs.O_NOFOLLOW }}}), + noent_okay: true + }); + node = lookup.node; + path = lookup.path; + } + // perhaps we need to create the node + var created = false; + if ((flags & {{{ cDefs.O_CREAT }}})) { + if (node) { + // if O_CREAT and O_EXCL are set, error out if the node already exists + if ((flags & {{{ cDefs.O_EXCL }}})) { + throw new FS.ErrnoError({{{ cDefs.EEXIST }}}); + } + } else if (isDirPath) { + throw new FS.ErrnoError({{{ cDefs.EISDIR }}}); + } 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({{{ cDefs.ENOENT }}}); + } + // can't truncate a device + if (FS.isChrdev(node.mode)) { + flags &= ~{{{ cDefs.O_TRUNC }}}; + } + // if asked only for a directory, then this must be one + if ((flags & {{{ cDefs.O_DIRECTORY }}}) && !FS.isDir(node.mode)) { + throw new FS.ErrnoError({{{ cDefs.ENOTDIR }}}); + } + // 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 & {{{ cDefs.O_TRUNC}}}) && !created) { + FS.truncate(node, 0); + } +#if FS_DEBUG + var trackingFlags = flags +#endif + // we've already handled these, don't pass down to the underlying vfs + flags &= ~({{{ cDefs.O_EXCL }}} | {{{ cDefs.O_TRUNC }}} | {{{ cDefs.O_NOFOLLOW }}}); + + // 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 expectToReceiveOnModule('logReadFiles') + if (Module['logReadFiles'] && !(flags & {{{ cDefs.O_WRONLY}}})) { + if (!(path in FS.readFiles)) { + FS.readFiles[path] = 1; +#if FS_DEBUG + dbg(`FS.trackingDelegate error on read file: ${path}`); +#endif + } + } +#endif +#if FS_DEBUG + if (FS.trackingDelegate['onOpenFile']) { + FS.trackingDelegate['onOpenFile'](path, trackingFlags); + } +#endif + return stream; + }, + close(stream) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError({{{ cDefs.EBADF }}}); + } + 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; +#if FS_DEBUG + if (stream.path && FS.trackingDelegate['onCloseFile']) { + FS.trackingDelegate['onCloseFile'](stream.path); + } +#endif + }, + isClosed(stream) { + return stream.fd === null; + }, + llseek(stream, offset, whence) { + if (FS.isClosed(stream)) { + throw new FS.ErrnoError({{{ cDefs.EBADF }}}); + } + if (!stream.seekable || !stream.stream_ops.llseek) { + throw new FS.ErrnoError({{{ cDefs.ESPIPE }}}); + } + if (whence != {{{ cDefs.SEEK_SET }}} && whence != {{{ cDefs.SEEK_CUR }}} && whence != {{{ cDefs.SEEK_END }}}) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + stream.position = stream.stream_ops.llseek(stream, offset, whence); + stream.ungotten = []; +#if FS_DEBUG + if (stream.path && FS.trackingDelegate['onSeekFile']) { + FS.trackingDelegate['onSeekFile'](stream.path, stream.position, whence); + } +#endif + return stream.position; + }, + read(stream, buffer, offset, length, position) { +#if ASSERTIONS + assert(offset >= 0); +#endif + if (length < 0 || position < 0) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + if (FS.isClosed(stream)) { + throw new FS.ErrnoError({{{ cDefs.EBADF }}}); + } + if ((stream.flags & {{{ cDefs.O_ACCMODE }}}) === {{{ cDefs.O_WRONLY}}}) { + throw new FS.ErrnoError({{{ cDefs.EBADF }}}); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError({{{ cDefs.EISDIR }}}); + } + if (!stream.stream_ops.read) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + var seeking = typeof position != 'undefined'; + if (!seeking) { + position = stream.position; + } else if (!stream.seekable) { + throw new FS.ErrnoError({{{ cDefs.ESPIPE }}}); + } + var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); + if (!seeking) stream.position += bytesRead; +#if FS_DEBUG + if (stream.path && FS.trackingDelegate['onReadFile']) { + FS.trackingDelegate['onReadFile'](stream.path, bytesRead); + } +#endif + return bytesRead; + }, + write(stream, buffer, offset, length, position, canOwn) { +#if ASSERTIONS + assert(offset >= 0); +#endif + if (length < 0 || position < 0) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + if (FS.isClosed(stream)) { + throw new FS.ErrnoError({{{ cDefs.EBADF }}}); + } + if ((stream.flags & {{{ cDefs.O_ACCMODE }}}) === {{{ cDefs.O_RDONLY}}}) { + throw new FS.ErrnoError({{{ cDefs.EBADF }}}); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError({{{ cDefs.EISDIR }}}); + } + if (!stream.stream_ops.write) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + if (stream.seekable && stream.flags & {{{ cDefs.O_APPEND }}}) { + // seek to the end before writing in append mode + FS.llseek(stream, 0, {{{ cDefs.SEEK_END }}}); + } + var seeking = typeof position != 'undefined'; + if (!seeking) { + position = stream.position; + } else if (!stream.seekable) { + throw new FS.ErrnoError({{{ cDefs.ESPIPE }}}); + } + var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); + if (!seeking) stream.position += bytesWritten; +#if FS_DEBUG + if (stream.path && FS.trackingDelegate['onWriteToFile']) { + FS.trackingDelegate['onWriteToFile'](stream.path, bytesWritten); + } +#endif + 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 & {{{ cDefs.PROT_WRITE }}}) !== 0 + && (flags & {{{ cDefs.MAP_PRIVATE}}}) === 0 + && (stream.flags & {{{ cDefs.O_ACCMODE }}}) !== {{{ cDefs.O_RDWR}}}) { + throw new FS.ErrnoError({{{ cDefs.EACCES }}}); + } + if ((stream.flags & {{{ cDefs.O_ACCMODE }}}) === {{{ cDefs.O_WRONLY}}}) { + throw new FS.ErrnoError({{{ cDefs.EACCES }}}); + } + if (!stream.stream_ops.mmap) { + throw new FS.ErrnoError({{{ cDefs.ENODEV }}}); + } + if (!length) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + return stream.stream_ops.mmap(stream, length, position, prot, flags); + }, + msync(stream, buffer, offset, length, mmapFlags) { +#if ASSERTIONS + assert(offset >= 0); +#endif + 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({{{ cDefs.ENOTTY }}}); + } + return stream.stream_ops.ioctl(stream, cmd, arg); + }, + readFile(path, opts = {}) { + opts.flags = opts.flags || {{{ cDefs.O_RDONLY }}}; + 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 || {{{ cDefs.O_TRUNC | cDefs.O_CREAT | cDefs.O_WRONLY }}}; + 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); + }, + + // + // module-level FS code + // + cwd: () => FS.currentPath, + chdir(path) { + var lookup = FS.lookupPath(path, { follow: true }); + if (lookup.node === null) { + throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); + } + if (!FS.isDir(lookup.node.mode)) { + throw new FS.ErrnoError({{{ cDefs.ENOTDIR }}}); + } + 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', {{{ cDefs.S_IFDIR | 0o777 }}}, {{{ cDefs.S_IXUGO }}}); + 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', {{{ cDefs.O_RDONLY }}}); + var stdout = FS.open('/dev/stdout', {{{ cDefs.O_WRONLY }}}); + var stderr = FS.open('/dev/stderr', {{{ cDefs.O_WRONLY }}}); +#if ASSERTIONS + assert(stdin.fd === 0, `invalid handle for stdin (${stdin.fd})`); + assert(stdout.fd === 1, `invalid handle for stdout (${stdout.fd})`); + assert(stderr.fd === 2, `invalid handle for stderr (${stderr.fd})`); +#endif + }, + staticInit() { + FS.nameTable = new Array(4096); + + FS.mount(MEMFS, {}, '/'); + + FS.createDefaultDirectories(); + FS.createDefaultDevices(); + FS.createSpecialDirectories(); + + FS.filesystems = { + 'MEMFS': MEMFS, +#if LibraryManager.has('libidbfs.js') + 'IDBFS': IDBFS, +#endif +#if LibraryManager.has('libnodefs.js') + 'NODEFS': NODEFS, +#endif +#if LibraryManager.has('libworkerfs.js') + 'WORKERFS': WORKERFS, +#endif +#if LibraryManager.has('libproxyfs.js') + 'PROXYFS': PROXYFS, +#endif + }; + }, + init(input, output, error) { +#if ASSERTIONS + assert(!FS.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); +#endif + FS.initialized = true; + + // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here +#if expectToReceiveOnModule('stdin') + input ??= Module['stdin']; +#endif +#if expectToReceiveOnModule('stdout') + output ??= Module['stdout']; +#endif +#if expectToReceiveOnModule('stderr') + error ??= Module['stderr']; +#endif + + FS.createStandardStreams(input, output, error); + }, + quit() { + FS.initialized = false; + // force-flush all streams, so we get musl std streams printed out +#if hasExportedSymbol('fflush') + _fflush(0); +#endif + // close all of our streams + for (var stream of FS.streams) { + if (stream) { + FS.close(stream); + } + } + }, + + // + // old v1 compatibility functions + // + 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 != {{{ cDefs.EEXIST }}}) 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 | {{{ cDefs.S_IWUGO }}}); + var stream = FS.open(node, {{{ cDefs.O_TRUNC | cDefs.O_CREAT | cDefs.O_WRONLY }}}); + 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({{{ charCode('\n') }}}); + } + }, + 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({{{ cDefs.EIO }}}); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError({{{ cDefs.EAGAIN }}}); + } + 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({{{ cDefs.EIO }}}); + } + } + if (length) { + stream.node.mtime = stream.node.ctime = Date.now(); + } + return i; + } + }); + return FS.mkdev(path, mode, dev); + }, + // Makes sure a file's contents are loaded. Returns whether the file has + // been loaded successfully. No-op for files that have been loaded already. + forceLoadFile(obj) { + if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; + #if FS_DEBUG + dbg(`forceLoadFile: ${obj.url}`) + #endif + 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) { + #if FS_DEBUG + dbg(`forceLoadFile exception: ${e}`); + #endif + throw new FS.ErrnoError({{{ cDefs.EIO }}}); + } + } + }, + // Creates a file record for lazy-loading from a URL. XXX This requires a synchronous + // XHR, which is not possible in browsers except in a web worker! Use preloading, + // either --preload-file in emcc or FS.createPreloadedFile + 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 +#if USE_CLOSURE_COMPILER + // Closure compiler requires us to declare all properties ahead of time. + getter = undefined; + _length = 0; + _chunkSize = 0; +#endif + 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"; + + #if SMALL_XHR_CHUNKS + var chunkSize = 1024; // Chunk size in bytes + #else + var chunkSize = 1024*1024; // Chunk size in bytes + #endif + + 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 = {}; + var keys = Object.keys(node.stream_ops); + keys.forEach((key) => { + var fn = node.stream_ops[key]; + stream_ops[key] = (...args) => { + FS.forceLoadFile(node); + return fn(...args); + }; + }); + function writeChunks(stream, buffer, offset, length, position) { + var contents = stream.node.contents; + if (position >= contents.length) + return 0; + var size = Math.min(contents.length - position, length); +#if ASSERTIONS + assert(size >= 0); +#endif + 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({{{ cDefs.ENOMEM }}}); + } + writeChunks(stream, HEAP8, ptr, length, position); + return { ptr, allocated: true }; + }; + node.stream_ops = stream_ops; + return node; + }, + + // Removed v1 functions +#if ASSERTIONS + absolutePath() { + abort('FS.absolutePath has been removed; use PATH_FS.resolve instead'); + }, + createFolder() { + abort('FS.createFolder has been removed; use FS.mkdir instead'); + }, + createLink() { + abort('FS.createLink has been removed; use FS.symlink instead'); + }, + joinPath() { + abort('FS.joinPath has been removed; use PATH.join instead'); + }, + mmapAlloc() { + abort('FS.mmapAlloc has been replaced by the top level function mmapAlloc'); + }, + standardizePath() { + abort('FS.standardizePath has been removed; use PATH.normalize instead'); + }, +#endif + }, + + $FS_mkdirTree__docs: ` + /** + * @param {number=} mode Optionally, the mode to create in. Uses mkdir's + * default if not set. + */`, + $FS_mkdirTree__deps: ['$FS'], + $FS_mkdirTree: (path, mode) => FS.mkdirTree(path, mode), +}; + +// Add library aliases for all the FS. as FS_. +for (let key in LibraryFS.$FS) { + const alias = `$FS_${key}`; + // Skip defining the alias if it already exists or if it's not an API function. + if (LibraryFS[alias] || key[0] !== key[0].toLowerCase()) { + continue; + } + LibraryFS[alias] = `(...args) => FS.${key}(...args)`; + LibraryFS[`${alias}__deps`] = ['$FS']; +} +addToLibrary(LibraryFS); diff --git a/src/lib/libfs_shared.js b/src/lib/libfs_shared.js new file mode 100644 index 0000000000000..c6728c753f2d6 --- /dev/null +++ b/src/lib/libfs_shared.js @@ -0,0 +1,198 @@ +/** + * @license + * Copyright 2032 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + $preloadPlugins__postset: () => addAtModule(makeModuleReceive('preloadPlugins')), + $preloadPlugins: [], + +#if !MINIMAL_RUNTIME + // Tries to handle an input byteArray using preload plugins. Returns true if + // it was handled. + $FS_handledByPreloadPlugin__internal: true, + $FS_handledByPreloadPlugin__deps: ['$preloadPlugins'], + $FS_handledByPreloadPlugin: async (byteArray, fullname) => { +#if LibraryManager.has('libbrowser.js') + // Ensure plugins are ready. + if (typeof Browser != 'undefined') Browser.init(); +#endif + + for (var plugin of preloadPlugins) { + if (plugin['canHandle'](fullname)) { +#if ASSERTIONS + assert(plugin['handle'].constructor.name === 'AsyncFunction', 'Filesystem plugin handlers must be async functions (See #24914)') +#endif + return plugin['handle'](byteArray, fullname); + } + } + // In no plugin handled this file then return the original/unmodified + // byteArray. + return byteArray; + }, + + // Legacy version of FS_preloadFile that uses callback rather than async + $FS_createPreloadedFile__deps: ['$FS_preloadFile'], + $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); + }, + + // Preloads a file asynchronously. You can call this before run, for example in + // preRun. run will be delayed until this file arrives and is set up. + // If you call it after run(), you may want to pause the main loop until it + // completes, if so, you can use the onload parameter to be notified when + // that happens. + // In addition to normally creating the file, we also asynchronously preload + // the browser-friendly versions of it: For an image, we preload an Image + // element and for an audio, and Audio. These are necessary for SDL_Image + // and _Mixer to find the files in preloadedImages/Audios. + // You can also call this with a typed array instead of a url. It will then + // do preloading for the Image/Audio part, as if the typed array were the + // result of an XHR that you did manually. + $FS_preloadFile__deps: [ + '$asyncLoad', + '$PATH_FS', + '$FS_createDataFile', + '$getUniqueRunDependency', + '$addRunDependency', + '$removeRunDependency', + '$FS_handledByPreloadPlugin', + ], + $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); + } + }, +#endif + + // convert the 'r', 'r+', etc. to it's corresponding set of O_* flags + $FS_modeStringToFlags: (str) => { + var flagModes = { + 'r': {{{ cDefs.O_RDONLY }}}, + 'r+': {{{ cDefs.O_RDWR }}}, + 'w': {{{ cDefs.O_TRUNC }}} | {{{ cDefs.O_CREAT }}} | {{{ cDefs.O_WRONLY }}}, + 'w+': {{{ cDefs.O_TRUNC }}} | {{{ cDefs.O_CREAT }}} | {{{ cDefs.O_RDWR }}}, + 'a': {{{ cDefs.O_APPEND }}} | {{{ cDefs.O_CREAT }}} | {{{ cDefs.O_WRONLY }}}, + 'a+': {{{ cDefs.O_APPEND }}} | {{{ cDefs.O_CREAT }}} | {{{ cDefs.O_RDWR }}}, + }; + var flags = flagModes[str]; + if (typeof flags == 'undefined') { + throw new Error(`Unknown file open mode: ${str}`); + } + return flags; + }, + $FS_getMode: (canRead, canWrite) => { + var mode = 0; + if (canRead) mode |= {{{ cDefs.S_IRUGO }}} | {{{ cDefs.S_IXUGO }}}; + if (canWrite) mode |= {{{ cDefs.S_IWUGO }}}; + return mode; + }, + + $FS_stdin_getChar_buffer: [], + + // getChar has 3 particular return values: + // a.) the next character represented as an integer + // b.) undefined to signal that no data is currently available + // c.) null to signal an EOF + $FS_stdin_getChar__deps: [ + '$FS_stdin_getChar_buffer', + '$intArrayFromString', + ], + $FS_stdin_getChar: () => { + if (!FS_stdin_getChar_buffer.length) { + var result = null; +#if ENVIRONMENT_MAY_BE_NODE + 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 +#endif +#if ENVIRONMENT_MAY_BE_WEB + if (globalThis.window?.prompt) { + // Browser. + result = window.prompt('Input: '); // returns null on cancel + if (result !== null) { + result += '\n'; + } + } else +#endif +#if ENVIRONMENT_MAY_BE_SHELL + if (globalThis.readline) { + /** @suppress{checkTypes, undefinedVars} */ + result = readline(); + if (result) { + result += '\n'; + } + } else +#endif + {} + if (!result) { + return null; + } + FS_stdin_getChar_buffer = intArrayFromString(result, true); + } + return FS_stdin_getChar_buffer.shift(); + }, + + $FS_unlink__deps: ['$FS'], + $FS_unlink: 'FS.unlink', + + $FS_createPath__deps: ['$FS'], + $FS_createPath: 'FS.createPath', + + $FS_createDevice__deps: ['$FS'], + $FS_createDevice: 'FS.createDevice', + + $FS_readFile__deps: ['$FS'], + $FS_readFile: 'FS.readFile', +}); + +// Normally only the FS things that the compiler sees are needed are included. +// FORCE_FILESYSTEM makes us always include the FS object, which lets the user +// call APIs on it from JS freely. +if (FORCE_FILESYSTEM) { + extraLibraryFuncs.push('$FS'); +} diff --git a/src/lib/libgetvalue.js b/src/lib/libgetvalue.js new file mode 100644 index 0000000000000..9a35b2cb1f6c6 --- /dev/null +++ b/src/lib/libgetvalue.js @@ -0,0 +1,64 @@ +/** + * @license + * Copyright 2022 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +// These functions are defined once here, then included in the library below +// under two different names. +function setValueImpl(ptr, value, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': {{{ makeSetValue('ptr', '0', 'value', 'i1') }}}; break; + case 'i8': {{{ makeSetValue('ptr', '0', 'value', 'i8') }}}; break; + case 'i16': {{{ makeSetValue('ptr', '0', 'value', 'i16') }}}; break; + case 'i32': {{{ makeSetValue('ptr', '0', 'value', 'i32') }}}; break; +#if WASM_BIGINT + case 'i64': {{{ makeSetValue('ptr', '0', 'value', 'i64') }}}; break; +#else + case 'i64': abort('to do setValue(i64) use WASM_BIGINT'); +#endif + case 'float': {{{ makeSetValue('ptr', '0', 'value', 'float') }}}; break; + case 'double': {{{ makeSetValue('ptr', '0', 'value', 'double') }}}; break; + case '*': {{{ makeSetValue('ptr', '0', 'value', '*') }}}; break; + default: abort(`invalid type for setValue: ${type}`); + } +} + +function getValueImpl(ptr, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': return {{{ makeGetValue('ptr', '0', 'i1') }}}; + case 'i8': return {{{ makeGetValue('ptr', '0', 'i8') }}}; + case 'i16': return {{{ makeGetValue('ptr', '0', 'i16') }}}; + case 'i32': return {{{ makeGetValue('ptr', '0', 'i32') }}}; +#if WASM_BIGINT + case 'i64': return {{{ makeGetValue('ptr', '0', 'i64') }}}; +#else + case 'i64': abort('to do getValue(i64) use WASM_BIGINT'); +#endif + case 'float': return {{{ makeGetValue('ptr', '0', 'float') }}}; + case 'double': return {{{ makeGetValue('ptr', '0', 'double') }}}; + case '*': return {{{ makeGetValue('ptr', '0', '*') }}}; + default: abort(`invalid type for getValue: ${type}`); + } +} + +var LibraryMemOps = { + $setValue__docs: ` + /** + * @param {number} ptr + * @param {number} value + * @param {string} type + */`, + $setValue: setValueImpl, + + $getValue__docs: ` + /** + * @param {number} ptr + * @param {string} type + */`, + $getValue: getValueImpl, +}; + +addToLibrary(LibraryMemOps); diff --git a/src/lib/libglemu.js b/src/lib/libglemu.js new file mode 100644 index 0000000000000..b3f6ff60fd867 --- /dev/null +++ b/src/lib/libglemu.js @@ -0,0 +1,3949 @@ +/** + * @license + * Copyright 2010 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +assert(LEGACY_GL_EMULATION, 'libglemu.js should only be included with LEGACY_GL_EMULATION set') +assert(!FULL_ES2, 'cannot emulate both ES2 and legacy GL'); +assert(!FULL_ES3, 'cannot emulate both ES3 and legacy GL'); + +{{{ + const copySigs = (func) => { + if (!RELOCATABLE) return ''; + return ` _${func}.sig = _emscripten_${func}.sig = orig_${func}.sig;`; + }; + const fromPtr = (arg) => { + if (CAN_ADDRESS_2GB) { + return `${arg} >>>= 0`; + } else if (MEMORY64) { + return `${arg} = Number(${arg})`; + } + return ''; + }; +}}} + +var LibraryGLEmulation = { + // GL emulation: provides misc. functionality not present in OpenGL ES 2.0 or WebGL + $GLEmulation__deps: ['$GLImmediateSetup', 'glEnable', 'glDisable', + 'glIsEnabled', 'glGetBooleanv', 'glGetIntegerv', 'glGetString', + 'glCreateShader', 'glShaderSource', 'glCompileShader', 'glAttachShader', + 'glDetachShader', 'glUseProgram', 'glDeleteProgram', 'glBindAttribLocation', + 'glLinkProgram', 'glBindBuffer', 'glGetFloatv', 'glHint', + 'glEnableVertexAttribArray', 'glDisableVertexAttribArray', + 'glVertexAttribPointer', 'glActiveTexture', '$stringToNewUTF8', + '$ptrToString', '$getEmscriptenSupportedExtensions', + ], + $GLEmulation__postset: +#if MAYBE_CLOSURE_COMPILER + // Forward declare GL functions that are overridden by GLEmulation here to appease Closure compiler. + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDrawArrays;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDrawElements;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glActiveTexture;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glEnable;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDisable;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glTexEnvf;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glTexEnvi;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glTexEnvfv;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetIntegerv;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glIsEnabled;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetBooleanv;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetString;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glCreateShader;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glShaderSource;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glCompileShader;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glAttachShader;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDetachShader;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glUseProgram;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDeleteProgram;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glBindAttribLocation;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glLinkProgram;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glBindBuffer;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetFloatv;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glHint;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glEnableVertexAttribArray;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDisableVertexAttribArray;' + + '/**@suppress {duplicate, undefinedVars}*/var _emscripten_glVertexAttribPointer;' + + '/**@suppress {duplicate, undefinedVars}*/var _glTexEnvf;' + + '/**@suppress {duplicate, undefinedVars}*/var _glTexEnvi;' + + '/**@suppress {duplicate, undefinedVars}*/var _glTexEnvfv;' + + '/**@suppress {duplicate, undefinedVars}*/var _glGetTexEnviv;' + + '/**@suppress {duplicate, undefinedVars}*/var _glGetTexEnvfv;' + +#endif + 'GLEmulation.init();', + $GLEmulation: { + // Fog support. Partial, we assume shaders are used that implement fog. We just pass them uniforms + fogStart: 0, + fogEnd: 1, + fogDensity: 1.0, + fogColor: null, + fogMode: 0x800, // GL_EXP + fogEnabled: false, + + // GL_CLIP_PLANE support + MAX_CLIP_PLANES: 6, + clipPlaneEnabled: [false, false, false, false, false, false], + clipPlaneEquation: [], + + // GL_LIGHTING support + lightingEnabled: false, + + lightModelAmbient: null, + lightModelLocalViewer: false, + lightModelTwoSide: false, + + materialAmbient: null, + materialDiffuse: null, + materialSpecular: null, + materialShininess: null, + materialEmission: null, + + MAX_LIGHTS: 8, + lightEnabled: [false, false, false, false, false, false, false, false], + lightAmbient: [], + lightDiffuse: [], + lightSpecular: [], + lightPosition: [], + // TODO attenuation modes of lights + + // GL_ALPHA_TEST support + alphaTestEnabled: false, + alphaTestFunc: 0x207, // GL_ALWAYS + alphaTestRef: 0.0, + + // GL_POINTS support. + pointSize: 1.0, + + // VAO support + vaos: [], + currentVao: null, + enabledVertexAttribArrays: {}, // helps with vao cleanups + + hasRunInit: false, + + // Find a token in a shader source string + findToken(source, token) { + function isIdentChar(ch) { + if (ch >= 48 && ch <= 57) // 0-9 + return true; + if (ch >= 65 && ch <= 90) // A-Z + return true; + if (ch >= 97 && ch <= 122) // a-z + return true; + return false; + } + var i = -1; + do { + i = source.indexOf(token, i + 1); + if (i < 0) { + break; + } + if (i > 0 && isIdentChar(source[i - 1])) { + continue; + } + i += token.length; + if (i < source.length - 1 && isIdentChar(source[i + 1])) { + continue; + } + return true; + } while (true); + return false; + }, + + init() { + // Do not activate immediate/emulation code (e.g. replace glDrawElements) + // when in FULL_ES2 mode. We do not need full emulation, we instead + // emulate client-side arrays etc. in FULL_ES2 code in a straightforward + // manner, and avoid not having a bound buffer be ambiguous between es2 + // emulation code and legacy gl emulation code. +#if FULL_ES2 + return; +#endif + + if (GLEmulation.hasRunInit) { + return; + } + GLEmulation.hasRunInit = true; + + GLEmulation.fogColor = new Float32Array(4); + + for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) { + GLEmulation.clipPlaneEquation[clipPlaneId] = new Float32Array(4); + } + + // set defaults for GL_LIGHTING + GLEmulation.lightModelAmbient = new Float32Array([0.2, 0.2, 0.2, 1.0]); + GLEmulation.materialAmbient = new Float32Array([0.2, 0.2, 0.2, 1.0]); + GLEmulation.materialDiffuse = new Float32Array([0.8, 0.8, 0.8, 1.0]); + GLEmulation.materialSpecular = new Float32Array([0.0, 0.0, 0.0, 1.0]); + GLEmulation.materialShininess = new Float32Array([0.0]); + GLEmulation.materialEmission = new Float32Array([0.0, 0.0, 0.0, 1.0]); + + for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) { + GLEmulation.lightAmbient[lightId] = new Float32Array([0.0, 0.0, 0.0, 1.0]); + GLEmulation.lightDiffuse[lightId] = lightId ? new Float32Array([0.0, 0.0, 0.0, 1.0]) : new Float32Array([1.0, 1.0, 1.0, 1.0]); + GLEmulation.lightSpecular[lightId] = lightId ? new Float32Array([0.0, 0.0, 0.0, 1.0]) : new Float32Array([1.0, 1.0, 1.0, 1.0]); + GLEmulation.lightPosition[lightId] = new Float32Array([0.0, 0.0, 1.0, 0.0]); + } + + + // Add some emulation workarounds + err('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work.'); +#if GL_UNSAFE_OPTS == 1 + err('WARNING: using emscripten GL emulation unsafe opts. If weirdness happens, try -sGL_UNSAFE_OPTS=0'); +#endif + + // XXX some of the capabilities we don't support may lead to incorrect rendering, if we do not emulate them in shaders + var validCapabilities = { + 0xB44: 1, // GL_CULL_FACE + 0xBE2: 1, // GL_BLEND + 0xBD0: 1, // GL_DITHER, + 0xB90: 1, // GL_STENCIL_TEST + 0xB71: 1, // GL_DEPTH_TEST + 0xC11: 1, // GL_SCISSOR_TEST + 0x8037: 1, // GL_POLYGON_OFFSET_FILL + 0x809E: 1, // GL_SAMPLE_ALPHA_TO_COVERAGE + 0x80A0: 1 // GL_SAMPLE_COVERAGE + }; + + var orig_glEnable = _glEnable; + _glEnable = _emscripten_glEnable = (cap) => { + // Clean up the renderer on any change to the rendering state. The optimization of + // skipping renderer setup is aimed at the case of multiple glDraw* right after each other + GLImmediate.lastRenderer?.cleanup(); + if (cap == 0xB60 /* GL_FOG */) { + if (GLEmulation.fogEnabled != true) { + GLImmediate.currentRenderer = null; // Fog parameter is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.fogEnabled = true; + } + return; + } else if ((cap >= 0x3000) && (cap < 0x3006) /* GL_CLIP_PLANE0 to GL_CLIP_PLANE5 */) { + var clipPlaneId = cap - 0x3000; + if (GLEmulation.clipPlaneEnabled[clipPlaneId] != true) { + GLImmediate.currentRenderer = null; // clip plane parameter is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.clipPlaneEnabled[clipPlaneId] = true; + } + return; + } else if ((cap >= 0x4000) && (cap < 0x4008) /* GL_LIGHT0 to GL_LIGHT7 */) { + var lightId = cap - 0x4000; + if (GLEmulation.lightEnabled[lightId] != true) { + GLImmediate.currentRenderer = null; // light parameter is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.lightEnabled[lightId] = true; + } + return; + } else if (cap == 0xB50 /* GL_LIGHTING */) { + if (GLEmulation.lightingEnabled != true) { + GLImmediate.currentRenderer = null; // light parameter is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.lightingEnabled = true; + } + return; + } else if (cap == 0xBC0 /* GL_ALPHA_TEST */) { + if (GLEmulation.alphaTestEnabled != true) { + GLImmediate.currentRenderer = null; // alpha testing is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.alphaTestEnabled = true; + } + return; + } else if (cap == 0xDE1 /* GL_TEXTURE_2D */) { + // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support + // it by forwarding to glEnableClientState + /* Actually, let's not, for now. (This sounds exceedingly broken) + * This is in gl_ps_workaround2.c. + _glEnableClientState(cap); + */ + return; + } else if (!(cap in validCapabilities)) { + return; + } + orig_glEnable(cap); + }; + {{{ copySigs('glEnable') }}} + + var orig_glDisable = _glDisable; + _glDisable = _emscripten_glDisable = (cap) => { + GLImmediate.lastRenderer?.cleanup(); + if (cap == 0xB60 /* GL_FOG */) { + if (GLEmulation.fogEnabled != false) { + GLImmediate.currentRenderer = null; // Fog parameter is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.fogEnabled = false; + } + return; + } else if ((cap >= 0x3000) && (cap < 0x3006) /* GL_CLIP_PLANE0 to GL_CLIP_PLANE5 */) { + var clipPlaneId = cap - 0x3000; + if (GLEmulation.clipPlaneEnabled[clipPlaneId] != false) { + GLImmediate.currentRenderer = null; // clip plane parameter is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.clipPlaneEnabled[clipPlaneId] = false; + } + return; + } else if ((cap >= 0x4000) && (cap < 0x4008) /* GL_LIGHT0 to GL_LIGHT7 */) { + var lightId = cap - 0x4000; + if (GLEmulation.lightEnabled[lightId] != false) { + GLImmediate.currentRenderer = null; // light parameter is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.lightEnabled[lightId] = false; + } + return; + } else if (cap == 0xB50 /* GL_LIGHTING */) { + if (GLEmulation.lightingEnabled != false) { + GLImmediate.currentRenderer = null; // light parameter is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.lightingEnabled = false; + } + return; + } else if (cap == 0xBC0 /* GL_ALPHA_TEST */) { + if (GLEmulation.alphaTestEnabled != false) { + GLImmediate.currentRenderer = null; // alpha testing is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.alphaTestEnabled = false; + } + return; + } else if (cap == 0xDE1 /* GL_TEXTURE_2D */) { + // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support + // it by forwarding to glDisableClientState + /* Actually, let's not, for now. (This sounds exceedingly broken) + * This is in gl_ps_workaround2.c. + _glDisableClientState(cap); + */ + return; + } else if (!(cap in validCapabilities)) { + return; + } + orig_glDisable(cap); + }; + {{{ copySigs('glDisable') }}} + + var orig_glIsEnabled = _glIsEnabled; + _glIsEnabled = _emscripten_glIsEnabled = (cap) => { + if (cap == 0xB60 /* GL_FOG */) { + return GLEmulation.fogEnabled ? 1 : 0; + } else if ((cap >= 0x3000) && (cap < 0x3006) /* GL_CLIP_PLANE0 to GL_CLIP_PLANE5 */) { + var clipPlaneId = cap - 0x3000; + return GLEmulation.clipPlaneEnabled[clipPlaneId] ? 1 : 0; + } else if ((cap >= 0x4000) && (cap < 0x4008) /* GL_LIGHT0 to GL_LIGHT7 */) { + var lightId = cap - 0x4000; + return GLEmulation.lightEnabled[lightId] ? 1 : 0; + } else if (cap == 0xB50 /* GL_LIGHTING */) { + return GLEmulation.lightingEnabled ? 1 : 0; + } else if (cap == 0xBC0 /* GL_ALPHA_TEST */) { + return GLEmulation.alphaTestEnabled ? 1 : 0; + } else if (!(cap in validCapabilities)) { + return 0; + } + return GLctx.isEnabled(cap); + }; + {{{ copySigs('glIsEnabled') }}} + + var orig_glGetBooleanv = _glGetBooleanv; + _glGetBooleanv = _emscripten_glGetBooleanv = (pname, p) => { + var attrib = GLEmulation.getAttributeFromCapability(pname); + if (attrib !== null) { + {{{ fromPtr('p') }}} + var result = GLImmediate.enabledClientAttributes[attrib]; + {{{ makeSetValue('p', '0', 'result === true ? 1 : 0', 'i8') }}}; + return; + } + orig_glGetBooleanv(pname, p); + }; + {{{ copySigs('glGetBooleanv') }}} + + var orig_glGetIntegerv = _glGetIntegerv; + _glGetIntegerv = _emscripten_glGetIntegerv = (pname, params) => { + {{{ fromPtr('params') }}} + switch (pname) { + case 0x84E2: pname = GLctx.MAX_TEXTURE_IMAGE_UNITS /* fake it */; break; // GL_MAX_TEXTURE_UNITS + case 0x8B4A: { // GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB + var result = GLctx.getParameter(GLctx.MAX_VERTEX_UNIFORM_VECTORS); + {{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply + return; + } + case 0x8B49: { // GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB + var result = GLctx.getParameter(GLctx.MAX_FRAGMENT_UNIFORM_VECTORS); + {{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply + return; + } + case 0x8B4B: { // GL_MAX_VARYING_FLOATS_ARB + var result = GLctx.getParameter(GLctx.MAX_VARYING_VECTORS); + {{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply + return; + } + case 0x8871: pname = GLctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS /* close enough */; break; // GL_MAX_TEXTURE_COORDS + case 0x807A: { // GL_VERTEX_ARRAY_SIZE + var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}}; + return; + } + case 0x807B: { // GL_VERTEX_ARRAY_TYPE + var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}}; + return; + } + case 0x807C: { // GL_VERTEX_ARRAY_STRIDE + var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}}; + return; + } + case 0x8081: { // GL_COLOR_ARRAY_SIZE + var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}}; + return; + } + case 0x8082: { // GL_COLOR_ARRAY_TYPE + var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}}; + return; + } + case 0x8083: { // GL_COLOR_ARRAY_STRIDE + var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}}; + return; + } + case 0x8088: { // GL_TEXTURE_COORD_ARRAY_SIZE + var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}}; + return; + } + case 0x8089: { // GL_TEXTURE_COORD_ARRAY_TYPE + var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}}; + return; + } + case 0x808A: { // GL_TEXTURE_COORD_ARRAY_STRIDE + var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; + {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}}; + return; + } + case 0x0D32: { // GL_MAX_CLIP_PLANES + {{{ makeSetValue('params', '0', 'GLEmulation.MAX_CLIP_PLANES', 'i32') }}}; // all implementations need to support atleast 6 + return; + } + case 0x0BA0: { // GL_MATRIX_MODE + {{{ makeSetValue('params', '0', 'GLImmediate.currentMatrix + 0x1700', 'i32') }}}; + return; + } + case 0x0BC1: { // GL_ALPHA_TEST_FUNC + {{{ makeSetValue('params', '0', 'GLEmulation.alphaTestFunc', 'i32') }}}; + return; + } + } + orig_glGetIntegerv(pname, params); + }; + {{{ copySigs('glGetIntegerv') }}} + + var orig_glGetString = _glGetString; + _glGetString = _emscripten_glGetString = (name_) => { + if (GL.stringCache[name_]) return GL.stringCache[name_]; + switch (name_) { + case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support + var ret = stringToNewUTF8(getEmscriptenSupportedExtensions(GLctx).join(' ') + + ' GL_EXT_texture_env_combine GL_ARB_texture_env_crossbar GL_ATI_texture_env_combine3 GL_NV_texture_env_combine4 GL_EXT_texture_env_dot3 GL_ARB_multitexture GL_ARB_vertex_buffer_object GL_EXT_framebuffer_object GL_ARB_vertex_program GL_ARB_fragment_program GL_ARB_shading_language_100 GL_ARB_shader_objects GL_ARB_vertex_shader GL_ARB_fragment_shader GL_ARB_texture_cube_map GL_EXT_draw_range_elements' + + (GL.currentContext.compressionExt ? ' GL_ARB_texture_compression GL_EXT_texture_compression_s3tc' : '') + + (GL.currentContext.anisotropicExt ? ' GL_EXT_texture_filter_anisotropic' : '') + ); + return GL.stringCache[name_] = {{{ to64('ret') }}}; + } + return orig_glGetString(name_); + }; + {{{ copySigs('glGetString') }}} + + // Do some automatic rewriting to work around GLSL differences. Note that this must be done in + // tandem with the rest of the program, by itself it cannot suffice. + // Note that we need to remember shader types for this rewriting, saving sources makes it easier to debug. + GL.shaderInfos = {}; +#if GL_DEBUG + GL.shaderSources = {}; + GL.shaderOriginalSources = {}; +#endif + var orig_glCreateShader = _glCreateShader; + _glCreateShader = _emscripten_glCreateShader = (shaderType) => { + var id = orig_glCreateShader(shaderType); + GL.shaderInfos[id] = { + type: shaderType, + ftransform: false + }; + return id; + }; + {{{ copySigs('glCreateShader') }}} + + function ensurePrecision(source) { + if (!/precision +(low|medium|high)p +float *;/.test(source)) { + source = '#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n' + source; + } + return source; + } + + var orig_glShaderSource = _glShaderSource; + _glShaderSource = _emscripten_glShaderSource = (shader, count, string, length) => { + {{{ fromPtr('string') }}} + {{{ fromPtr('length') }}} + var source = GL.getSource(shader, count, string, length); +#if GL_DEBUG + dbg("glShaderSource: Input: \n" + source); + GL.shaderOriginalSources[shader] = source; +#endif + // XXX We add attributes and uniforms to shaders. The program can ask for the # of them, and see the + // ones we generated, potentially confusing it? Perhaps we should hide them. + if (GL.shaderInfos[shader].type == GLctx.VERTEX_SHADER) { + // Replace ftransform() with explicit project/modelview transforms, and add position and matrix info. + var has_pm = source.search(/u_projection/) >= 0; + var has_mm = source.search(/u_modelView/) >= 0; + var has_pv = source.search(/a_position/) >= 0; + var need_pm = 0, need_mm = 0, need_pv = 0; + var old = source; + source = source.replace(/ftransform\(\)/g, '(u_projection * u_modelView * a_position)'); + if (old != source) need_pm = need_mm = need_pv = 1; + old = source; + source = source.replace(/gl_ProjectionMatrix/g, 'u_projection'); + if (old != source) need_pm = 1; + old = source; + source = source.replace(/gl_ModelViewMatrixTranspose\[2\]/g, 'vec4(u_modelView[0][2], u_modelView[1][2], u_modelView[2][2], u_modelView[3][2])'); // XXX extremely inefficient + if (old != source) need_mm = 1; + old = source; + source = source.replace(/gl_ModelViewMatrix/g, 'u_modelView'); + if (old != source) need_mm = 1; + old = source; + source = source.replace(/gl_Vertex/g, 'a_position'); + if (old != source) need_pv = 1; + old = source; + source = source.replace(/gl_ModelViewProjectionMatrix/g, '(u_projection * u_modelView)'); + if (old != source) need_pm = need_mm = 1; + if (need_pv && !has_pv) source = 'attribute vec4 a_position; \n' + source; + if (need_mm && !has_mm) source = 'uniform mat4 u_modelView; \n' + source; + if (need_pm && !has_pm) source = 'uniform mat4 u_projection; \n' + source; + GL.shaderInfos[shader].ftransform = need_pm || need_mm || need_pv; // we will need to provide the fixed function stuff as attributes and uniforms + for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { + // XXX To handle both regular texture mapping and cube mapping, we use vec4 for tex coordinates. + old = source; + var need_vtc = source.search(`v_texCoord${i}`) == -1; + source = source.replace(new RegExp(`gl_TexCoord\\[${i}\\]`, 'g'), `v_texCoord${i}`) + .replace(new RegExp(`gl_MultiTexCoord${i}`, 'g'), `a_texCoord${i}`); + if (source != old) { + source = `attribute vec4 a_texCoord${i}; \n${source}`; + if (need_vtc) { + source = `varying vec4 v_texCoord${i}; \n${source}`; + } + } + + old = source; + source = source.replace(new RegExp(`gl_TextureMatrix\\[${i}\\]`, 'g'), `u_textureMatrix${i}`); + if (source != old) { + source = `uniform mat4 u_textureMatrix${i}; \n${source}`; + } + } + if (source.includes('gl_FrontColor')) { + source = 'varying vec4 v_color; \n' + + source.replace(/gl_FrontColor/g, 'v_color'); + } + if (source.includes('gl_Color')) { + source = 'attribute vec4 a_color; \n' + + source.replace(/gl_Color/g, 'a_color'); + } + if (source.includes('gl_Normal')) { + source = 'attribute vec3 a_normal; \n' + + source.replace(/gl_Normal/g, 'a_normal'); + } + // fog + if (source.includes('gl_FogFragCoord')) { + source = 'varying float v_fogFragCoord; \n' + + source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord'); + } + } else { // Fragment shader + for (i = 0; i < GLImmediate.MAX_TEXTURES; i++) { + old = source; + source = source.replace(new RegExp(`gl_TexCoord\\[${i}\\]`, 'g'), `v_texCoord${i}`); + if (source != old) { + source = 'varying vec4 v_texCoord' + i + '; \n' + source; + } + } + if (source.includes('gl_Color')) { + source = 'varying vec4 v_color; \n' + source.replace(/gl_Color/g, 'v_color'); + } + if (source.includes('gl_Fog.color')) { + source = 'uniform vec4 u_fogColor; \n' + + source.replace(/gl_Fog.color/g, 'u_fogColor'); + } + if (source.includes('gl_Fog.end')) { + source = 'uniform float u_fogEnd; \n' + + source.replace(/gl_Fog.end/g, 'u_fogEnd'); + } + if (source.includes('gl_Fog.scale')) { + source = 'uniform float u_fogScale; \n' + + source.replace(/gl_Fog.scale/g, 'u_fogScale'); + } + if (source.includes('gl_Fog.density')) { + source = 'uniform float u_fogDensity; \n' + + source.replace(/gl_Fog.density/g, 'u_fogDensity'); + } + if (source.includes('gl_FogFragCoord')) { + source = 'varying float v_fogFragCoord; \n' + + source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord'); + } + source = ensurePrecision(source); + } +#if GL_DEBUG + GL.shaderSources[shader] = source; + dbg("glShaderSource: Output: \n" + source); +#endif + GLctx.shaderSource(GL.shaders[shader], source); + }; + {{{ copySigs('glShaderSource') }}} + + var orig_glCompileShader = _glCompileShader; + _glCompileShader = _emscripten_glCompileShader = (shader) => { + GLctx.compileShader(GL.shaders[shader]); +#if GL_DEBUG + if (!GLctx.getShaderParameter(GL.shaders[shader], GLctx.COMPILE_STATUS)) { + dbg(`Failed to compile shader: ${GLctx.getShaderInfoLog(GL.shaders[shader])}`); + dbg(`Info: ${JSON.stringify(GL.shaderInfos[shader])}`); + dbg(`Original source: ${GL.shaderOriginalSources[shader]}`); + dbg(`Source: ${GL.shaderSources[shader]}`); + abort('Shader compilation halt'); + } +#endif + }; + {{{ copySigs('glCompileShader') }}} + + GL.programShaders = {}; + var orig_glAttachShader = _glAttachShader; + _glAttachShader = _emscripten_glAttachShader = (program, shader) => { + GL.programShaders[program] ||= []; + GL.programShaders[program].push(shader); + orig_glAttachShader(program, shader); + }; + {{{ copySigs('glAttachShader') }}} + + var orig_glDetachShader = _glDetachShader; + _glDetachShader = _emscripten_glDetachShader = (program, shader) => { + var programShader = GL.programShaders[program]; + if (!programShader) { + err(`WARNING: _glDetachShader received invalid program: ${program}`); + return; + } + var index = programShader.indexOf(shader); + programShader.splice(index, 1); + orig_glDetachShader(program, shader); + }; + {{{ copySigs('glDetachShader') }}} + + var orig_glUseProgram = _glUseProgram; + _glUseProgram = _emscripten_glUseProgram = (program) => { +#if GL_DEBUG + if (GL.debug) { + dbg('[using program with shaders]'); + if (program) { + GL.programShaders[program].forEach((shader) => { + dbg(` shader ${shader}, original source: ${GL.shaderOriginalSources[shader]}`); + dbg(` Source: ${GL.shaderSources[shader]}`); + }); + } + } +#endif + if (GL.currProgram != program) { + GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that. + GL.currProgram = program; + GLImmediate.fixedFunctionProgram = 0; + orig_glUseProgram(program); + } + } + {{{ copySigs('glUseProgram') }}} + + var orig_glDeleteProgram = _glDeleteProgram; + _glDeleteProgram = _emscripten_glDeleteProgram = (program) => { + orig_glDeleteProgram(program); + if (program == GL.currProgram) { + GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that. + GL.currProgram = 0; + } + }; + {{{ copySigs('glDeleteProgram') }}} + + // If attribute 0 was not bound, bind it to 0 for WebGL performance reasons. Track if 0 is free for that. + var zeroUsedPrograms = {}; + var orig_glBindAttribLocation = _glBindAttribLocation; + _glBindAttribLocation = _emscripten_glBindAttribLocation = (program, index, name) => { + if (index == 0) zeroUsedPrograms[program] = true; + orig_glBindAttribLocation(program, index, name); + }; + {{{ copySigs('glBindAttribLocation') }}} + + var orig_glLinkProgram = _glLinkProgram; + _glLinkProgram = _emscripten_glLinkProgram = (program) => { + if (!(program in zeroUsedPrograms)) { + GLctx.bindAttribLocation(GL.programs[program], 0, 'a_position'); + } + orig_glLinkProgram(program); + }; + {{{ copySigs('glLinkProgram') }}} + + var orig_glBindBuffer = _glBindBuffer; + _glBindBuffer = _emscripten_glBindBuffer = (target, buffer) => { + orig_glBindBuffer(target, buffer); + if (target == GLctx.ARRAY_BUFFER) { + if (GLEmulation.currentVao) { +#if ASSERTIONS + assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao'); +#endif + GLEmulation.currentVao.arrayBuffer = buffer; + } + } else if (target == GLctx.ELEMENT_ARRAY_BUFFER) { + if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer; + } + }; + {{{ copySigs('glBindBuffer') }}} + + var orig_glGetFloatv = _glGetFloatv; + _glGetFloatv = _emscripten_glGetFloatv = (pname, params) => { + {{{ fromPtr('params') }}} + if (pname == 0xBA6) { // GL_MODELVIEW_MATRIX + HEAPF32.set(GLImmediate.matrix[0/*m*/], {{{ getHeapOffset('params', 'float') }}}); + } else if (pname == 0xBA7) { // GL_PROJECTION_MATRIX + HEAPF32.set(GLImmediate.matrix[1/*p*/], {{{ getHeapOffset('params', 'float') }}}); + } else if (pname == 0xBA8) { // GL_TEXTURE_MATRIX + HEAPF32.set(GLImmediate.matrix[2/*t*/ + GLImmediate.clientActiveTexture], {{{ getHeapOffset('params', 'float') }}}); + } else if (pname == 0xB66) { // GL_FOG_COLOR + HEAPF32.set(GLEmulation.fogColor, {{{ getHeapOffset('params', 'float') }}}); + } else if (pname == 0xB63) { // GL_FOG_START + {{{ makeSetValue('params', '0', 'GLEmulation.fogStart', 'float') }}}; + } else if (pname == 0xB64) { // GL_FOG_END + {{{ makeSetValue('params', '0', 'GLEmulation.fogEnd', 'float') }}}; + } else if (pname == 0xB62) { // GL_FOG_DENSITY + {{{ makeSetValue('params', '0', 'GLEmulation.fogDensity', 'float') }}}; + } else if (pname == 0xB65) { // GL_FOG_MODE + {{{ makeSetValue('params', '0', 'GLEmulation.fogMode', 'float') }}}; + } else if (pname == 0xB53) { // GL_LIGHT_MODEL_AMBIENT + {{{ makeSetValue('params', '0', 'GLEmulation.lightModelAmbient[0]', 'float') }}}; + {{{ makeSetValue('params', '4', 'GLEmulation.lightModelAmbient[1]', 'float') }}}; + {{{ makeSetValue('params', '8', 'GLEmulation.lightModelAmbient[2]', 'float') }}}; + {{{ makeSetValue('params', '12', 'GLEmulation.lightModelAmbient[3]', 'float') }}}; + } else if (pname == 0xBC2) { // GL_ALPHA_TEST_REF + {{{ makeSetValue('params', '0', 'GLEmulation.alphaTestRef', 'float') }}}; + } else { + orig_glGetFloatv(pname, params); + } + }; + {{{ copySigs('glGetFloatv') }}} + + var orig_glHint = _glHint; + _glHint = _emscripten_glHint = (target, mode) => { + if (target == 0x84EF) { // GL_TEXTURE_COMPRESSION_HINT + return; + } + orig_glHint(target, mode); + }; + {{{ copySigs('glHint') }}} + + var orig_glEnableVertexAttribArray = _glEnableVertexAttribArray; + _glEnableVertexAttribArray = _emscripten_glEnableVertexAttribArray = (index) => { + orig_glEnableVertexAttribArray(index); + GLEmulation.enabledVertexAttribArrays[index] = 1; + if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1; + }; + {{{ copySigs('glEnableVertexAttribArray') }}} + + var orig_glDisableVertexAttribArray = _glDisableVertexAttribArray; + _glDisableVertexAttribArray = _emscripten_glDisableVertexAttribArray = (index) => { + orig_glDisableVertexAttribArray(index); + delete GLEmulation.enabledVertexAttribArrays[index]; + if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index]; + }; + {{{ copySigs('glDisableVertexAttribArray') }}} + + var orig_glVertexAttribPointer = _glVertexAttribPointer; + _glVertexAttribPointer = _emscripten_glVertexAttribPointer = (index, size, type, normalized, stride, pointer) => { + orig_glVertexAttribPointer(index, size, type, normalized, stride, pointer); + if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though + GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer]; + } + }; + {{{ copySigs('glVertexAttribPointer') }}} + }, + + getAttributeFromCapability(cap) { + var attrib = null; + switch (cap) { + case 0xDE1: // GL_TEXTURE_2D - XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support it +#if ASSERTIONS + abort("GL_TEXTURE_2D is not a spec-defined capability for gl{Enable,Disable}ClientState."); +#endif + // Fall through: + case 0x8078: // GL_TEXTURE_COORD_ARRAY + attrib = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture; break; + case 0x8074: // GL_VERTEX_ARRAY + attrib = GLImmediate.VERTEX; break; + case 0x8075: // GL_NORMAL_ARRAY + attrib = GLImmediate.NORMAL; break; + case 0x8076: // GL_COLOR_ARRAY + attrib = GLImmediate.COLOR; break; + } + return attrib; + }, + }, + + glDeleteObject__deps: ['glDeleteProgram', 'glDeleteShader'], + glDeleteObject: (id) => { + if (GL.programs[id]) { + _glDeleteProgram(id); + } else if (GL.shaders[id]) { + _glDeleteShader(id); + } else { + err(`WARNING: deleteObject received invalid id: ${id}`); + } + }, + glDeleteObjectARB: 'glDeleteObject', + + glGetObjectParameteriv__deps: ['glGetProgramiv', 'glGetShaderiv'], + glGetObjectParameteriv: (id, type, result) => { + if (GL.programs[id]) { + if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB + var log = GLctx.getProgramInfoLog(GL.programs[id]); + if (log === null) log = '(unknown error)'; + {{{ makeSetValue('result', '0', 'log.length', 'i32') }}}; + return; + } + _glGetProgramiv(id, type, result); + } else if (GL.shaders[id]) { + if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB + var log = GLctx.getShaderInfoLog(GL.shaders[id]); + if (log === null) log = '(unknown error)'; + {{{ makeSetValue('result', '0', 'log.length', 'i32') }}}; + return; + } else if (type == 0x8B88) { // GL_OBJECT_SHADER_SOURCE_LENGTH_ARB + var source = GLctx.getShaderSource(GL.shaders[id]); + if (source === null) return; // If an error occurs, nothing will be written to result + {{{ makeSetValue('result', '0', 'source.length', 'i32') }}}; + return; + } + _glGetShaderiv(id, type, result); + } else { + err(`WARNING: getObjectParameteriv received invalid id: ${id}`); + } + }, + glGetObjectParameterivARB: 'glGetObjectParameteriv', + + glGetInfoLog__deps: ['glGetProgramInfoLog', 'glGetShaderInfoLog'], + glGetInfoLog: (id, maxLength, length, infoLog) => { + if (GL.programs[id]) { + _glGetProgramInfoLog(id, maxLength, length, infoLog); + } else if (GL.shaders[id]) { + _glGetShaderInfoLog(id, maxLength, length, infoLog); + } else { + err(`WARNING: glGetInfoLog received invalid id: ${id}`); + } + }, + glGetInfoLogARB: 'glGetInfoLog', + + glBindProgram: (type, id) => { +#if ASSERTIONS + assert(id == 0); +#endif + }, + glBindProgramARB: 'glBindProgram', + + glGetPointerv: (name, p) => { + var attribute; + switch (name) { + case 0x808E: // GL_VERTEX_ARRAY_POINTER + attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; break; + case 0x8090: // GL_COLOR_ARRAY_POINTER + attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break; + case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER + attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; break; + default: + GL.recordError(0x500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + err(`GL_INVALID_ENUM in glGetPointerv: Unsupported name ${name}!`); +#endif + return; + } + {{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}}; + }, + + // GL Immediate mode + + // See comment in GLEmulation.init() +#if !FULL_ES2 + $GLImmediate__postset: 'GLImmediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(() => GLImmediate.init());', +#endif + $GLImmediate__deps: ['$Browser', '$GL', '$GLEmulation'], + $GLImmediate: { + MapTreeLib: null, + spawnMapTreeLib: () => { + /** + * A naive implementation of a map backed by an array, and accessed by + * naive iteration along the array. (hashmap with only one bucket) + * @constructor + */ + function CNaiveListMap() { + var list = []; + + this.insert = function CNaiveListMap_insert(key, val) { + if (this.contains(key|0)) return false; + list.push([key, val]); + return true; + }; + + var __contains_i; + this.contains = function CNaiveListMap_contains(key) { + for (__contains_i = 0; __contains_i < list.length; ++__contains_i) { + if (list[__contains_i][0] === key) return true; + } + return false; + }; + + var __get_i; + this.get = function CNaiveListMap_get(key) { + for (__get_i = 0; __get_i < list.length; ++__get_i) { + if (list[__get_i][0] === key) return list[__get_i][1]; + } + return undefined; + }; + }; + + /** + * A tree of map nodes. + * Uses `KeyView`s to allow descending the tree without garbage. + * Example: { + * // Create our map object. + * var map = new ObjTreeMap(); + * + * // Grab the static keyView for the map. + * var keyView = map.GetStaticKeyView(); + * + * // Let's make a map for: + * // root: + * // 1: + * // 2: + * // 5: "Three, sir!" + * // 3: "Three!" + * + * // Note how we can chain together `Reset` and `Next` to + * // easily descend based on multiple key fragments. + * keyView.Reset().Next(1).Next(2).Next(5).Set("Three, sir!"); + * keyView.Reset().Next(1).Next(2).Next(3).Set("Three!"); + * } + * @constructor + */ + function CMapTree() { + /** @constructor */ + function CNLNode() { + var map = new CNaiveListMap(); + + this.child = function CNLNode_child(keyFrag) { + if (!map.contains(keyFrag|0)) { + map.insert(keyFrag|0, new CNLNode()); + } + return map.get(keyFrag|0); + }; + + this.value = undefined; + this.get = function CNLNode_get() { + return this.value; + }; + + this.set = function CNLNode_set(val) { + this.value = val; + }; + } + + /** @constructor */ + function CKeyView(root) { + var cur; + + this.reset = function CKeyView_reset() { + cur = root; + return this; + }; + this.reset(); + + this.next = function CKeyView_next(keyFrag) { + cur = cur.child(keyFrag); + return this; + }; + + this.get = function CKeyView_get() { + return cur.get(); + }; + + this.set = function CKeyView_set(val) { + cur.set(val); + }; + }; + + var root; + var staticKeyView; + + this.createKeyView = function CNLNode_createKeyView() { + return new CKeyView(root); + } + + this.clear = function CNLNode_clear() { + root = new CNLNode(); + staticKeyView = this.createKeyView(); + }; + this.clear(); + + this.getStaticKeyView = function CNLNode_getStaticKeyView() { + staticKeyView.reset(); + return staticKeyView; + }; + }; + + // Exports: + return { + create: () => new CMapTree(), + }; + }, + + TexEnvJIT: null, + spawnTexEnvJIT: () => { + // GL defs: + var GL_TEXTURE0 = 0x84C0; + var GL_TEXTURE_1D = 0xDE0; + var GL_TEXTURE_2D = 0xDE1; + var GL_TEXTURE_3D = 0x806f; + var GL_TEXTURE_CUBE_MAP = 0x8513; + var GL_TEXTURE_ENV = 0x2300; + var GL_TEXTURE_ENV_MODE = 0x2200; + var GL_TEXTURE_ENV_COLOR = 0x2201; + var GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515; + var GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516; + var GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517; + var GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518; + var GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519; + var GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A; + + var GL_SRC0_RGB = 0x8580; + var GL_SRC1_RGB = 0x8581; + var GL_SRC2_RGB = 0x8582; + + var GL_SRC0_ALPHA = 0x8588; + var GL_SRC1_ALPHA = 0x8589; + var GL_SRC2_ALPHA = 0x858A; + + var GL_OPERAND0_RGB = 0x8590; + var GL_OPERAND1_RGB = 0x8591; + var GL_OPERAND2_RGB = 0x8592; + + var GL_OPERAND0_ALPHA = 0x8598; + var GL_OPERAND1_ALPHA = 0x8599; + var GL_OPERAND2_ALPHA = 0x859A; + + var GL_COMBINE_RGB = 0x8571; + var GL_COMBINE_ALPHA = 0x8572; + + var GL_RGB_SCALE = 0x8573; + var GL_ALPHA_SCALE = 0xD1C; + + // env.mode + var GL_ADD = 0x104; + var GL_BLEND = 0xBE2; + var GL_REPLACE = 0x1E01; + var GL_MODULATE = 0x2100; + var GL_DECAL = 0x2101; + var GL_COMBINE = 0x8570; + + // env.color/alphaCombiner + //var GL_ADD = 0x104; + //var GL_REPLACE = 0x1E01; + //var GL_MODULATE = 0x2100; + var GL_SUBTRACT = 0x84E7; + var GL_INTERPOLATE = 0x8575; + + // env.color/alphaSrc + var GL_TEXTURE = 0x1702; + var GL_CONSTANT = 0x8576; + var GL_PRIMARY_COLOR = 0x8577; + var GL_PREVIOUS = 0x8578; + + // env.color/alphaOp + var GL_SRC_COLOR = 0x300; + var GL_ONE_MINUS_SRC_COLOR = 0x301; + var GL_SRC_ALPHA = 0x302; + var GL_ONE_MINUS_SRC_ALPHA = 0x303; + + var GL_RGB = 0x1907; + var GL_RGBA = 0x1908; + + // Our defs: + var TEXENVJIT_NAMESPACE_PREFIX = "tej_"; + // Not actually constant, as they can be changed between JIT passes: + var TEX_UNIT_UNIFORM_PREFIX = "uTexUnit"; + var TEX_COORD_VARYING_PREFIX = "vTexCoord"; + var PRIM_COLOR_VARYING = "vPrimColor"; + var TEX_MATRIX_UNIFORM_PREFIX = "uTexMatrix"; + + // Static vars: + var s_texUnits = null; //[]; + var s_activeTexture = 0; + + var s_requiredTexUnitsForPass = []; + + // Static funcs: + function abort_noSupport(info) { + abort("[TexEnvJIT] ABORT: No support: " + info); + } + + function abort_sanity(info) { + abort("[TexEnvJIT] ABORT: Sanity failure: " + info); + } + + function genTexUnitSampleExpr(texUnitID) { + var texUnit = s_texUnits[texUnitID]; + var texType = texUnit.getTexType(); + + var func = null; + switch (texType) { + case GL_TEXTURE_1D: + func = "texture2D"; + break; + case GL_TEXTURE_2D: + func = "texture2D"; + break; + case GL_TEXTURE_3D: + return abort_noSupport("No support for 3D textures."); + case GL_TEXTURE_CUBE_MAP: + func = "textureCube"; + break; + default: + return abort_sanity(`Unknown texType: ${ptrToString(texType)}`); + } + + var texCoordExpr = TEX_COORD_VARYING_PREFIX + texUnitID; + if (TEX_MATRIX_UNIFORM_PREFIX != null) { + texCoordExpr = `(${TEX_MATRIX_UNIFORM_PREFIX}${texUnitID} * ${texCoordExpr})`; + } + return `${func}(${TEX_UNIT_UNIFORM_PREFIX}${texUnitID}, ${texCoordExpr}.xy)`; + } + + function getTypeFromCombineOp(op) { + switch (op) { + case GL_SRC_COLOR: + case GL_ONE_MINUS_SRC_COLOR: + return "vec3"; + case GL_SRC_ALPHA: + case GL_ONE_MINUS_SRC_ALPHA: + return "float"; + } + + return abort_noSupport("Unsupported combiner op: " + ptrToString(op)); + } + + function getCurTexUnit() { + return s_texUnits[s_activeTexture]; + } + + function genCombinerSourceExpr(texUnitID, constantExpr, previousVar, + src, op) + { + var srcExpr = null; + switch (src) { + case GL_TEXTURE: + srcExpr = genTexUnitSampleExpr(texUnitID); + break; + case GL_CONSTANT: + srcExpr = constantExpr; + break; + case GL_PRIMARY_COLOR: + srcExpr = PRIM_COLOR_VARYING; + break; + case GL_PREVIOUS: + srcExpr = previousVar; + break; + default: + return abort_noSupport("Unsupported combiner src: " + ptrToString(src)); + } + + var expr = null; + switch (op) { + case GL_SRC_COLOR: + expr = srcExpr + ".rgb"; + break; + case GL_ONE_MINUS_SRC_COLOR: + expr = "(vec3(1.0) - " + srcExpr + ".rgb)"; + break; + case GL_SRC_ALPHA: + expr = srcExpr + ".a"; + break; + case GL_ONE_MINUS_SRC_ALPHA: + expr = "(1.0 - " + srcExpr + ".a)"; + break; + default: + return abort_noSupport("Unsupported combiner op: " + ptrToString(op)); + } + + return expr; + } + + function valToFloatLiteral(val) { + if (val == Math.round(val)) return val + '.0'; + return val; + } + + + // Classes: + /** @constructor */ + function CTexEnv() { + this.mode = GL_MODULATE; + this.colorCombiner = GL_MODULATE; + this.alphaCombiner = GL_MODULATE; + this.colorScale = 1; + this.alphaScale = 1; + this.envColor = [0, 0, 0, 0]; + + this.colorSrc = [ + GL_TEXTURE, + GL_PREVIOUS, + GL_CONSTANT + ]; + this.alphaSrc = [ + GL_TEXTURE, + GL_PREVIOUS, + GL_CONSTANT + ]; + this.colorOp = [ + GL_SRC_COLOR, + GL_SRC_COLOR, + GL_SRC_ALPHA + ]; + this.alphaOp = [ + GL_SRC_ALPHA, + GL_SRC_ALPHA, + GL_SRC_ALPHA + ]; + + // Map GLenums to small values to efficiently pack the enums to bits for tighter access. + this.traverseKey = { + // mode + 0x1E01 /* GL_REPLACE */: 0, + 0x2100 /* GL_MODULATE */: 1, + 0x104 /* GL_ADD */: 2, + 0xBE2 /* GL_BLEND */: 3, + 0x2101 /* GL_DECAL */: 4, + 0x8570 /* GL_COMBINE */: 5, + + // additional color and alpha combiners + 0x84E7 /* GL_SUBTRACT */: 3, + 0x8575 /* GL_INTERPOLATE */: 4, + + // color and alpha src + 0x1702 /* GL_TEXTURE */: 0, + 0x8576 /* GL_CONSTANT */: 1, + 0x8577 /* GL_PRIMARY_COLOR */: 2, + 0x8578 /* GL_PREVIOUS */: 3, + + // color and alpha op + 0x300 /* GL_SRC_COLOR */: 0, + 0x301 /* GL_ONE_MINUS_SRC_COLOR */: 1, + 0x302 /* GL_SRC_ALPHA */: 2, + 0x303 /* GL_ONE_MINUS_SRC_ALPHA */: 3 + }; + + // The tuple (key0,key1,key2) uniquely identifies the state of the variables in CTexEnv. + // -1 on key0 denotes 'the whole cached key is dirty' + this.key0 = -1; + this.key1 = 0; + this.key2 = 0; + + this.computeKey0 = function() { + var k = this.traverseKey; + var key = k[this.mode] * 1638400; // 6 distinct values. + key += k[this.colorCombiner] * 327680; // 5 distinct values. + key += k[this.alphaCombiner] * 65536; // 5 distinct values. + // The above three fields have 6*5*5=150 distinct values -> 8 bits. + key += (this.colorScale-1) * 16384; // 10 bits used. + key += (this.alphaScale-1) * 4096; // 12 bits used. + key += k[this.colorSrc[0]] * 1024; // 14 + key += k[this.colorSrc[1]] * 256; // 16 + key += k[this.colorSrc[2]] * 64; // 18 + key += k[this.alphaSrc[0]] * 16; // 20 + key += k[this.alphaSrc[1]] * 4; // 22 + key += k[this.alphaSrc[2]]; // 24 bits used total. + return key; + } + this.computeKey1 = function() { + var k = this.traverseKey; + var key = k[this.colorOp[0]] * 4096; + key += k[this.colorOp[1]] * 1024; + key += k[this.colorOp[2]] * 256; + key += k[this.alphaOp[0]] * 16; + key += k[this.alphaOp[1]] * 4; + key += k[this.alphaOp[2]]; + return key; + } + // TODO: remove this. The color should not be part of the key! + this.computeKey2 = function() { + return this.envColor[0] * 16777216 + this.envColor[1] * 65536 + this.envColor[2] * 256 + 1 + this.envColor[3]; + } + this.recomputeKey = function() { + this.key0 = this.computeKey0(); + this.key1 = this.computeKey1(); + this.key2 = this.computeKey2(); + } + this.invalidateKey = function() { + this.key0 = -1; // The key of this texture unit must be recomputed when rendering the next time. + GLImmediate.currentRenderer = null; // The currently used renderer must be re-evaluated at next render. + } + } + + /** @constructor */ + function CTexUnit() { + this.env = new CTexEnv(); + this.enabled_tex1D = false; + this.enabled_tex2D = false; + this.enabled_tex3D = false; + this.enabled_texCube = false; + this.texTypesEnabled = 0; // A bitfield combination of the four flags above, used for fast access to operations. + + this.traverseState = function CTexUnit_traverseState(keyView) { + if (this.texTypesEnabled) { + if (this.env.key0 == -1) { + this.env.recomputeKey(); + } + keyView.next(this.texTypesEnabled | (this.env.key0 << 4)); + keyView.next(this.env.key1); + keyView.next(this.env.key2); + } else { + // For correctness, must traverse a zero value, theoretically a subsequent integer key could collide with this value otherwise. + keyView.next(0); + } + }; + }; + + // Class impls: + CTexUnit.prototype.enabled = function CTexUnit_enabled() { + return this.texTypesEnabled; + } + + CTexUnit.prototype.genPassLines = function CTexUnit_genPassLines(passOutputVar, passInputVar, texUnitID) { + if (!this.enabled()) { + return ["vec4 " + passOutputVar + " = " + passInputVar + ";"]; + } + var lines = this.env.genPassLines(passOutputVar, passInputVar, texUnitID).join('\n'); + + var texLoadLines = ''; + var texLoadRegex = /(texture.*?\(.*?\))/g; + var loadCounter = 0; + var load; + + // As an optimization, merge duplicate identical texture loads to one var. + while (load = texLoadRegex.exec(lines)) { + var texLoadExpr = load[1]; + var secondOccurrence = lines.slice(load.index+1).indexOf(texLoadExpr); + if (secondOccurrence != -1) { // And also has a second occurrence of same load expression.. + // Create new var to store the common load. + var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_"; + var texLoadVar = prefix + 'texload' + loadCounter++; + var texLoadLine = 'vec4 ' + texLoadVar + ' = ' + texLoadExpr + ';\n'; + texLoadLines += texLoadLine + '\n'; // Store the generated texture load statements in a temp string to not confuse regex search in progress. + lines = lines.split(texLoadExpr).join(texLoadVar); + // Reset regex search, since we modified the string. + texLoadRegex = /(texture.*\(.*\))/g; + } + } + return [texLoadLines + lines]; + } + + CTexUnit.prototype.getTexType = function CTexUnit_getTexType() { + if (this.enabled_texCube) { + return GL_TEXTURE_CUBE_MAP; + } else if (this.enabled_tex3D) { + return GL_TEXTURE_3D; + } else if (this.enabled_tex2D) { + return GL_TEXTURE_2D; + } else if (this.enabled_tex1D) { + return GL_TEXTURE_1D; + } + return 0; + } + + CTexEnv.prototype.genPassLines = function CTexEnv_genPassLines(passOutputVar, passInputVar, texUnitID) { + switch (this.mode) { + case GL_REPLACE: { + /* RGB: + * Cv = Cs + * Av = Ap // Note how this is different, and that we'll + * need to track the bound texture internalFormat + * to get this right. + * + * RGBA: + * Cv = Cs + * Av = As + */ + return [ + "vec4 " + passOutputVar + " = " + genTexUnitSampleExpr(texUnitID) + ";", + ]; + } + case GL_ADD: { + /* RGBA: + * Cv = Cp + Cs + * Av = ApAs + */ + var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_"; + var texVar = prefix + "tex"; + var colorVar = prefix + "color"; + var alphaVar = prefix + "alpha"; + + return [ + "vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";", + "vec3 " + colorVar + " = " + passInputVar + ".rgb + " + texVar + ".rgb;", + "float " + alphaVar + " = " + passInputVar + ".a * " + texVar + ".a;", + "vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");", + ]; + } + case GL_MODULATE: { + /* RGBA: + * Cv = CpCs + * Av = ApAs + */ + var line = [ + "vec4 " + passOutputVar, + " = ", + passInputVar, + " * ", + genTexUnitSampleExpr(texUnitID), + ";", + ]; + return [line.join("")]; + } + case GL_DECAL: { + /* RGBA: + * Cv = Cp(1 - As) + CsAs + * Av = Ap + */ + var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_"; + var texVar = prefix + "tex"; + var colorVar = prefix + "color"; + var alphaVar = prefix + "alpha"; + + return [ + "vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";", + [ + "vec3 " + colorVar + " = ", + passInputVar + ".rgb * (1.0 - " + texVar + ".a)", + " + ", + texVar + ".rgb * " + texVar + ".a", + ";" + ].join(""), + "float " + alphaVar + " = " + passInputVar + ".a;", + "vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");", + ]; + } + case GL_BLEND: { + /* RGBA: + * Cv = Cp(1 - Cs) + CcCs + * Av = As + */ + var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_"; + var texVar = prefix + "tex"; + var colorVar = prefix + "color"; + var alphaVar = prefix + "alpha"; + + return [ + "vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";", + [ + "vec3 " + colorVar + " = ", + passInputVar + ".rgb * (1.0 - " + texVar + ".rgb)", + " + ", + PRIM_COLOR_VARYING + ".rgb * " + texVar + ".rgb", + ";" + ].join(""), + "float " + alphaVar + " = " + texVar + ".a;", + "vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");", + ]; + } + case GL_COMBINE: { + var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_"; + var colorVar = prefix + "color"; + var alphaVar = prefix + "alpha"; + var colorLines = this.genCombinerLines(true, colorVar, + passInputVar, texUnitID, + this.colorCombiner, this.colorSrc, this.colorOp); + var alphaLines = this.genCombinerLines(false, alphaVar, + passInputVar, texUnitID, + this.alphaCombiner, this.alphaSrc, this.alphaOp); + + // Generate scale, but avoid generating an identity op that multiplies by one. + var scaledColor = (this.colorScale == 1) ? colorVar : (colorVar + " * " + valToFloatLiteral(this.colorScale)); + var scaledAlpha = (this.alphaScale == 1) ? alphaVar : (alphaVar + " * " + valToFloatLiteral(this.alphaScale)); + + var line = [ + "vec4 " + passOutputVar, + " = ", + "vec4(", + scaledColor, + ", ", + scaledAlpha, + ")", + ";", + ].join(""); + return [].concat(colorLines, alphaLines, [line]); + } + } + + return abort_noSupport("Unsupported TexEnv mode: " + ptrToString(this.mode)); + } + + CTexEnv.prototype.genCombinerLines = function CTexEnv_getCombinerLines(isColor, outputVar, + passInputVar, texUnitID, + combiner, srcArr, opArr) + { + var argsNeeded = null; + switch (combiner) { + case GL_REPLACE: + argsNeeded = 1; + break; + + case GL_MODULATE: + case GL_ADD: + case GL_SUBTRACT: + argsNeeded = 2; + break; + + case GL_INTERPOLATE: + argsNeeded = 3; + break; + + default: + return abort_noSupport("Unsupported combiner: " + ptrToString(combiner)); + } + + var constantExpr = [ + "vec4(", + valToFloatLiteral(this.envColor[0]), + ", ", + valToFloatLiteral(this.envColor[1]), + ", ", + valToFloatLiteral(this.envColor[2]), + ", ", + valToFloatLiteral(this.envColor[3]), + ")", + ].join(""); + var src0Expr = (argsNeeded >= 1) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[0], opArr[0]) + : null; + var src1Expr = (argsNeeded >= 2) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[1], opArr[1]) + : null; + var src2Expr = (argsNeeded >= 3) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[2], opArr[2]) + : null; + + var outputType = isColor ? "vec3" : "float"; + var lines = null; + switch (combiner) { + case GL_REPLACE: { + lines = [`${outputType} ${outputVar} = ${src0Expr};`] + break; + } + case GL_MODULATE: { + lines = [`${outputType} ${outputVar} = ${src0Expr} * ${src1Expr};`]; + break; + } + case GL_ADD: { + lines = [`${outputType} ${outputVar} = ${src0Expr} + ${src1Expr};`] + break; + } + case GL_SUBTRACT: { + lines = [`${outputType} ${outputVar} = ${src0Expr} - ${src1Expr};`] + break; + } + case GL_INTERPOLATE: { + var prefix = `${TEXENVJIT_NAMESPACE_PREFIX}env${texUnitID}_`; + var arg2Var = `${prefix}colorSrc2`; + var arg2Type = getTypeFromCombineOp(this.colorOp[2]); + + lines = [ + `${arg2Type} ${arg2Var} = ${src2Expr};`, + `${outputType} ${outputVar} = ${src0Expr} * ${arg2Var} + ${src1Expr} * (1.0 - ${arg2Var});`, + ]; + break; + } + + default: + return abort_sanity("Unmatched TexEnv.colorCombiner?"); + } + + return lines; + } + + return { + // Exports: + init: (gl, specifiedMaxTextureImageUnits) => { + var maxTexUnits = 0; + if (specifiedMaxTextureImageUnits) { + maxTexUnits = specifiedMaxTextureImageUnits; + } else if (gl) { + maxTexUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); + } +#if ASSERTIONS + assert(maxTexUnits > 0); +#endif + s_texUnits = []; + for (var i = 0; i < maxTexUnits; i++) { + s_texUnits.push(new CTexUnit()); + } + }, + + setGLSLVars: (uTexUnitPrefix, vTexCoordPrefix, vPrimColor, uTexMatrixPrefix) => { + TEX_UNIT_UNIFORM_PREFIX = uTexUnitPrefix; + TEX_COORD_VARYING_PREFIX = vTexCoordPrefix; + PRIM_COLOR_VARYING = vPrimColor; + TEX_MATRIX_UNIFORM_PREFIX = uTexMatrixPrefix; + }, + + genAllPassLines: (resultDest, indentSize = 0) => { + s_requiredTexUnitsForPass.length = 0; // Clear the list. + var lines = []; + var lastPassVar = PRIM_COLOR_VARYING; + for (var i = 0; i < s_texUnits.length; i++) { + if (!s_texUnits[i].enabled()) continue; + + s_requiredTexUnitsForPass.push(i); + + var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + i + "_"; + var passOutputVar = prefix + "result"; + + var newLines = s_texUnits[i].genPassLines(passOutputVar, lastPassVar, i); + lines = lines.concat(newLines, [""]); + + lastPassVar = passOutputVar; + } + lines.push(resultDest + " = " + lastPassVar + ";"); + + var indent = ""; + for (var i = 0; i < indentSize; i++) indent += " "; + + var output = indent + lines.join("\n" + indent); + + return output; + }, + + getUsedTexUnitList: () => s_requiredTexUnitsForPass, + + getActiveTexture: () => s_activeTexture, + + traverseState: (keyView) => { + for (var texUnit of s_texUnits) { + texUnit.traverseState(keyView); + } + }, + + getTexUnitType: (texUnitID) => { +#if ASSERTIONS + assert(texUnitID >= 0 && + texUnitID < s_texUnits.length); +#endif + return s_texUnits[texUnitID].getTexType(); + }, + + // Hooks: + hook_activeTexture: (texture) => { + s_activeTexture = texture - GL_TEXTURE0; + // Check if the current matrix mode is GL_TEXTURE. + if (GLImmediate.currentMatrix >= 2) { + // Switch to the corresponding texture matrix stack. + GLImmediate.currentMatrix = 2 + s_activeTexture; + } + }, + + hook_enable: (cap) => { + var cur = getCurTexUnit(); + switch (cap) { + case GL_TEXTURE_1D: + if (!cur.enabled_tex1D) { + GLImmediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again. + cur.enabled_tex1D = true; + cur.texTypesEnabled |= 1; + } + break; + case GL_TEXTURE_2D: + if (!cur.enabled_tex2D) { + GLImmediate.currentRenderer = null; + cur.enabled_tex2D = true; + cur.texTypesEnabled |= 2; + } + break; + case GL_TEXTURE_3D: + if (!cur.enabled_tex3D) { + GLImmediate.currentRenderer = null; + cur.enabled_tex3D = true; + cur.texTypesEnabled |= 4; + } + break; + case GL_TEXTURE_CUBE_MAP: + if (!cur.enabled_texCube) { + GLImmediate.currentRenderer = null; + cur.enabled_texCube = true; + cur.texTypesEnabled |= 8; + } + break; + } + }, + + hook_disable: (cap) => { + var cur = getCurTexUnit(); + switch (cap) { + case GL_TEXTURE_1D: + if (cur.enabled_tex1D) { + GLImmediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again. + cur.enabled_tex1D = false; + cur.texTypesEnabled &= ~1; + } + break; + case GL_TEXTURE_2D: + if (cur.enabled_tex2D) { + GLImmediate.currentRenderer = null; + cur.enabled_tex2D = false; + cur.texTypesEnabled &= ~2; + } + break; + case GL_TEXTURE_3D: + if (cur.enabled_tex3D) { + GLImmediate.currentRenderer = null; + cur.enabled_tex3D = false; + cur.texTypesEnabled &= ~4; + } + break; + case GL_TEXTURE_CUBE_MAP: + if (cur.enabled_texCube) { + GLImmediate.currentRenderer = null; + cur.enabled_texCube = false; + cur.texTypesEnabled &= ~8; + } + break; + } + }, + + hook_texEnvf(target, pname, param) { + if (target != GL_TEXTURE_ENV) + return; + + var env = getCurTexUnit().env; + switch (pname) { + case GL_RGB_SCALE: + if (env.colorScale != param) { + env.invalidateKey(); // We changed FFP emulation renderer state. + env.colorScale = param; + } + break; + case GL_ALPHA_SCALE: + if (env.alphaScale != param) { + env.invalidateKey(); + env.alphaScale = param; + } + break; + + default: + err('WARNING: Unhandled `pname` in call to `glTexEnvf`.'); + } + }, + + hook_texEnvi(target, pname, param) { + if (target != GL_TEXTURE_ENV) + return; + + var env = getCurTexUnit().env; + switch (pname) { + case GL_TEXTURE_ENV_MODE: + if (env.mode != param) { + env.invalidateKey(); // We changed FFP emulation renderer state. + env.mode = param; + } + break; + + case GL_COMBINE_RGB: + if (env.colorCombiner != param) { + env.invalidateKey(); + env.colorCombiner = param; + } + break; + case GL_COMBINE_ALPHA: + if (env.alphaCombiner != param) { + env.invalidateKey(); + env.alphaCombiner = param; + } + break; + + case GL_SRC0_RGB: + if (env.colorSrc[0] != param) { + env.invalidateKey(); + env.colorSrc[0] = param; + } + break; + case GL_SRC1_RGB: + if (env.colorSrc[1] != param) { + env.invalidateKey(); + env.colorSrc[1] = param; + } + break; + case GL_SRC2_RGB: + if (env.colorSrc[2] != param) { + env.invalidateKey(); + env.colorSrc[2] = param; + } + break; + + case GL_SRC0_ALPHA: + if (env.alphaSrc[0] != param) { + env.invalidateKey(); + env.alphaSrc[0] = param; + } + break; + case GL_SRC1_ALPHA: + if (env.alphaSrc[1] != param) { + env.invalidateKey(); + env.alphaSrc[1] = param; + } + break; + case GL_SRC2_ALPHA: + if (env.alphaSrc[2] != param) { + env.invalidateKey(); + env.alphaSrc[2] = param; + } + break; + + case GL_OPERAND0_RGB: + if (env.colorOp[0] != param) { + env.invalidateKey(); + env.colorOp[0] = param; + } + break; + case GL_OPERAND1_RGB: + if (env.colorOp[1] != param) { + env.invalidateKey(); + env.colorOp[1] = param; + } + break; + case GL_OPERAND2_RGB: + if (env.colorOp[2] != param) { + env.invalidateKey(); + env.colorOp[2] = param; + } + break; + + case GL_OPERAND0_ALPHA: + if (env.alphaOp[0] != param) { + env.invalidateKey(); + env.alphaOp[0] = param; + } + break; + case GL_OPERAND1_ALPHA: + if (env.alphaOp[1] != param) { + env.invalidateKey(); + env.alphaOp[1] = param; + } + break; + case GL_OPERAND2_ALPHA: + if (env.alphaOp[2] != param) { + env.invalidateKey(); + env.alphaOp[2] = param; + } + break; + + case GL_RGB_SCALE: + if (env.colorScale != param) { + env.invalidateKey(); + env.colorScale = param; + } + break; + case GL_ALPHA_SCALE: + if (env.alphaScale != param) { + env.invalidateKey(); + env.alphaScale = param; + } + break; + + default: + err('WARNING: Unhandled `pname` in call to `glTexEnvi`.'); + } + }, + + hook_texEnvfv(target, pname, params) { + if (target != GL_TEXTURE_ENV) return; + + var env = getCurTexUnit().env; + switch (pname) { + case GL_TEXTURE_ENV_COLOR: { + for (var i = 0; i < 4; i++) { + var param = {{{ makeGetValue('params', 'i*4', 'float') }}}; + if (env.envColor[i] != param) { + env.invalidateKey(); // We changed FFP emulation renderer state. + env.envColor[i] = param; + } + } + break + } + default: + err('WARNING: Unhandled `pname` in call to `glTexEnvfv`.'); + } + }, + + hook_getTexEnviv(target, pname, param) { + if (target != GL_TEXTURE_ENV) + return; + + var env = getCurTexUnit().env; + switch (pname) { + case GL_TEXTURE_ENV_MODE: + {{{ makeSetValue('param', '0', 'env.mode', 'i32') }}}; + return; + + case GL_TEXTURE_ENV_COLOR: + {{{ makeSetValue('param', '0', 'Math.max(Math.min(env.envColor[0]*255, 255, -255))', 'i32') }}}; + {{{ makeSetValue('param', '1', 'Math.max(Math.min(env.envColor[1]*255, 255, -255))', 'i32') }}}; + {{{ makeSetValue('param', '2', 'Math.max(Math.min(env.envColor[2]*255, 255, -255))', 'i32') }}}; + {{{ makeSetValue('param', '3', 'Math.max(Math.min(env.envColor[3]*255, 255, -255))', 'i32') }}}; + return; + + case GL_COMBINE_RGB: + {{{ makeSetValue('param', '0', 'env.colorCombiner', 'i32') }}}; + return; + + case GL_COMBINE_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaCombiner', 'i32') }}}; + return; + + case GL_SRC0_RGB: + {{{ makeSetValue('param', '0', 'env.colorSrc[0]', 'i32') }}}; + return; + + case GL_SRC1_RGB: + {{{ makeSetValue('param', '0', 'env.colorSrc[1]', 'i32') }}}; + return; + + case GL_SRC2_RGB: + {{{ makeSetValue('param', '0', 'env.colorSrc[2]', 'i32') }}}; + return; + + case GL_SRC0_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaSrc[0]', 'i32') }}}; + return; + + case GL_SRC1_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaSrc[1]', 'i32') }}}; + return; + + case GL_SRC2_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaSrc[2]', 'i32') }}}; + return; + + case GL_OPERAND0_RGB: + {{{ makeSetValue('param', '0', 'env.colorOp[0]', 'i32') }}}; + return; + + case GL_OPERAND1_RGB: + {{{ makeSetValue('param', '0', 'env.colorOp[1]', 'i32') }}}; + return; + + case GL_OPERAND2_RGB: + {{{ makeSetValue('param', '0', 'env.colorOp[2]', 'i32') }}}; + return; + + case GL_OPERAND0_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaOp[0]', 'i32') }}}; + return; + + case GL_OPERAND1_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaOp[1]', 'i32') }}}; + return; + + case GL_OPERAND2_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaOp[2]', 'i32') }}}; + return; + + case GL_RGB_SCALE: + {{{ makeSetValue('param', '0', 'env.colorScale', 'i32') }}}; + return; + + case GL_ALPHA_SCALE: + {{{ makeSetValue('param', '0', 'env.alphaScale', 'i32') }}}; + return; + + default: + err('WARNING: Unhandled `pname` in call to `glGetTexEnvi`.'); + } + }, + + hook_getTexEnvfv: (target, pname, param) => { + if (target != GL_TEXTURE_ENV) + return; + + var env = getCurTexUnit().env; + switch (pname) { + case GL_TEXTURE_ENV_COLOR: + {{{ makeSetValue('param', '0', 'env.envColor[0]', 'float') }}}; + {{{ makeSetValue('param', '4', 'env.envColor[1]', 'float') }}}; + {{{ makeSetValue('param', '8', 'env.envColor[2]', 'float') }}}; + {{{ makeSetValue('param', '12', 'env.envColor[3]', 'float') }}}; + return; + } + } + }; + }, + + // Vertex and index data + vertexData: null, // current vertex data. either tempData (glBegin etc.) or a view into the heap (gl*Pointer). Default view is F32 + vertexDataU8: null, // U8 view + tempData: null, + indexData: null, + vertexCounter: 0, + mode: -1, + + rendererCache: null, + rendererComponents: [], // small cache for calls inside glBegin/end. counts how many times the element was seen + rendererComponentPointer: 0, // next place to start a glBegin/end component + lastRenderer: null, // used to avoid cleaning up and re-preparing the same renderer + lastArrayBuffer: null, // used in conjunction with lastRenderer + lastProgram: null, // "" + lastStride: -1, // "" + + // The following data structures are used for OpenGL Immediate Mode matrix routines. + matrix: [], + matrixStack: [], + currentMatrix: 0, // 0: modelview, 1: projection, 2+i, texture matrix i. + tempMatrix: null, + matricesModified: false, + useTextureMatrix: false, + + // Clientside attributes + VERTEX: 0, + NORMAL: 1, + COLOR: 2, + TEXTURE0: 3, + NUM_ATTRIBUTES: -1, // Initialized in GL emulation init(). + MAX_TEXTURES: -1, // Initialized in GL emulation init(). + + totalEnabledClientAttributes: 0, + enabledClientAttributes: [0, 0], + clientAttributes: [], // raw data, including possible unneeded ones + liveClientAttributes: [], // the ones actually alive in the current computation, sorted + currentRenderer: null, // Caches the currently active FFP emulation renderer, so that it does not have to be re-looked up unless relevant state changes. + modifiedClientAttributes: false, + clientActiveTexture: 0, + clientColor: null, + usedTexUnitList: [], + fixedFunctionProgram: null, + + setClientAttribute(name, size, type, stride, pointer) { + var attrib = GLImmediate.clientAttributes[name]; + if (!attrib) { + for (var i = 0; i <= name; i++) { // keep flat + GLImmediate.clientAttributes[i] ||= { + name, + size, + type, + stride, + pointer, + offset: 0 + }; + } + } else { + attrib.name = name; + attrib.size = size; + attrib.type = type; + attrib.stride = stride; + attrib.pointer = pointer; + attrib.offset = 0; + } + GLImmediate.modifiedClientAttributes = true; + }, + + // Renderers + addRendererComponent(name, size, type) { + if (!GLImmediate.rendererComponents[name]) { + GLImmediate.rendererComponents[name] = 1; +#if ASSERTIONS + if (GLImmediate.enabledClientAttributes[name]) { + warnOnce("Warning: glTexCoord used after EnableClientState for TEXTURE_COORD_ARRAY for TEXTURE0. Disabling TEXTURE_COORD_ARRAY..."); + } +#endif + GLImmediate.enabledClientAttributes[name] = true; + GLImmediate.setClientAttribute(name, size, type, 0, GLImmediate.rendererComponentPointer); + GLImmediate.rendererComponentPointer += size * GL.byteSizeByType[type - GL.byteSizeByTypeRoot]; +#if GL_FFP_ONLY + // We can enable the correct attribute stream index immediately here, since the same attribute in each shader + // will be bound to this same index. + GL.enableVertexAttribArray(name); +#endif + } else { + GLImmediate.rendererComponents[name]++; + } + }, + + disableBeginEndClientAttributes() { + for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) { + if (GLImmediate.rendererComponents[i]) GLImmediate.enabledClientAttributes[i] = false; + } + }, + + getRenderer() { + // If no FFP state has changed that would have forced to re-evaluate which FFP emulation shader to use, + // we have the currently used renderer in cache, and can immediately return that. + if (GLImmediate.currentRenderer) { + return GLImmediate.currentRenderer; + } + // return a renderer object given the liveClientAttributes + // we maintain a cache of renderers, optimized to not generate garbage + var attributes = GLImmediate.liveClientAttributes; + var cacheMap = GLImmediate.rendererCache; + var keyView = cacheMap.getStaticKeyView().reset(); + + // By attrib state: + var enabledAttributesKey = 0; + for (var attr of attributes) { + enabledAttributesKey |= 1 << attr.name; + } + + // To prevent using more than 31 bits add another level to the maptree + // and reset the enabledAttributesKey for the next glemulation state bits + keyView.next(enabledAttributesKey); + enabledAttributesKey = 0; + + // By fog state: + var fogParam = 0; + if (GLEmulation.fogEnabled) { + switch (GLEmulation.fogMode) { + case 0x801: // GL_EXP2 + fogParam = 1; + break; + case 0x2601: // GL_LINEAR + fogParam = 2; + break; + default: // default to GL_EXP + fogParam = 3; + break; + } + } + enabledAttributesKey = (enabledAttributesKey << 2) | fogParam; + + // By clip plane mode + for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) { + enabledAttributesKey = (enabledAttributesKey << 1) | GLEmulation.clipPlaneEnabled[clipPlaneId]; + } + + // By lighting mode and enabled lights + enabledAttributesKey = (enabledAttributesKey << 1) | GLEmulation.lightingEnabled; + for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) { + enabledAttributesKey = (enabledAttributesKey << 1) | (GLEmulation.lightingEnabled ? GLEmulation.lightEnabled[lightId] : 0); + } + + // By alpha testing mode + enabledAttributesKey = (enabledAttributesKey << 3) | (GLEmulation.alphaTestEnabled ? (GLEmulation.alphaTestFunc - 0x200) : 0x7); + + // By drawing mode: + enabledAttributesKey = (enabledAttributesKey << 1) | (GLImmediate.mode == GLctx.POINTS ? 1 : 0); + + keyView.next(enabledAttributesKey); + +#if !GL_FFP_ONLY + // By cur program: + keyView.next(GL.currProgram); + if (!GL.currProgram) { +#endif + GLImmediate.TexEnvJIT.traverseState(keyView); +#if !GL_FFP_ONLY + } +#endif + + // If we don't already have it, create it. + var renderer = keyView.get(); + if (!renderer) { +#if GL_DEBUG + dbg(`generating renderer for ${JSON.stringify(attributes)}`); +#endif + renderer = GLImmediate.createRenderer(); + GLImmediate.currentRenderer = renderer; + keyView.set(renderer); + return renderer; + } + GLImmediate.currentRenderer = renderer; // Cache the currently used renderer, so later lookups without state changes can get this fast. + return renderer; + }, + + createRenderer(renderer) { + var useCurrProgram = !!GL.currProgram; + var hasTextures = false; + for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { + var texAttribName = GLImmediate.TEXTURE0 + i; + if (!GLImmediate.enabledClientAttributes[texAttribName]) + continue; + +#if ASSERTIONS + if (!useCurrProgram) { + if (GLImmediate.TexEnvJIT.getTexUnitType(i) == 0) { + warnOnce("GL_TEXTURE" + i + " coords are supplied, but that texture unit is disabled in the fixed-function pipeline."); + } + } +#endif + + hasTextures = true; + } + + /** @constructor */ + function Renderer() { + this.init = function() { + // For fixed-function shader generation. + var uTexUnitPrefix = 'u_texUnit'; + var aTexCoordPrefix = 'a_texCoord'; + var vTexCoordPrefix = 'v_texCoord'; + var vPrimColor = 'v_color'; + var uTexMatrixPrefix = GLImmediate.useTextureMatrix ? 'u_textureMatrix' : null; + + if (useCurrProgram) { + if (GL.shaderInfos[GL.programShaders[GL.currProgram][0]].type == GLctx.VERTEX_SHADER) { + this.vertexShader = GL.shaders[GL.programShaders[GL.currProgram][0]]; + this.fragmentShader = GL.shaders[GL.programShaders[GL.currProgram][1]]; + } else { + this.vertexShader = GL.shaders[GL.programShaders[GL.currProgram][1]]; + this.fragmentShader = GL.shaders[GL.programShaders[GL.currProgram][0]]; + } + this.program = GL.programs[GL.currProgram]; + this.usedTexUnitList = []; + } else { + // IMPORTANT NOTE: If you parameterize the shader source based on any runtime values + // in order to create the least expensive shader possible based on the features being + // used, you should also update the code in the beginning of getRenderer to make sure + // that you cache the renderer based on the said parameters. + if (GLEmulation.fogEnabled) { + switch (GLEmulation.fogMode) { + case 0x801: // GL_EXP2 + // fog = exp(-(gl_Fog.density * gl_FogFragCoord)^2) + var fogFormula = ' float fog = exp(-u_fogDensity * u_fogDensity * ecDistance * ecDistance); \n'; + break; + case 0x2601: // GL_LINEAR + // fog = (gl_Fog.end - gl_FogFragCoord) * gl_fog.scale + var fogFormula = ' float fog = (u_fogEnd - ecDistance) * u_fogScale; \n'; + break; + default: // default to GL_EXP + // fog = exp(-gl_Fog.density * gl_FogFragCoord) + var fogFormula = ' float fog = exp(-u_fogDensity * ecDistance); \n'; + break; + } + } + + GLImmediate.TexEnvJIT.setGLSLVars(uTexUnitPrefix, vTexCoordPrefix, vPrimColor, uTexMatrixPrefix); + var fsTexEnvPass = GLImmediate.TexEnvJIT.genAllPassLines('gl_FragColor', 2); + + var texUnitAttribList = ''; + var texUnitVaryingList = ''; + var texUnitUniformList = ''; + var vsTexCoordInits = ''; + this.usedTexUnitList = GLImmediate.TexEnvJIT.getUsedTexUnitList(); + for (var texUnit of this.usedTexUnitList) { + texUnitAttribList += 'attribute vec4 ' + aTexCoordPrefix + texUnit + ';\n'; + texUnitVaryingList += 'varying vec4 ' + vTexCoordPrefix + texUnit + ';\n'; + texUnitUniformList += 'uniform sampler2D ' + uTexUnitPrefix + texUnit + ';\n'; + vsTexCoordInits += ' ' + vTexCoordPrefix + texUnit + ' = ' + aTexCoordPrefix + texUnit + ';\n'; + + if (GLImmediate.useTextureMatrix) { + texUnitUniformList += 'uniform mat4 ' + uTexMatrixPrefix + texUnit + ';\n'; + } + } + + var vsFogVaryingInit = null; + if (GLEmulation.fogEnabled) { + vsFogVaryingInit = ' v_fogFragCoord = abs(ecPosition.z);\n'; + } + + var vsPointSizeDefs = null; + var vsPointSizeInit = null; + if (GLImmediate.mode == GLctx.POINTS) { + vsPointSizeDefs = 'uniform float u_pointSize;\n'; + vsPointSizeInit = ' gl_PointSize = u_pointSize;\n'; + } + + var vsClipPlaneDefs = ''; + var vsClipPlaneInit = ''; + var fsClipPlaneDefs = ''; + var fsClipPlanePass = ''; + for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) { + if (GLEmulation.clipPlaneEnabled[clipPlaneId]) { + vsClipPlaneDefs += 'uniform vec4 u_clipPlaneEquation' + clipPlaneId + ';'; + vsClipPlaneDefs += 'varying float v_clipDistance' + clipPlaneId + ';'; + vsClipPlaneInit += ' v_clipDistance' + clipPlaneId + ' = dot(ecPosition, u_clipPlaneEquation' + clipPlaneId + ');'; + fsClipPlaneDefs += 'varying float v_clipDistance' + clipPlaneId + ';'; + fsClipPlanePass += ' if (v_clipDistance' + clipPlaneId + ' < 0.0) discard;'; + } + } + + var vsLightingDefs = ''; + var vsLightingPass = ''; + if (GLEmulation.lightingEnabled) { + vsLightingDefs += 'attribute vec3 a_normal;'; + vsLightingDefs += 'uniform mat3 u_normalMatrix;'; + vsLightingDefs += 'uniform vec4 u_lightModelAmbient;'; + vsLightingDefs += 'uniform vec4 u_materialAmbient;'; + vsLightingDefs += 'uniform vec4 u_materialDiffuse;'; + vsLightingDefs += 'uniform vec4 u_materialSpecular;'; + vsLightingDefs += 'uniform float u_materialShininess;'; + vsLightingDefs += 'uniform vec4 u_materialEmission;'; + + vsLightingPass += ' vec3 ecNormal = normalize(u_normalMatrix * a_normal);'; + vsLightingPass += ' v_color.w = u_materialDiffuse.w;'; + vsLightingPass += ' v_color.xyz = u_materialEmission.xyz;'; + vsLightingPass += ' v_color.xyz += u_lightModelAmbient.xyz * u_materialAmbient.xyz;'; + + for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) { + if (GLEmulation.lightEnabled[lightId]) { + vsLightingDefs += 'uniform vec4 u_lightAmbient' + lightId + ';'; + vsLightingDefs += 'uniform vec4 u_lightDiffuse' + lightId + ';'; + vsLightingDefs += 'uniform vec4 u_lightSpecular' + lightId + ';'; + vsLightingDefs += 'uniform vec4 u_lightPosition' + lightId + ';'; + + vsLightingPass += ' {'; + vsLightingPass += ' vec3 lightDirection = normalize(u_lightPosition' + lightId + ').xyz;'; + vsLightingPass += ' vec3 halfVector = normalize(lightDirection + vec3(0,0,1));'; + vsLightingPass += ' vec3 ambient = u_lightAmbient' + lightId + '.xyz * u_materialAmbient.xyz;'; + vsLightingPass += ' float diffuseI = max(dot(ecNormal, lightDirection), 0.0);'; + vsLightingPass += ' float specularI = max(dot(ecNormal, halfVector), 0.0);'; + vsLightingPass += ' vec3 diffuse = diffuseI * u_lightDiffuse' + lightId + '.xyz * u_materialDiffuse.xyz;'; + vsLightingPass += ' specularI = (diffuseI > 0.0 && specularI > 0.0) ? exp(u_materialShininess * log(specularI)) : 0.0;'; + vsLightingPass += ' vec3 specular = specularI * u_lightSpecular' + lightId + '.xyz * u_materialSpecular.xyz;'; + vsLightingPass += ' v_color.xyz += ambient + diffuse + specular;'; + vsLightingPass += ' }'; + } + } + vsLightingPass += ' v_color = clamp(v_color, 0.0, 1.0);'; + } + + var vsSource = [ + 'attribute vec4 a_position;', + 'attribute vec4 a_color;', + 'varying vec4 v_color;', + texUnitAttribList, + texUnitVaryingList, + (GLEmulation.fogEnabled ? 'varying float v_fogFragCoord;' : null), + 'uniform mat4 u_modelView;', + 'uniform mat4 u_projection;', + vsPointSizeDefs, + vsClipPlaneDefs, + vsLightingDefs, + 'void main()', + '{', + ' vec4 ecPosition = u_modelView * a_position;', // eye-coordinate position + ' gl_Position = u_projection * ecPosition;', + ' v_color = a_color;', + vsTexCoordInits, + vsFogVaryingInit, + vsPointSizeInit, + vsClipPlaneInit, + vsLightingPass, + '}', + '' + ].join('\n').replace(/\n\n+/g, '\n'); + + this.vertexShader = GLctx.createShader(GLctx.VERTEX_SHADER); + GLctx.shaderSource(this.vertexShader, vsSource); + GLctx.compileShader(this.vertexShader); + + var fogHeaderIfNeeded = null; + if (GLEmulation.fogEnabled) { + fogHeaderIfNeeded = [ + '', + 'varying float v_fogFragCoord; ', + 'uniform vec4 u_fogColor; ', + 'uniform float u_fogEnd; ', + 'uniform float u_fogScale; ', + 'uniform float u_fogDensity; ', + 'float ffog(in float ecDistance) { ', + fogFormula, + ' fog = clamp(fog, 0.0, 1.0); ', + ' return fog; ', + '}', + '', + ].join("\n"); + } + + var fogPass = null; + if (GLEmulation.fogEnabled) { + fogPass = 'gl_FragColor = vec4(mix(u_fogColor.rgb, gl_FragColor.rgb, ffog(v_fogFragCoord)), gl_FragColor.a);\n'; + } + + var fsAlphaTestDefs = ''; + var fsAlphaTestPass = ''; + if (GLEmulation.alphaTestEnabled) { + fsAlphaTestDefs = 'uniform float u_alphaTestRef;'; + switch (GLEmulation.alphaTestFunc) { + case 0x200: // GL_NEVER + fsAlphaTestPass = 'discard;'; + break; + case 0x201: // GL_LESS + fsAlphaTestPass = 'if (!(gl_FragColor.a < u_alphaTestRef)) { discard; }'; + break; + case 0x202: // GL_EQUAL + fsAlphaTestPass = 'if (!(gl_FragColor.a == u_alphaTestRef)) { discard; }'; + break; + case 0x203: // GL_LEQUAL + fsAlphaTestPass = 'if (!(gl_FragColor.a <= u_alphaTestRef)) { discard; }'; + break; + case 0x204: // GL_GREATER + fsAlphaTestPass = 'if (!(gl_FragColor.a > u_alphaTestRef)) { discard; }'; + break; + case 0x205: // GL_NOTEQUAL + fsAlphaTestPass = 'if (!(gl_FragColor.a != u_alphaTestRef)) { discard; }'; + break; + case 0x206: // GL_GEQUAL + fsAlphaTestPass = 'if (!(gl_FragColor.a >= u_alphaTestRef)) { discard; }'; + break; + case 0x207: // GL_ALWAYS + fsAlphaTestPass = ''; + break; + } + } + + var fsSource = [ + 'precision mediump float;', + texUnitVaryingList, + texUnitUniformList, + 'varying vec4 v_color;', + fogHeaderIfNeeded, + fsClipPlaneDefs, + fsAlphaTestDefs, + 'void main()', + '{', + fsClipPlanePass, + fsTexEnvPass, + fogPass, + fsAlphaTestPass, + '}', + '' + ].join("\n").replace(/\n\n+/g, '\n'); + + this.fragmentShader = GLctx.createShader(GLctx.FRAGMENT_SHADER); + GLctx.shaderSource(this.fragmentShader, fsSource); + GLctx.compileShader(this.fragmentShader); + + this.program = GLctx.createProgram(); + GLctx.attachShader(this.program, this.vertexShader); + GLctx.attachShader(this.program, this.fragmentShader); + + // As optimization, bind all attributes to prespecified locations, so that the FFP emulation + // code can submit attributes to any generated FFP shader without having to examine each shader in turn. + // These prespecified locations are only assumed if GL_FFP_ONLY is specified, since user could also create their + // own shaders that didn't have attributes in the same locations. + GLctx.bindAttribLocation(this.program, GLImmediate.VERTEX, 'a_position'); + GLctx.bindAttribLocation(this.program, GLImmediate.COLOR, 'a_color'); + GLctx.bindAttribLocation(this.program, GLImmediate.NORMAL, 'a_normal'); + var maxVertexAttribs = GLctx.getParameter(GLctx.MAX_VERTEX_ATTRIBS); + for (var i = 0; i < GLImmediate.MAX_TEXTURES && GLImmediate.TEXTURE0 + i < maxVertexAttribs; i++) { + GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, 'a_texCoord'+i); + GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, aTexCoordPrefix+i); + } + GLctx.linkProgram(this.program); + } + + // Stores an array that remembers which matrix uniforms are up-to-date in this FFP renderer, so they don't need to be resubmitted + // each time we render with this program. + this.textureMatrixVersion = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; + + this.positionLocation = GLctx.getAttribLocation(this.program, 'a_position'); + + this.texCoordLocations = []; + + for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { + if (!GLImmediate.enabledClientAttributes[GLImmediate.TEXTURE0 + i]) { + this.texCoordLocations[i] = -1; + continue; + } + + if (useCurrProgram) { + this.texCoordLocations[i] = GLctx.getAttribLocation(this.program, `a_texCoord${i}`); + } else { + this.texCoordLocations[i] = GLctx.getAttribLocation(this.program, aTexCoordPrefix + i); + } + } + this.colorLocation = GLctx.getAttribLocation(this.program, 'a_color'); + if (!useCurrProgram) { + // Temporarily switch to the program so we can set our sampler uniforms early. + var prevBoundProg = GLctx.getParameter(GLctx.CURRENT_PROGRAM); + GLctx.useProgram(this.program); + { + for (var i = 0; i < this.usedTexUnitList.length; i++) { + var texUnitID = this.usedTexUnitList[i]; + var texSamplerLoc = GLctx.getUniformLocation(this.program, uTexUnitPrefix + texUnitID); + GLctx.uniform1i(texSamplerLoc, texUnitID); + } + } + // The default color attribute value is not the same as the default for all other attribute streams (0,0,0,1) but (1,1,1,1), + // so explicitly set it right at start. + GLctx.vertexAttrib4fv(this.colorLocation, [1,1,1,1]); + GLctx.useProgram(prevBoundProg); + } + + this.textureMatrixLocations = []; + for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { + this.textureMatrixLocations[i] = GLctx.getUniformLocation(this.program, `u_textureMatrix${i}`); + } + this.normalLocation = GLctx.getAttribLocation(this.program, 'a_normal'); + + this.modelViewLocation = GLctx.getUniformLocation(this.program, 'u_modelView'); + this.projectionLocation = GLctx.getUniformLocation(this.program, 'u_projection'); + this.normalMatrixLocation = GLctx.getUniformLocation(this.program, 'u_normalMatrix'); + + this.hasTextures = hasTextures; + this.hasNormal = GLImmediate.enabledClientAttributes[GLImmediate.NORMAL] && + GLImmediate.clientAttributes[GLImmediate.NORMAL].size > 0 && + this.normalLocation >= 0; + this.hasColor = (this.colorLocation === 0) || this.colorLocation > 0; + + this.floatType = GLctx.FLOAT; // minor optimization + + this.fogColorLocation = GLctx.getUniformLocation(this.program, 'u_fogColor'); + this.fogEndLocation = GLctx.getUniformLocation(this.program, 'u_fogEnd'); + this.fogScaleLocation = GLctx.getUniformLocation(this.program, 'u_fogScale'); + this.fogDensityLocation = GLctx.getUniformLocation(this.program, 'u_fogDensity'); + this.hasFog = !!(this.fogColorLocation || this.fogEndLocation || + this.fogScaleLocation || this.fogDensityLocation); + + this.pointSizeLocation = GLctx.getUniformLocation(this.program, 'u_pointSize'); + + this.hasClipPlane = false; + this.clipPlaneEquationLocation = []; + for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) { + this.clipPlaneEquationLocation[clipPlaneId] = GLctx.getUniformLocation(this.program, `u_clipPlaneEquation${clipPlaneId}`); + this.hasClipPlane = (this.hasClipPlane || this.clipPlaneEquationLocation[clipPlaneId]); + } + + this.hasLighting = GLEmulation.lightingEnabled; + this.lightModelAmbientLocation = GLctx.getUniformLocation(this.program, 'u_lightModelAmbient'); + this.materialAmbientLocation = GLctx.getUniformLocation(this.program, 'u_materialAmbient'); + this.materialDiffuseLocation = GLctx.getUniformLocation(this.program, 'u_materialDiffuse'); + this.materialSpecularLocation = GLctx.getUniformLocation(this.program, 'u_materialSpecular'); + this.materialShininessLocation = GLctx.getUniformLocation(this.program, 'u_materialShininess'); + this.materialEmissionLocation = GLctx.getUniformLocation(this.program, 'u_materialEmission'); + this.lightAmbientLocation = [] + this.lightDiffuseLocation = [] + this.lightSpecularLocation = [] + this.lightPositionLocation = [] + for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) { + this.lightAmbientLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightAmbient${lightId}`); + this.lightDiffuseLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightDiffuse${lightId}`); + this.lightSpecularLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightSpecular${lightId}`); + this.lightPositionLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightPosition${lightId}`); + } + + this.hasAlphaTest = GLEmulation.alphaTestEnabled; + this.alphaTestRefLocation = GLctx.getUniformLocation(this.program, 'u_alphaTestRef'); + + }; + + this.prepare = function() { + // Calculate the array buffer + var arrayBuffer; + if (!GLctx.currentArrayBufferBinding) { + var start = GLImmediate.firstVertex*GLImmediate.stride; + var end = GLImmediate.lastVertex*GLImmediate.stride; +#if ASSERTIONS + assert(end <= GL.MAX_TEMP_BUFFER_SIZE, 'too much vertex data'); +#endif + arrayBuffer = GL.getTempVertexBuffer(end); + // TODO: consider using the last buffer we bound, if it was larger. downside is larger buffer, but we might avoid rebinding and preparing + } else { + arrayBuffer = GLctx.currentArrayBufferBinding; + } + +#if GL_UNSAFE_OPTS + // If the array buffer is unchanged and the renderer as well, then we can avoid all the work here + // XXX We use some heuristics here, and this may not work in all cases. Try disabling GL_UNSAFE_OPTS if you + // have odd glitches + var lastRenderer = GLImmediate.lastRenderer; + var canSkip = this == lastRenderer && + arrayBuffer == GLImmediate.lastArrayBuffer && + (GL.currProgram || this.program) == GLImmediate.lastProgram && + GLImmediate.stride == GLImmediate.lastStride && + !GLImmediate.matricesModified; + if (!canSkip && lastRenderer) lastRenderer.cleanup(); +#endif + if (!GLctx.currentArrayBufferBinding) { + // Bind the array buffer and upload data after cleaning up the previous renderer + + if (arrayBuffer != GLImmediate.lastArrayBuffer) { + GLctx.bindBuffer(GLctx.ARRAY_BUFFER, arrayBuffer); + GLImmediate.lastArrayBuffer = arrayBuffer; + } + + GLctx.bufferSubData(GLctx.ARRAY_BUFFER, start, GLImmediate.vertexData.subarray(start >> 2, end >> 2)); + } +#if GL_UNSAFE_OPTS + if (canSkip) return; + GLImmediate.lastRenderer = this; + GLImmediate.lastProgram = GL.currProgram || this.program; + GLImmediate.lastStride = GLImmediate.stride; + GLImmediate.matricesModified = false; +#endif + + if (!GL.currProgram) { + if (GLImmediate.fixedFunctionProgram != this.program) { + GLctx.useProgram(this.program); + GLImmediate.fixedFunctionProgram = this.program; + } + } + + if (this.modelViewLocation && this.modelViewMatrixVersion != GLImmediate.matrixVersion[0/*m*/]) { + this.modelViewMatrixVersion = GLImmediate.matrixVersion[0/*m*/]; + GLctx.uniformMatrix4fv(this.modelViewLocation, false, GLImmediate.matrix[0/*m*/]); + + // set normal matrix to the upper 3x3 of the inverse transposed current modelview matrix + if (GLEmulation.lightEnabled) { + var tmpMVinv = GLImmediate.matrixLib.mat4.create(GLImmediate.matrix[0]); + GLImmediate.matrixLib.mat4.inverse(tmpMVinv); + GLImmediate.matrixLib.mat4.transpose(tmpMVinv); + GLctx.uniformMatrix3fv(this.normalMatrixLocation, false, GLImmediate.matrixLib.mat4.toMat3(tmpMVinv)); + } + } + if (this.projectionLocation && this.projectionMatrixVersion != GLImmediate.matrixVersion[1/*p*/]) { + this.projectionMatrixVersion = GLImmediate.matrixVersion[1/*p*/]; + GLctx.uniformMatrix4fv(this.projectionLocation, false, GLImmediate.matrix[1/*p*/]); + } + + var clientAttributes = GLImmediate.clientAttributes; + var posAttr = clientAttributes[GLImmediate.VERTEX]; + +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(posAttr.size, posAttr.type, GLImmediate.stride, clientAttributes[GLImmediate.VERTEX].offset); +#endif + +#if GL_FFP_ONLY + if (!GLctx.currentArrayBufferBinding) { + GLctx.vertexAttribPointer(GLImmediate.VERTEX, posAttr.size, posAttr.type, false, GLImmediate.stride, posAttr.offset); + if (this.hasNormal) { + var normalAttr = clientAttributes[GLImmediate.NORMAL]; + GLctx.vertexAttribPointer(GLImmediate.NORMAL, normalAttr.size, normalAttr.type, true, GLImmediate.stride, normalAttr.offset); + } + } +#else + GLctx.vertexAttribPointer(this.positionLocation, posAttr.size, posAttr.type, false, GLImmediate.stride, posAttr.offset); + GLctx.enableVertexAttribArray(this.positionLocation); + if (this.hasNormal) { + var normalAttr = clientAttributes[GLImmediate.NORMAL]; +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(normalAttr.size, normalAttr.type, GLImmediate.stride, normalAttr.offset); +#endif + GLctx.vertexAttribPointer(this.normalLocation, normalAttr.size, normalAttr.type, true, GLImmediate.stride, normalAttr.offset); + GLctx.enableVertexAttribArray(this.normalLocation); + } +#endif + if (this.hasTextures) { + for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { +#if GL_FFP_ONLY + if (!GLctx.currentArrayBufferBinding) { + var attribLoc = GLImmediate.TEXTURE0+i; + var texAttr = clientAttributes[attribLoc]; + if (texAttr.size) { + GLctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GLImmediate.stride, texAttr.offset); + } else { + // These two might be dangerous, but let's try them. + GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1); + } + } +#else + var attribLoc = this.texCoordLocations[i]; + if (attribLoc === undefined || attribLoc < 0) continue; + var texAttr = clientAttributes[GLImmediate.TEXTURE0+i]; + + if (texAttr.size) { +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(texAttr.size, texAttr.type, GLImmediate.stride, texAttr.offset); +#endif + GLctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GLImmediate.stride, texAttr.offset); + GLctx.enableVertexAttribArray(attribLoc); + } else { + // These two might be dangerous, but let's try them. + GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1); + GLctx.disableVertexAttribArray(attribLoc); + } +#endif + var t = 2/*t*/+i; + if (this.textureMatrixLocations[i] && this.textureMatrixVersion[t] != GLImmediate.matrixVersion[t]) { // XXX might we need this even without the condition we are currently in? + this.textureMatrixVersion[t] = GLImmediate.matrixVersion[t]; + GLctx.uniformMatrix4fv(this.textureMatrixLocations[i], false, GLImmediate.matrix[t]); + } + } + } + if (GLImmediate.enabledClientAttributes[GLImmediate.COLOR]) { + var colorAttr = clientAttributes[GLImmediate.COLOR]; +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(colorAttr.size, colorAttr.type, GLImmediate.stride, colorAttr.offset); +#endif +#if GL_FFP_ONLY + if (!GLctx.currentArrayBufferBinding) { + GLctx.vertexAttribPointer(GLImmediate.COLOR, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset); + } +#else + GLctx.vertexAttribPointer(this.colorLocation, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset); + GLctx.enableVertexAttribArray(this.colorLocation); +#endif + } +#if !GL_FFP_ONLY + else if (this.hasColor) { + GLctx.disableVertexAttribArray(this.colorLocation); + GLctx.vertexAttrib4fv(this.colorLocation, GLImmediate.clientColor); + } +#endif + if (this.hasFog) { + if (this.fogColorLocation) GLctx.uniform4fv(this.fogColorLocation, GLEmulation.fogColor); + if (this.fogEndLocation) GLctx.uniform1f(this.fogEndLocation, GLEmulation.fogEnd); + if (this.fogScaleLocation) GLctx.uniform1f(this.fogScaleLocation, 1/(GLEmulation.fogEnd - GLEmulation.fogStart)); + if (this.fogDensityLocation) GLctx.uniform1f(this.fogDensityLocation, GLEmulation.fogDensity); + } + + if (this.hasClipPlane) { + for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) { + if (this.clipPlaneEquationLocation[clipPlaneId]) GLctx.uniform4fv(this.clipPlaneEquationLocation[clipPlaneId], GLEmulation.clipPlaneEquation[clipPlaneId]); + } + } + + if (this.hasLighting) { + if (this.lightModelAmbientLocation) GLctx.uniform4fv(this.lightModelAmbientLocation, GLEmulation.lightModelAmbient); + if (this.materialAmbientLocation) GLctx.uniform4fv(this.materialAmbientLocation, GLEmulation.materialAmbient); + if (this.materialDiffuseLocation) GLctx.uniform4fv(this.materialDiffuseLocation, GLEmulation.materialDiffuse); + if (this.materialSpecularLocation) GLctx.uniform4fv(this.materialSpecularLocation, GLEmulation.materialSpecular); + if (this.materialShininessLocation) GLctx.uniform1f(this.materialShininessLocation, GLEmulation.materialShininess[0]); + if (this.materialEmissionLocation) GLctx.uniform4fv(this.materialEmissionLocation, GLEmulation.materialEmission); + for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) { + if (this.lightAmbientLocation[lightId]) GLctx.uniform4fv(this.lightAmbientLocation[lightId], GLEmulation.lightAmbient[lightId]); + if (this.lightDiffuseLocation[lightId]) GLctx.uniform4fv(this.lightDiffuseLocation[lightId], GLEmulation.lightDiffuse[lightId]); + if (this.lightSpecularLocation[lightId]) GLctx.uniform4fv(this.lightSpecularLocation[lightId], GLEmulation.lightSpecular[lightId]); + if (this.lightPositionLocation[lightId]) GLctx.uniform4fv(this.lightPositionLocation[lightId], GLEmulation.lightPosition[lightId]); + } + } + + if (this.hasAlphaTest) { + if (this.alphaTestRefLocation) GLctx.uniform1f(this.alphaTestRefLocation, GLEmulation.alphaTestRef); + } + + if (GLImmediate.mode == GLctx.POINTS) { + if (this.pointSizeLocation) { + GLctx.uniform1f(this.pointSizeLocation, GLEmulation.pointSize); + } + } + }; + + this.cleanup = function() { +#if !GL_FFP_ONLY + GLctx.disableVertexAttribArray(this.positionLocation); + if (this.hasTextures) { + for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { + if (GLImmediate.enabledClientAttributes[GLImmediate.TEXTURE0+i] && this.texCoordLocations[i] >= 0) { + GLctx.disableVertexAttribArray(this.texCoordLocations[i]); + } + } + } + if (this.hasColor) { + GLctx.disableVertexAttribArray(this.colorLocation); + } + if (this.hasNormal) { + GLctx.disableVertexAttribArray(this.normalLocation); + } + if (!GL.currProgram) { + GLctx.useProgram(null); + GLImmediate.fixedFunctionProgram = 0; + } + if (!GLctx.currentArrayBufferBinding) { + GLctx.bindBuffer(GLctx.ARRAY_BUFFER, null); + GLImmediate.lastArrayBuffer = null; + } + +#if GL_UNSAFE_OPTS + GLImmediate.lastRenderer = null; + GLImmediate.lastProgram = null; +#endif + GLImmediate.matricesModified = true; +#endif + } + + this.init(); + } + return new Renderer(); + }, + + setupFuncs() { + // TexEnv stuff needs to be prepared early, so do it here. + // init() is too late for -O2, since it freezes the GL functions + // by that point. + GLImmediate.MapTreeLib = GLImmediate.spawnMapTreeLib(); + GLImmediate.spawnMapTreeLib = null; + + GLImmediate.TexEnvJIT = GLImmediate.spawnTexEnvJIT(); + GLImmediate.spawnTexEnvJIT = null; + + GLImmediate.setupHooks(); + }, + + setupHooks() { + if (!GLEmulation.hasRunInit) { + GLEmulation.init(); + } + + var glActiveTexture = _glActiveTexture; + _glActiveTexture = _emscripten_glActiveTexture = (texture) => { + GLImmediate.TexEnvJIT.hook_activeTexture(texture); + glActiveTexture(texture); + }; + + var glEnable = _glEnable; + _glEnable = _emscripten_glEnable = (cap) => { + GLImmediate.TexEnvJIT.hook_enable(cap); + glEnable(cap); + }; + + var glDisable = _glDisable; + _glDisable = _emscripten_glDisable = (cap) => { + GLImmediate.TexEnvJIT.hook_disable(cap); + glDisable(cap); + }; + + var glTexEnvf = (typeof _glTexEnvf != 'undefined') ? _glTexEnvf : () => {}; + /** @suppress {checkTypes} */ + _glTexEnvf = _emscripten_glTexEnvf = (target, pname, param) => { + GLImmediate.TexEnvJIT.hook_texEnvf(target, pname, param); + // Don't call old func, since we are the implementor. + //glTexEnvf(target, pname, param); + }; + + var glTexEnvi = (typeof _glTexEnvi != 'undefined') ? _glTexEnvi : () => {}; + /** @suppress {checkTypes} */ + _glTexEnvi = _emscripten_glTexEnvi = (target, pname, param) => { + {{{ fromPtr('param') }}} + GLImmediate.TexEnvJIT.hook_texEnvi(target, pname, param); + // Don't call old func, since we are the implementor. + //glTexEnvi(target, pname, param); + }; + + var glTexEnvfv = (typeof _glTexEnvfv != 'undefined') ? _glTexEnvfv : () => {}; + /** @suppress {checkTypes} */ + _glTexEnvfv = _emscripten_glTexEnvfv = (target, pname, param) => { + {{{ fromPtr('param') }}} + GLImmediate.TexEnvJIT.hook_texEnvfv(target, pname, param); + // Don't call old func, since we are the implementor. + //glTexEnvfv(target, pname, param); + }; + + _glGetTexEnviv = (target, pname, param) => { + {{{ fromPtr('param') }}} + GLImmediate.TexEnvJIT.hook_getTexEnviv(target, pname, param); + }; + + _glGetTexEnvfv = (target, pname, param) => { + {{{ fromPtr('param') }}} + GLImmediate.TexEnvJIT.hook_getTexEnvfv(target, pname, param); + }; + + var glGetIntegerv = _glGetIntegerv; + _glGetIntegerv = _emscripten_glGetIntegerv = (pname, params) => { + switch (pname) { + case 0x8B8D: { // GL_CURRENT_PROGRAM + // Just query directly so we're working with WebGL objects. + var cur = GLctx.getParameter(GLctx.CURRENT_PROGRAM); + if (cur == GLImmediate.fixedFunctionProgram) { + // Pretend we're not using a program. + {{{ makeSetValue('params', '0', '0', 'i32') }}}; + return; + } + break; + } + } + glGetIntegerv(pname, params); + }; + }, + + // Main functions + initted: false, + init() { + err('WARNING: using emscripten GL immediate mode emulation. This is very limited in what it supports'); + GLImmediate.initted = true; + + if (!Browser.useWebGL) return; // a 2D canvas may be currently used TODO: make sure we are actually called in that case + + // User can override the maximum number of texture units that we emulate. Using fewer texture units increases runtime performance + // slightly, so it is advantageous to choose as small value as needed. + // Limit to a maximum of 28 to not overflow the state bits used for renderer caching (31 bits = 3 attributes + 28 texture units). + GLImmediate.MAX_TEXTURES = Math.min(Module['GL_MAX_TEXTURE_IMAGE_UNITS'] || GLctx.getParameter(GLctx.MAX_TEXTURE_IMAGE_UNITS), 28); + + GLImmediate.TexEnvJIT.init(GLctx, GLImmediate.MAX_TEXTURES); + + GLImmediate.NUM_ATTRIBUTES = 3 /*pos+normal+color attributes*/ + GLImmediate.MAX_TEXTURES; + GLImmediate.clientAttributes = []; + GLEmulation.enabledClientAttribIndices = []; + for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) { + GLImmediate.clientAttributes.push({}); + GLEmulation.enabledClientAttribIndices.push(false); + } + + // Initialize matrix library + // When user sets a matrix, increment a 'version number' on the new data, and when rendering, submit + // the matrices to the shader program only if they have an old version of the data. + GLImmediate.matrix = []; + GLImmediate.matrixStack = []; + GLImmediate.matrixVersion = []; + for (var i = 0; i < 2 + GLImmediate.MAX_TEXTURES; i++) { // Modelview, Projection, plus one matrix for each texture coordinate. + GLImmediate.matrixStack.push([]); + GLImmediate.matrixVersion.push(0); + GLImmediate.matrix.push(GLImmediate.matrixLib.mat4.create()); + GLImmediate.matrixLib.mat4.identity(GLImmediate.matrix[i]); + } + + // Renderer cache + GLImmediate.rendererCache = GLImmediate.MapTreeLib.create(); + + // Buffers for data + GLImmediate.tempData = new Float32Array(GL.MAX_TEMP_BUFFER_SIZE >> 2); + GLImmediate.indexData = new Uint16Array(GL.MAX_TEMP_BUFFER_SIZE >> 1); + + GLImmediate.vertexDataU8 = new Uint8Array(GLImmediate.tempData.buffer); + + GL.generateTempBuffers(true, GL.currentContext); + + GLImmediate.clientColor = new Float32Array([1, 1, 1, 1]); + }, + + // Prepares and analyzes client attributes. + // Modifies liveClientAttributes, stride, vertexPointer, vertexCounter + // count: number of elements we will draw + // beginEnd: whether we are drawing the results of a begin/end block + prepareClientAttributes(count, beginEnd) { + // If no client attributes were modified since we were last called, do + // nothing. Note that this does not work for glBegin/End, where we + // generate renderer components dynamically and then disable them + // ourselves, but it does help with glDrawElements/Arrays. + if (!GLImmediate.modifiedClientAttributes) { +#if GL_ASSERTIONS + if ((GLImmediate.stride & 3) != 0) { + warnOnce(`Warning: Rendering from client side vertex arrays where stride (${GLImmediate.stride}) is not a multiple of four! This is not currently supported!`); + } +#endif + GLImmediate.vertexCounter = (GLImmediate.stride * count) / 4; // XXX assuming float + return; + } + GLImmediate.modifiedClientAttributes = false; + + // The role of prepareClientAttributes is to examine the set of + // client-side vertex attribute buffers that user code has submitted, and + // to prepare them to be uploaded to a VBO in GPU memory (since WebGL does + // not support client-side rendering, i.e. rendering from vertex data in + // CPU memory). User can submit vertex data generally in three different + // configurations: + // 1. Fully planar: all attributes are in their own separate + // tightly-packed arrays in CPU memory. + // 2. Fully interleaved: all attributes share a single array where data is + // interleaved something like (pos,uv,normal), + // (pos,uv,normal), ... + // 3. Complex hybrid: Multiple separate arrays that either are sparsely + // strided, and/or partially interleaves vertex + // attributes. + + // For simplicity, we support the case (2) as the fast case. For (1) and + // (3), we do a memory copy of the vertex data here to prepare a + // relayouted buffer that is of the structure in case (2). The reason + // for this is that it allows the emulation code to get away with using + // just one VBO buffer for rendering, and not have to maintain multiple + // ones. Therefore cases (1) and (3) will be very slow, and case (2) is + // fast. + + // Detect which case we are in by using a quick heuristic by examining the + // strides of the buffers. If all the buffers have identical stride, we + // assume we have case (2), otherwise we have something more complex. + var clientStartPointer = {{{ POINTER_MAX }}}; + var bytes = 0; // Total number of bytes taken up by a single vertex. + var minStride = {{{ POINTER_MAX }}}; + var maxStride = 0; + var attributes = GLImmediate.liveClientAttributes; + attributes.length = 0; + for (var i = 0; i < 3+GLImmediate.MAX_TEXTURES; i++) { + if (GLImmediate.enabledClientAttributes[i]) { + var attr = GLImmediate.clientAttributes[i]; + attributes.push(attr); + clientStartPointer = Math.min(clientStartPointer, attr.pointer); + attr.sizeBytes = attr.size * GL.byteSizeByType[attr.type - GL.byteSizeByTypeRoot]; + bytes += attr.sizeBytes; + minStride = Math.min(minStride, attr.stride); + maxStride = Math.max(maxStride, attr.stride); + } + } + + if ((minStride != maxStride || maxStride < bytes) && !beginEnd) { + // We are in cases (1) or (3): slow path, shuffle the data around into a + // single interleaved vertex buffer. + // The immediate-mode glBegin()/glEnd() vertex submission gets + // automatically generated in appropriate layout, so never need to come + // down this path if that was used. +#if GL_ASSERTIONS + warnOnce('Rendering from planar client-side vertex arrays. This is a very slow emulation path! Use interleaved vertex arrays for best performance.'); +#endif + GLImmediate.restrideBuffer ||= _malloc(GL.MAX_TEMP_BUFFER_SIZE); + var start = GLImmediate.restrideBuffer; + bytes = 0; + // calculate restrided offsets and total size + for (var attr of attributes) { + var size = attr.sizeBytes; + if (size % 4 != 0) size += 4 - (size % 4); // align everything + attr.offset = bytes; + bytes += size; + } + // copy out the data (we need to know the stride for that, and define attr.pointer) + for (var attr of attributes) { + var srcStride = Math.max(attr.sizeBytes, attr.stride); + if ((srcStride & 3) == 0 && (attr.sizeBytes & 3) == 0) { + for (var j = 0; j < count; j++) { + for (var k = 0; k < attr.sizeBytes; k+=4) { // copy in chunks of 4 bytes, our alignment makes this possible + var val = {{{ makeGetValue('attr.pointer', 'j*srcStride + k', 'i32') }}}; + {{{ makeSetValue('start + attr.offset', 'bytes*j + k', 'val', 'i32') }}}; + } + } + } else { + for (var j = 0; j < count; j++) { + for (var k = 0; k < attr.sizeBytes; k++) { // source data was not aligned to multiples of 4, must copy byte by byte. + HEAP8[start + attr.offset + bytes*j + k] = HEAP8[attr.pointer + j*srcStride + k]; + } + } + } + attr.pointer = start + attr.offset; + } + GLImmediate.stride = bytes; + GLImmediate.vertexPointer = start; + } else { + // case (2): fast path, all data is interleaved to a single vertex array so we can get away with a single VBO upload. + if (GLctx.currentArrayBufferBinding) { + GLImmediate.vertexPointer = 0; + } else { + GLImmediate.vertexPointer = clientStartPointer; + } + for (var attr of attributes) { + attr.offset = attr.pointer - GLImmediate.vertexPointer; // Compute what will be the offset of this attribute in the VBO after we upload. + } + GLImmediate.stride = Math.max(maxStride, bytes); + } + if (!beginEnd) { +#if GL_ASSERTIONS + if ((GLImmediate.stride & 3) != 0) { + warnOnce(`Warning: Rendering from client side vertex arrays where stride (${GLImmediate.stride}) is not a multiple of four! This is not currently supported!`); + } +#endif + GLImmediate.vertexCounter = (GLImmediate.stride * count) / 4; // XXX assuming float + } + }, + + flush(numProvidedIndexes, startIndex = 0, ptr = 0) { +#if ASSERTIONS + assert(numProvidedIndexes >= 0 || !numProvidedIndexes); +#endif + var renderer = GLImmediate.getRenderer(); + + // Generate index data in a format suitable for GLES 2.0/WebGL + var numVertices = 4 * GLImmediate.vertexCounter / GLImmediate.stride; + if (!numVertices) return; +#if ASSERTIONS + assert(numVertices % 1 == 0, "`numVertices` must be an integer."); +#endif + var emulatedElementArrayBuffer = false; + var numIndexes = 0; + if (numProvidedIndexes) { + numIndexes = numProvidedIndexes; + if (!GLctx.currentArrayBufferBinding && GLImmediate.firstVertex > GLImmediate.lastVertex) { + // Figure out the first and last vertex from the index data +#if ASSERTIONS + // If we are going to upload array buffer data, we need to find which range to + // upload based on the indices. If they are in a buffer on the GPU, that is very + // inconvenient! So if you do not have an array buffer, you should also not have + // an element array buffer. But best is to use both buffers! + assert(!GLctx.currentElementArrayBufferBinding); +#endif + for (var i = 0; i < numProvidedIndexes; i++) { + var currIndex = {{{ makeGetValue('ptr', 'i*2', 'u16') }}}; + GLImmediate.firstVertex = Math.min(GLImmediate.firstVertex, currIndex); + GLImmediate.lastVertex = Math.max(GLImmediate.lastVertex, currIndex+1); + } + } + if (!GLctx.currentElementArrayBufferBinding) { + // If no element array buffer is bound, then indices is a literal pointer to clientside data +#if ASSERTIONS + assert(numProvidedIndexes << 1 <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (a)'); +#endif + var indexBuffer = GL.getTempIndexBuffer(numProvidedIndexes << 1); + GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, indexBuffer); + GLctx.bufferSubData(GLctx.ELEMENT_ARRAY_BUFFER, 0, {{{ makeHEAPView('U16', 'ptr', 'ptr + (numProvidedIndexes << 1)') }}}); + ptr = 0; + emulatedElementArrayBuffer = true; + } + } else if (GLImmediate.mode > 6) { // above GL_TRIANGLE_FAN are the non-GL ES modes + if (GLImmediate.mode != 7) abort('unsupported immediate mode ' + GLImmediate.mode); // GL_QUADS + // GLImmediate.firstVertex is the first vertex we want. Quad indexes are + // in the pattern 0 1 2, 0 2 3, 4 5 6, 4 6 7, so we need to look at + // index firstVertex * 1.5 to see it. Then since indexes are 2 bytes + // each, that means 3 +#if ASSERTIONS + assert(GLImmediate.firstVertex % 4 == 0); +#endif + ptr = GLImmediate.firstVertex * 3; + var numQuads = numVertices / 4; + numIndexes = numQuads * 6; // 0 1 2, 0 2 3 pattern +#if ASSERTIONS + assert(ptr + (numIndexes << 1) <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (b)'); +#endif + GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.currentContext.tempQuadIndexBuffer); + emulatedElementArrayBuffer = true; + GLImmediate.mode = GLctx.TRIANGLES; + } + + renderer.prepare(); + + if (numIndexes) { + GLctx.drawElements(GLImmediate.mode, numIndexes, GLctx.UNSIGNED_SHORT, ptr); + } else { + GLctx.drawArrays(GLImmediate.mode, startIndex, numVertices); + } + + if (emulatedElementArrayBuffer) { + GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GLctx.currentElementArrayBufferBinding] || null); + } + +#if !GL_UNSAFE_OPTS +#if !GL_FFP_ONLY + renderer.cleanup(); +#endif +#endif + } + }, + + $GLImmediateSetup__deps: ['$GLImmediate', () => 'GLImmediate.matrixLib = ' + read('gl-matrix.js') + ';\n'], + $GLImmediateSetup: {}, + + glBegin__deps: ['$GLImmediateSetup'], + glBegin: (mode) => { + // Push the old state: + GLImmediate.enabledClientAttributes_preBegin = GLImmediate.enabledClientAttributes; + GLImmediate.enabledClientAttributes = []; + + GLImmediate.clientAttributes_preBegin = GLImmediate.clientAttributes; + GLImmediate.clientAttributes = [] + for (var i = 0; i < GLImmediate.clientAttributes_preBegin.length; i++) { + GLImmediate.clientAttributes.push({}); + } + + GLImmediate.mode = mode; + GLImmediate.vertexCounter = 0; + var components = GLImmediate.rendererComponents = []; + for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) { + components[i] = 0; + } + GLImmediate.rendererComponentPointer = 0; + GLImmediate.vertexData = GLImmediate.tempData; + }, + + glEnd: () => { + GLImmediate.prepareClientAttributes(GLImmediate.rendererComponents[GLImmediate.VERTEX], true); + GLImmediate.firstVertex = 0; + GLImmediate.lastVertex = GLImmediate.vertexCounter / (GLImmediate.stride >> 2); + GLImmediate.flush(); + GLImmediate.disableBeginEndClientAttributes(); + GLImmediate.mode = -1; + + // Pop the old state: + GLImmediate.enabledClientAttributes = GLImmediate.enabledClientAttributes_preBegin; + GLImmediate.clientAttributes = GLImmediate.clientAttributes_preBegin; + GLImmediate.currentRenderer = null; // The set of active client attributes changed, we must re-lookup the renderer to use. + GLImmediate.modifiedClientAttributes = true; + }, + + glVertex2f: (x, y) => { +#if ASSERTIONS + assert(GLImmediate.mode >= 0); // must be in begin/end +#endif + GLImmediate.vertexData[GLImmediate.vertexCounter++] = x; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = y; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = 0; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = 1; +#if ASSERTIONS + assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE); +#endif + GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT); + }, + + glVertex3f: (x, y, z) => { +#if ASSERTIONS + assert(GLImmediate.mode >= 0); // must be in begin/end +#endif + GLImmediate.vertexData[GLImmediate.vertexCounter++] = x; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = y; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = z; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = 1; +#if ASSERTIONS + assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE); +#endif + GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT); + }, + + glVertex4f: (x, y, z, w) => { +#if ASSERTIONS + assert(GLImmediate.mode >= 0); // must be in begin/end +#endif + GLImmediate.vertexData[GLImmediate.vertexCounter++] = x; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = y; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = z; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = w; +#if ASSERTIONS + assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE); +#endif + GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT); + }, + + glVertex2fv__deps: ['glVertex2f'], + glVertex2fv: (p) => _glVertex2f({{{ makeGetValue('p', '0', 'float') }}}, + {{{ makeGetValue('p', '4', 'float') }}}), + + glVertex3fv__deps: ['glVertex3f'], + glVertex3fv: (p) => _glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, + {{{ makeGetValue('p', '4', 'float') }}}, + {{{ makeGetValue('p', '8', 'float') }}}), + + glVertex4fv__deps: ['glVertex4f'], + glVertex4fv: (p) => _glVertex4f({{{ makeGetValue('p', '0', 'float') }}}, + {{{ makeGetValue('p', '4', 'float') }}}, + {{{ makeGetValue('p', '8', 'float') }}}, + {{{ makeGetValue('p', '12', 'float') }}}), + + glVertex2i: 'glVertex2f', + + glVertex3i: 'glVertex3f', + + glVertex4i: 'glVertex4f', + + glTexCoord2i: (u, v) => { +#if ASSERTIONS + assert(GLImmediate.mode >= 0); // must be in begin/end +#endif + GLImmediate.vertexData[GLImmediate.vertexCounter++] = u; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = v; + GLImmediate.addRendererComponent(GLImmediate.TEXTURE0, 2, GLctx.FLOAT); + }, + glTexCoord2f: 'glTexCoord2i', + + glTexCoord2fv__deps: ['glTexCoord2i'], + glTexCoord2fv: (v) => + _glTexCoord2i({{{ makeGetValue('v', '0', 'float') }}}, {{{ makeGetValue('v', '4', 'float') }}}), + + glTexCoord4f: () => { abort('glTexCoord4f: TODO') }, + + glColor4f: (r, g, b, a) => { + r = Math.max(Math.min(r, 1), 0); + g = Math.max(Math.min(g, 1), 0); + b = Math.max(Math.min(b, 1), 0); + a = Math.max(Math.min(a, 1), 0); + + // TODO: make ub the default, not f, save a few mathops + if (GLImmediate.mode >= 0) { + var start = GLImmediate.vertexCounter << 2; + GLImmediate.vertexDataU8[start + 0] = r * 255; + GLImmediate.vertexDataU8[start + 1] = g * 255; + GLImmediate.vertexDataU8[start + 2] = b * 255; + GLImmediate.vertexDataU8[start + 3] = a * 255; + GLImmediate.vertexCounter++; + GLImmediate.addRendererComponent(GLImmediate.COLOR, 4, GLctx.UNSIGNED_BYTE); + } else { + GLImmediate.clientColor[0] = r; + GLImmediate.clientColor[1] = g; + GLImmediate.clientColor[2] = b; + GLImmediate.clientColor[3] = a; +#if GL_FFP_ONLY + GLctx.vertexAttrib4fv(GLImmediate.COLOR, GLImmediate.clientColor); +#endif + } + }, + + glColor4d: 'glColor4f', + + glColor4ub__deps: ['glColor4f'], + glColor4ub: (r, g, b, a) => _glColor4f((r&255)/255, (g&255)/255, (b&255)/255, (a&255)/255), + + glColor4us__deps: ['glColor4f'], + glColor4us: (r, g, b, a) => _glColor4f((r&65535)/65535, (g&65535)/65535, (b&65535)/65535, (a&65535)/65535), + + glColor4ui__deps: ['glColor4f'], + glColor4ui: (r, g, b, a) => _glColor4f((r>>>0)/4294967295, (g>>>0)/4294967295, (b>>>0)/4294967295, (a>>>0)/4294967295), + + glColor3f__deps: ['glColor4f'], + glColor3f: (r, g, b) => _glColor4f(r, g, b, 1), + + glColor3d: 'glColor3f', + + glColor3ub__deps: ['glColor4ub'], + glColor3ub: (r, g, b) => _glColor4ub(r, g, b, 255), + + glColor3us__deps: ['glColor4us'], + glColor3us: (r, g, b) => _glColor4us(r, g, b, 65535), + + glColor3ui__deps: ['glColor4ui'], + glColor3ui: (r, g, b) => _glColor4ui(r, g, b, 4294967295), + + glColor3ubv__deps: ['glColor3ub'], + glColor3ubv: (p) => _glColor3ub({{{ makeGetValue('p', '0', 'i8') }}}, + {{{ makeGetValue('p', '1', 'i8') }}}, + {{{ makeGetValue('p', '2', 'i8') }}}), + + glColor3usv__deps: ['glColor3us'], + glColor3usv: (p) => _glColor3us({{{ makeGetValue('p', '0', 'i16') }}}, + {{{ makeGetValue('p', '2', 'i16') }}}, + {{{ makeGetValue('p', '4', 'i16') }}}), + + glColor3uiv__deps: ['glColor3ui'], + glColor3uiv: (p) => _glColor3ui({{{ makeGetValue('p', '0', 'i32') }}}, + {{{ makeGetValue('p', '4', 'i32') }}}, + {{{ makeGetValue('p', '8', 'i32') }}}), + + glColor3fv__deps: ['glColor3f'], + glColor3fv: (p) => _glColor3f({{{ makeGetValue('p', '0', 'float') }}}, + {{{ makeGetValue('p', '4', 'float') }}}, + {{{ makeGetValue('p', '8', 'float') }}}), + + glColor4fv__deps: ['glColor4f'], + glColor4fv: (p) => _glColor4f({{{ makeGetValue('p', '0', 'float') }}}, + {{{ makeGetValue('p', '4', 'float') }}}, + {{{ makeGetValue('p', '8', 'float') }}}, + {{{ makeGetValue('p', '12', 'float') }}}), + + glColor4ubv__deps: ['glColor4ub'], + glColor4ubv: (p) => _glColor4ub({{{ makeGetValue('p', '0', 'i8') }}}, + {{{ makeGetValue('p', '1', 'i8') }}}, + {{{ makeGetValue('p', '2', 'i8') }}}, + {{{ makeGetValue('p', '3', 'i8') }}}), + + glFogf: (pname, param) => { // partial support, TODO + switch (pname) { + case 0xB63: // GL_FOG_START + GLEmulation.fogStart = param; break; + case 0xB64: // GL_FOG_END + GLEmulation.fogEnd = param; break; + case 0xB62: // GL_FOG_DENSITY + GLEmulation.fogDensity = param; break; + case 0xB65: // GL_FOG_MODE + switch (param) { + case 0x801: // GL_EXP2 + case 0x2601: // GL_LINEAR + if (GLEmulation.fogMode != param) { + GLImmediate.currentRenderer = null; // Fog mode is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.fogMode = param; + } + break; + default: // default to GL_EXP + if (GLEmulation.fogMode != 0x800 /* GL_EXP */) { + GLImmediate.currentRenderer = null; // Fog mode is part of the FFP shader state, we must re-lookup the renderer to use. + GLEmulation.fogMode = 0x800 /* GL_EXP */; + } + break; + } + break; + } + }, + glFogi__deps: ['glFogf'], + glFogi: (pname, param) => { + return _glFogf(pname, param); + }, + glFogfv__deps: ['glFogf'], + glFogfv: (pname, param) => { // partial support, TODO + switch (pname) { + case 0xB66: // GL_FOG_COLOR + GLEmulation.fogColor[0] = {{{ makeGetValue('param', '0', 'float') }}}; + GLEmulation.fogColor[1] = {{{ makeGetValue('param', '4', 'float') }}}; + GLEmulation.fogColor[2] = {{{ makeGetValue('param', '8', 'float') }}}; + GLEmulation.fogColor[3] = {{{ makeGetValue('param', '12', 'float') }}}; + break; + case 0xB63: // GL_FOG_START + case 0xB64: // GL_FOG_END + _glFogf(pname, {{{ makeGetValue('param', '0', 'float') }}}); break; + } + }, + glFogiv__deps: ['glFogf'], + glFogiv: (pname, param) => { + switch (pname) { + case 0xB66: // GL_FOG_COLOR + GLEmulation.fogColor[0] = ({{{ makeGetValue('param', '0', 'i32') }}}/2147483647)/2.0+0.5; + GLEmulation.fogColor[1] = ({{{ makeGetValue('param', '4', 'i32') }}}/2147483647)/2.0+0.5; + GLEmulation.fogColor[2] = ({{{ makeGetValue('param', '8', 'i32') }}}/2147483647)/2.0+0.5; + GLEmulation.fogColor[3] = ({{{ makeGetValue('param', '12', 'i32') }}}/2147483647)/2.0+0.5; + break; + default: + _glFogf(pname, {{{ makeGetValue('param', '0', 'i32') }}}); break; + } + }, + glFogx: 'glFogi', + glFogxv: 'glFogiv', + + glPointSize: (size) => { + GLEmulation.pointSize = size; + }, + + glPolygonMode: () => {}, // TODO + + glAlphaFunc: (func, ref) => { + switch(func) { + case 0x200: // GL_NEVER + case 0x201: // GL_LESS + case 0x202: // GL_EQUAL + case 0x203: // GL_LEQUAL + case 0x204: // GL_GREATER + case 0x205: // GL_NOTEQUAL + case 0x206: // GL_GEQUAL + case 0x207: // GL_ALWAYS + GLEmulation.alphaTestRef = ref; + if (GLEmulation.alphaTestFunc != func) { + GLEmulation.alphaTestFunc = func; + GLImmediate.currentRenderer = null; // alpha test mode is part of the FFP shader state, we must re-lookup the renderer to use. + } + break; + default: // invalid value provided +#if GL_ASSERTIONS + err(`glAlphaFunc: Invalid alpha comparison function ${ptrToString(func)}!`); +#endif + break; + } + }, + + glNormal3f: (x, y, z) => { +#if ASSERTIONS + assert(GLImmediate.mode >= 0); // must be in begin/end +#endif + GLImmediate.vertexData[GLImmediate.vertexCounter++] = x; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = y; + GLImmediate.vertexData[GLImmediate.vertexCounter++] = z; +#if ASSERTIONS + assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE); +#endif + GLImmediate.addRendererComponent(GLImmediate.NORMAL, 3, GLctx.FLOAT); + }, + + glNormal3fv__deps: ['glNormal3f'], + glNormal3fv: (p) => { + _glNormal3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}}); + }, + + + // Additional non-GLES rendering calls + + glDrawRangeElements__deps: ['glDrawElements'], + glDrawRangeElements: (mode, start, end, count, type, indices) => { + _glDrawElements(mode, count, type, indices, start, end); + }, + + // ClientState/gl*Pointer + + glEnableClientState: (cap) => { + var attrib = GLEmulation.getAttributeFromCapability(cap); + if (attrib === null) { +#if ASSERTIONS + err(`WARNING: unhandled clientstate: ${cap}`); +#endif + return; + } + if (!GLImmediate.enabledClientAttributes[attrib]) { + GLImmediate.enabledClientAttributes[attrib] = true; + GLImmediate.totalEnabledClientAttributes++; + GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed. +#if GL_FFP_ONLY + // In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here. + GL.enableVertexAttribArray(attrib); +#endif + if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1; + GLImmediate.modifiedClientAttributes = true; + } + }, + glDisableClientState: (cap) => { + var attrib = GLEmulation.getAttributeFromCapability(cap); + if (attrib === null) { +#if ASSERTIONS + err(`WARNING: unhandled clientstate: ${cap}`); +#endif + return; + } + if (GLImmediate.enabledClientAttributes[attrib]) { + GLImmediate.enabledClientAttributes[attrib] = false; + GLImmediate.totalEnabledClientAttributes--; + GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed. +#if GL_FFP_ONLY + // In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here. + GL.disableVertexAttribArray(attrib); +#endif + if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap]; + GLImmediate.modifiedClientAttributes = true; + } + }, + + glVertexPointer: (size, type, stride, pointer) => { + GLImmediate.setClientAttribute(GLImmediate.VERTEX, size, type, stride, pointer); +#if GL_FFP_ONLY + if (GLctx.currentArrayBufferBinding) { + GLctx.vertexAttribPointer(GLImmediate.VERTEX, size, type, false, stride, pointer); + } +#endif + }, + glTexCoordPointer: (size, type, stride, pointer) => { + GLImmediate.setClientAttribute(GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture, size, type, stride, pointer); +#if GL_FFP_ONLY + if (GLctx.currentArrayBufferBinding) { + var loc = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture; + GLctx.vertexAttribPointer(loc, size, type, false, stride, pointer); + } +#endif + }, + glNormalPointer: (type, stride, pointer) => { + GLImmediate.setClientAttribute(GLImmediate.NORMAL, 3, type, stride, pointer); +#if GL_FFP_ONLY + if (GLctx.currentArrayBufferBinding) { + GLctx.vertexAttribPointer(GLImmediate.NORMAL, 3, type, true, stride, pointer); + } +#endif + }, + glColorPointer: (size, type, stride, pointer) => { + GLImmediate.setClientAttribute(GLImmediate.COLOR, size, type, stride, pointer); +#if GL_FFP_ONLY + if (GLctx.currentArrayBufferBinding) { + GLctx.vertexAttribPointer(GLImmediate.COLOR, size, type, true, stride, pointer); + } +#endif + }, + + glClientActiveTexture: (texture) => { + GLImmediate.clientActiveTexture = texture - 0x84C0; // GL_TEXTURE0 + }, + + // Replace some functions with immediate-mode aware versions. If there are no + // client attributes enabled, and we use webgl-friendly modes (no GL_QUADS), + // then no need for emulation + glDrawArrays: (mode, first, count) => { + if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6) { + GLctx.drawArrays(mode, first, count); + return; + } + GLImmediate.prepareClientAttributes(count, false); + GLImmediate.mode = mode; + if (!GLctx.currentArrayBufferBinding) { + GLImmediate.vertexData = {{{ makeHEAPView('F32', 'GLImmediate.vertexPointer', 'GLImmediate.vertexPointer + (first+count)*GLImmediate.stride') }}}; // XXX assuming float + GLImmediate.firstVertex = first; + GLImmediate.lastVertex = first + count; + } + GLImmediate.flush(null, first); + GLImmediate.mode = -1; + }, + + // start, end are given if we come from glDrawRangeElements + glDrawElements: (mode, count, type, indices, start, end) => { + if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6 && GLctx.currentElementArrayBufferBinding) { + GLctx.drawElements(mode, count, type, indices); + return; + } +#if ASSERTIONS + if (!GLctx.currentElementArrayBufferBinding) { + assert(type == GLctx.UNSIGNED_SHORT); // We can only emulate buffers of this kind, for now + } + warnOnce("DrawElements doesn't actually prepareClientAttributes properly."); +#endif + GLImmediate.prepareClientAttributes(count, false); + GLImmediate.mode = mode; + if (!GLctx.currentArrayBufferBinding) { + GLImmediate.firstVertex = end ? start : HEAP8.length; // if we don't know the start, set an invalid value and we will calculate it later from the indices + GLImmediate.lastVertex = end ? end + 1 : 0; + start = GLImmediate.vertexPointer; + // TODO(sbc): Combine these two subarray calls back into a single one if + // we ever fix https://github.com/emscripten-core/emscripten/issues/21250. + if (end) { + end = GLImmediate.vertexPointer + (end +1 ) * GLImmediate.stride; + GLImmediate.vertexData = HEAPF32.subarray({{{ getHeapOffset('start', 'float') }}}, {{{ getHeapOffset('end', 'float') }}}); + } else { + GLImmediate.vertexData = HEAPF32.subarray({{{ getHeapOffset('start', 'float') }}}); + } + } + GLImmediate.flush(count, 0, indices); + GLImmediate.mode = -1; + }, + + // Vertex array object (VAO) support. TODO: when the WebGL extension is + // popular, use that and remove this code and GL.vaos + $emulGlGenVertexArrays__deps: ['$GLEmulation'], + $emulGlGenVertexArrays: (n, vaos) => { + for (var i = 0; i < n; i++) { + var id = GL.getNewId(GLEmulation.vaos); + GLEmulation.vaos[id] = { + id, + arrayBuffer: 0, + elementArrayBuffer: 0, + enabledVertexAttribArrays: {}, + vertexAttribPointers: {}, + enabledClientStates: {}, + }; + {{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}}; + } + }, + $emulGlDeleteVertexArrays: (n, vaos) => { + for (var i = 0; i < n; i++) { + var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}}; + GLEmulation.vaos[id] = null; + if (GLEmulation.currentVao?.id == id) GLEmulation.currentVao = null; + } + }, + $emulGlIsVertexArray: (array) => { + var vao = GLEmulation.vaos[array]; + if (!vao) return 0; + return 1; + }, + $emulGlBindVertexArray__deps: ['glBindBuffer', 'glEnableVertexAttribArray', 'glVertexAttribPointer', 'glEnableClientState'], + $emulGlBindVertexArray: (vao) => { + // undo vao-related things, wipe the slate clean, both for vao of 0 or an actual vao + GLEmulation.currentVao = null; // make sure the commands we run here are not recorded + GLImmediate.lastRenderer?.cleanup(); + _glBindBuffer(GLctx.ARRAY_BUFFER, 0); // XXX if one was there before we were bound? + _glBindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, 0); + for (var vaa in GLEmulation.enabledVertexAttribArrays) { + GLctx.disableVertexAttribArray(vaa); + } + GLEmulation.enabledVertexAttribArrays = {}; + GLImmediate.enabledClientAttributes = [0, 0]; + GLImmediate.totalEnabledClientAttributes = 0; + GLImmediate.modifiedClientAttributes = true; + if (vao) { + // replay vao + var info = GLEmulation.vaos[vao]; + _glBindBuffer(GLctx.ARRAY_BUFFER, info.arrayBuffer); // XXX overwrite current binding? + _glBindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, info.elementArrayBuffer); + for (var vaa in info.enabledVertexAttribArrays) { + _glEnableVertexAttribArray(vaa); + } + for (var vaa in info.vertexAttribPointers) { + _glVertexAttribPointer(...info.vertexAttribPointers[vaa]); + } + for (var attrib in info.enabledClientStates) { + _glEnableClientState(attrib|0); + } + GLEmulation.currentVao = info; // set currentVao last, so the commands we ran here were not recorded + } + }, + + // OpenGL Immediate Mode matrix routines. + // Note that in the future we might make these available only in certain modes. + glMatrixMode__deps: ['$GL', '$GLImmediateSetup'], + glMatrixMode: (mode) => { + if (mode == 0x1700 /* GL_MODELVIEW */) { + GLImmediate.currentMatrix = 0/*m*/; + } else if (mode == 0x1701 /* GL_PROJECTION */) { + GLImmediate.currentMatrix = 1/*p*/; + } else if (mode == 0x1702) { // GL_TEXTURE + GLImmediate.useTextureMatrix = true; + GLImmediate.currentMatrix = 2/*t*/ + GLImmediate.TexEnvJIT.getActiveTexture(); + } else { + throw `Wrong mode ${mode} passed to glMatrixMode`; + } + }, + + glPushMatrix: () => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixStack[GLImmediate.currentMatrix].push( + Array.prototype.slice.call(GLImmediate.matrix[GLImmediate.currentMatrix])); + }, + + glPopMatrix: () => { + if (GLImmediate.matrixStack[GLImmediate.currentMatrix].length == 0) { + GL.recordError(0x504/*GL_STACK_UNDERFLOW*/); + return; + } + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrix[GLImmediate.currentMatrix] = GLImmediate.matrixStack[GLImmediate.currentMatrix].pop(); + }, + + glLoadIdentity__deps: ['$GL', '$GLImmediateSetup'], + glLoadIdentity: () => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.identity(GLImmediate.matrix[GLImmediate.currentMatrix]); + }, + + glLoadMatrixd: (matrix) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]); + }, + + glLoadMatrixf: (matrix) => { +#if GL_DEBUG + if (GL.debug) dbg('glLoadMatrixf receiving: ' + Array.prototype.slice.call(HEAPF32.subarray(matrix >> 2, (matrix >> 2) + 16))); +#endif + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]); + }, + + glLoadTransposeMatrixd: (matrix) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]); + GLImmediate.matrixLib.mat4.transpose(GLImmediate.matrix[GLImmediate.currentMatrix]); + }, + + glLoadTransposeMatrixf: (matrix) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]); + GLImmediate.matrixLib.mat4.transpose(GLImmediate.matrix[GLImmediate.currentMatrix]); + }, + + glMultMatrixd: (matrix) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], + {{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}); + }, + + glMultMatrixf: (matrix) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], + {{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}); + }, + + glMultTransposeMatrixd: (matrix) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + var colMajor = GLImmediate.matrixLib.mat4.create(); + GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, colMajor); + GLImmediate.matrixLib.mat4.transpose(colMajor); + GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], colMajor); + }, + + glMultTransposeMatrixf: (matrix) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + var colMajor = GLImmediate.matrixLib.mat4.create(); + GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, colMajor); + GLImmediate.matrixLib.mat4.transpose(colMajor); + GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], colMajor); + }, + + glFrustum: (left, right, bottom, top_, nearVal, farVal) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], + GLImmediate.matrixLib.mat4.frustum(left, right, bottom, top_, nearVal, farVal)); + }, + glFrustumf: 'glFrustum', + + glOrtho: (left, right, bottom, top_, nearVal, farVal) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], + GLImmediate.matrixLib.mat4.ortho(left, right, bottom, top_, nearVal, farVal)); + }, + glOrthof: 'glOrtho', + + glScaled: (x, y, z) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.scale(GLImmediate.matrix[GLImmediate.currentMatrix], [x, y, z]); + }, + glScalef: 'glScaled', + + glTranslated: (x, y, z) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.translate(GLImmediate.matrix[GLImmediate.currentMatrix], [x, y, z]); + }, + glTranslatef: 'glTranslated', + + glRotated: (angle, x, y, z) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.rotate(GLImmediate.matrix[GLImmediate.currentMatrix], angle*Math.PI/180, [x, y, z]); + }, + glRotatef: 'glRotated', + + glDrawBuffer: () => { abort('glDrawBuffer: TODO') }, +#if MAX_WEBGL_VERSION < 2 + glReadBuffer: () => { abort('glReadBuffer: TODO') }, +#endif + + glClipPlane: (pname, param) => { + if ((pname >= 0x3000) && (pname < 0x3006) /* GL_CLIP_PLANE0 to GL_CLIP_PLANE5 */) { + var clipPlaneId = pname - 0x3000; + + GLEmulation.clipPlaneEquation[clipPlaneId][0] = {{{ makeGetValue('param', '0', 'double') }}}; + GLEmulation.clipPlaneEquation[clipPlaneId][1] = {{{ makeGetValue('param', '8', 'double') }}}; + GLEmulation.clipPlaneEquation[clipPlaneId][2] = {{{ makeGetValue('param', '16', 'double') }}}; + GLEmulation.clipPlaneEquation[clipPlaneId][3] = {{{ makeGetValue('param', '24', 'double') }}}; + + // apply inverse transposed current modelview matrix when setting clip plane + var tmpMV = GLImmediate.matrixLib.mat4.create(GLImmediate.matrix[0]); + GLImmediate.matrixLib.mat4.inverse(tmpMV); + GLImmediate.matrixLib.mat4.transpose(tmpMV); + GLImmediate.matrixLib.mat4.multiplyVec4(tmpMV, GLEmulation.clipPlaneEquation[clipPlaneId]); + } + }, + + glLightfv: (light, pname, param) => { + if ((light >= 0x4000) && (light < 0x4008) /* GL_LIGHT0 to GL_LIGHT7 */) { + var lightId = light - 0x4000; + + if (pname == 0x1200) { // GL_AMBIENT + GLEmulation.lightAmbient[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}}; + GLEmulation.lightAmbient[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}}; + GLEmulation.lightAmbient[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}}; + GLEmulation.lightAmbient[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}}; + } else if (pname == 0x1201) { // GL_DIFFUSE + GLEmulation.lightDiffuse[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}}; + GLEmulation.lightDiffuse[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}}; + GLEmulation.lightDiffuse[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}}; + GLEmulation.lightDiffuse[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}}; + } else if (pname == 0x1202) { // GL_SPECULAR + GLEmulation.lightSpecular[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}}; + GLEmulation.lightSpecular[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}}; + GLEmulation.lightSpecular[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}}; + GLEmulation.lightSpecular[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}}; + } else if (pname == 0x1203) { // GL_POSITION + GLEmulation.lightPosition[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}}; + GLEmulation.lightPosition[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}}; + GLEmulation.lightPosition[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}}; + GLEmulation.lightPosition[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}}; + + // multiply position with current modelviewmatrix + GLImmediate.matrixLib.mat4.multiplyVec4(GLImmediate.matrix[0], GLEmulation.lightPosition[lightId]); + } else { + abort('glLightfv: TODO: ' + pname); + } + } + }, + + glLightModelf: (pname, param) => { + if (pname == 0x0B52) { // GL_LIGHT_MODEL_TWO_SIDE + GLEmulation.lightModelTwoSide = (param != 0) ? true : false; + } else { + abort('glLightModelf: TODO: ' + pname); + } + }, + + glLightModelfv: (pname, param) => { // TODO: GL_LIGHT_MODEL_LOCAL_VIEWER + if (pname == 0x0B53) { // GL_LIGHT_MODEL_AMBIENT + GLEmulation.lightModelAmbient[0] = {{{ makeGetValue('param', '0', 'float') }}}; + GLEmulation.lightModelAmbient[1] = {{{ makeGetValue('param', '4', 'float') }}}; + GLEmulation.lightModelAmbient[2] = {{{ makeGetValue('param', '8', 'float') }}}; + GLEmulation.lightModelAmbient[3] = {{{ makeGetValue('param', '12', 'float') }}}; + } else { + abort('glLightModelfv: TODO: ' + pname); + } + }, + + glMaterialfv: (face, pname, param) => { + if ((face != 0x0404) && (face != 0x0408)) { abort('glMaterialfv: TODO' + face); } // only GL_FRONT and GL_FRONT_AND_BACK supported + + if (pname == 0x1200) { // GL_AMBIENT + GLEmulation.materialAmbient[0] = {{{ makeGetValue('param', '0', 'float') }}}; + GLEmulation.materialAmbient[1] = {{{ makeGetValue('param', '4', 'float') }}}; + GLEmulation.materialAmbient[2] = {{{ makeGetValue('param', '8', 'float') }}}; + GLEmulation.materialAmbient[3] = {{{ makeGetValue('param', '12', 'float') }}}; + } else if (pname == 0x1201) { // GL_DIFFUSE + GLEmulation.materialDiffuse[0] = {{{ makeGetValue('param', '0', 'float') }}}; + GLEmulation.materialDiffuse[1] = {{{ makeGetValue('param', '4', 'float') }}}; + GLEmulation.materialDiffuse[2] = {{{ makeGetValue('param', '8', 'float') }}}; + GLEmulation.materialDiffuse[3] = {{{ makeGetValue('param', '12', 'float') }}}; + } else if (pname == 0x1202) { // GL_SPECULAR + GLEmulation.materialSpecular[0] = {{{ makeGetValue('param', '0', 'float') }}}; + GLEmulation.materialSpecular[1] = {{{ makeGetValue('param', '4', 'float') }}}; + GLEmulation.materialSpecular[2] = {{{ makeGetValue('param', '8', 'float') }}}; + GLEmulation.materialSpecular[3] = {{{ makeGetValue('param', '12', 'float') }}}; + } else if (pname == 0x1601) { // GL_SHININESS + GLEmulation.materialShininess[0] = {{{ makeGetValue('param', '0', 'float') }}}; + } else { + abort('glMaterialfv: TODO: ' + pname); + } + }, + + glTexGeni: (coord, pname, param) => abort('glTexGeni: TODO'), + glTexGenfv: (coord, pname, param) => abort('glTexGenfv: TODO'), + glTexEnvi: (target, pname, params) => warnOnce('glTexEnvi: TODO'), + glTexEnvf: (target, pname, params) => warnOnce('glTexEnvf: TODO'), + glTexEnvfv: (target, pname, params) => warnOnce('glTexEnvfv: TODO'), + + glGetTexEnviv: (target, pname, param) => abort('GL emulation not initialized!'), + glGetTexEnvfv: (target, pname, param) => abort('GL emulation not initialized!'), + + glTexImage1D: (target, level, internalformat, width, border, format, type, data) => abort('glTexImage1D: TODO'), + glTexCoord3f: (target, level, internalformat, width, border, format, type, data) => abort('glTexCoord3f: TODO'), + glGetTexLevelParameteriv: (target, level, pname, params) => abort('glGetTexLevelParameteriv: TODO'), + + glShadeModel: () => warnOnce('TODO: glShadeModel'), + + // Open GLES1.1 compatibility + + glGenFramebuffersOES: 'glGenFramebuffers', + glGenRenderbuffersOES: 'glGenRenderbuffers', + glBindFramebufferOES: 'glBindFramebuffer', + glBindRenderbufferOES: 'glBindRenderbuffer', + glGetRenderbufferParameterivOES: 'glGetRenderbufferParameteriv', + glFramebufferRenderbufferOES: 'glFramebufferRenderbuffer', + glRenderbufferStorageOES: 'glRenderbufferStorage', + glCheckFramebufferStatusOES: 'glCheckFramebufferStatus', + glDeleteFramebuffersOES: 'glDeleteFramebuffers', + glDeleteRenderbuffersOES: 'glDeleteRenderbuffers', + glFramebufferTexture2DOES: 'glFramebufferTexture2D', + + // GLU + + gluPerspective: (fov, aspect, near, far) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrix[GLImmediate.currentMatrix] = + GLImmediate.matrixLib.mat4.perspective(fov, aspect, near, far, + GLImmediate.matrix[GLImmediate.currentMatrix]); + }, + + gluLookAt: (ex, ey, ez, cx, cy, cz, ux, uy, uz) => { + GLImmediate.matricesModified = true; + GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; + GLImmediate.matrixLib.mat4.lookAt(GLImmediate.matrix[GLImmediate.currentMatrix], [ex, ey, ez], + [cx, cy, cz], [ux, uy, uz]); + }, + + gluProject: (objX, objY, objZ, model, proj, view, winX, winY, winZ) => { + // The algorithm for this functions comes from Mesa + + var inVec = new Float32Array(4); + var outVec = new Float32Array(4); + GLImmediate.matrixLib.mat4.multiplyVec4({{{ makeHEAPView('F64', 'model', 'model+' + (16*8)) }}}, + [objX, objY, objZ, 1.0], outVec); + GLImmediate.matrixLib.mat4.multiplyVec4({{{ makeHEAPView('F64', 'proj', 'proj+' + (16*8)) }}}, + outVec, inVec); + if (inVec[3] == 0.0) { + return 0 /* GL_FALSE */; + } + inVec[0] /= inVec[3]; + inVec[1] /= inVec[3]; + inVec[2] /= inVec[3]; + // Map x, y and z to range 0-1 */ + inVec[0] = inVec[0] * 0.5 + 0.5; + inVec[1] = inVec[1] * 0.5 + 0.5; + inVec[2] = inVec[2] * 0.5 + 0.5; + // Map x, y to viewport + inVec[0] = inVec[0] * {{{ makeGetValue('view', 2*4, 'i32') }}} + {{{ makeGetValue('view', 0*4, 'i32') }}}; + inVec[1] = inVec[1] * {{{ makeGetValue('view', 3*4, 'i32') }}} + {{{ makeGetValue('view', 1*4, 'i32') }}}; + + {{{ makeSetValue('winX', '0', 'inVec[0]', 'double') }}}; + {{{ makeSetValue('winY', '0', 'inVec[1]', 'double') }}}; + {{{ makeSetValue('winZ', '0', 'inVec[2]', 'double') }}}; + + return 1 /* GL_TRUE */; + }, + + gluUnProject: (winX, winY, winZ, model, proj, view, objX, objY, objZ) => { + var result = GLImmediate.matrixLib.vec3.unproject([winX, winY, winZ], + {{{ makeHEAPView('F64', 'model', 'model+' + (16*8)) }}}, + {{{ makeHEAPView('F64', 'proj', 'proj+' + (16*8)) }}}, + {{{ makeHEAPView('32', 'view', 'view+' + (4*4)) }}}); + + if (result === null) { + return 0 /* GL_FALSE */; + } + + {{{ makeSetValue('objX', '0', 'result[0]', 'double') }}}; + {{{ makeSetValue('objY', '0', 'result[1]', 'double') }}}; + {{{ makeSetValue('objZ', '0', 'result[2]', 'double') }}}; + + return 1 /* GL_TRUE */; + }, + + gluOrtho2D__deps: ['glOrtho'], + gluOrtho2D: (left, right, bottom, top) => _glOrtho(left, right, bottom, top, -1, 1), +}; + +extraLibraryFuncs.push('$GLEmulation'); + +recordGLProcAddressGet(LibraryGLEmulation); + +addToLibrary(LibraryGLEmulation); diff --git a/src/lib/libglew.js b/src/lib/libglew.js new file mode 100644 index 0000000000000..dc8f9a3947a23 --- /dev/null +++ b/src/lib/libglew.js @@ -0,0 +1,132 @@ +/** + * @license + * Copyright 2014 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +/* + * EMSCRIPTEN GLEW 1.10.0 emulation + * + * What it does: + * - Stubs init function. + * - GL Extensions support. + * + * Optional: + * - isLinaroFork variable to enable glew-es specific error strings. + * This is enabled by default, but should be disabled when upstream glew conflicts. + * + * Authors: + * - Jari Vetoniemi + */ + +var LibraryGLEW = { + $GLEW__deps: ['glGetString', '$stringToNewUTF8', '$UTF8ToString', '$webglGetExtensions'], + $GLEW: { + isLinaroFork: 1, + extensions: null, + + error: { + 0:null, // GLEW_OK || GLEW_NO_ERROR + 1:null, // GLEW_ERROR_NO_GL_VERSION + 2:null, // GLEW_ERROR_GL_VERSION_10_ONLY + 3:null, // GLEW_ERROR_GLX_VERSION_11_ONLY + + 4:null, // GLEW_ERROR_NOT_GLES_VERSION + 5:null, // GLEW_ERROR_GLES_VERSION + 6:null, // GLEW_ERROR_NO_EGL_VERSION + 7:null, // GLEW_ERROR_EGL_VERSION_10_ONLY + + 8:null, // Unknown error + }, + + version: { + 1:null, // GLEW_VERSION + 2:null, // GLEW_VERSION_MAJOR + 3:null, // GLEW_VERSION_MINOR + 4:null, // GLEW_VERSION_MICRO + }, + + 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)); + }, + }, + + glewInit: () => 0, + + glewIsSupported: (name) => { + var exts = UTF8ToString(name).split(' '); + for (var ext of exts) { + if (!GLEW.extensionIsSupported(ext)) return 0; + } + return 1; + }, + + glewGetExtension: (name) => GLEW.extensionIsSupported(UTF8ToString(name)), + + glewGetErrorString: (error) => GLEW.errorString(error), + + glewGetString: (name) => GLEW.versionString(name), + +}; + +autoAddDeps(LibraryGLEW, '$GLEW'); +addToLibrary(LibraryGLEW); diff --git a/src/lib/libglfw.js b/src/lib/libglfw.js new file mode 100644 index 0000000000000..b0769857f4563 --- /dev/null +++ b/src/lib/libglfw.js @@ -0,0 +1,2063 @@ +/** + * @license + * Copyright 2013 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +/* + * EMSCRIPTEN GLFW 2.x-3.x emulation. + * It tries to emulate the behavior described in + * http://www.glfw.org/docs/latest/ + * + * This also implements parts of GLFW 2.x on top of GLFW 3.x. + * + * What it does: + * - Creates a GL context. + * - Manage keyboard and mouse events. + * - GL Extensions support. + * + * What it does not but should probably do: + * - Transmit events when glfwPollEvents, glfwWaitEvents or glfwSwapBuffers is + * called. Events callbacks are called as soon as event are received. + * - Input modes. + * - Gamma ramps. + * - Video modes. + * - Monitors. + * - Clipboard (not possible from javascript?). + * - Multiple windows. + * - Error codes && messages through callback. + * - Thread emulation. (removed in GLFW3). + * - Image/Texture I/O support (removed in GLFW 3). + * + * Authors: + * - Jari Vetoniemi + * - Éloi Rivard + * - Thomas Borsos + */ + +var LibraryGLFW = { + $GLFW_Window__docs: '/** @constructor */', + $GLFW_Window: function(id, width, height, framebufferWidth, framebufferHeight, title, monitor, share) { + this.id = id; + this.x = 0; + this.y = 0; + this.fullscreen = false; // Used to determine if app in fullscreen mode + this.storedX = 0; // Used to store X before fullscreen + this.storedY = 0; // Used to store Y before fullscreen + this.width = width; + this.height = height; + this.framebufferWidth = framebufferWidth; + this.framebufferHeight = framebufferHeight; + this.storedWidth = width; // Used to store width before fullscreen + this.storedHeight = height; // Used to store height before fullscreen + this.title = title; + this.monitor = monitor; + this.share = share; + this.attributes = {...GLFW.hints}; + this.inputModes = { + 0x00033001:0x00034001, // GLFW_CURSOR (GLFW_CURSOR_NORMAL) + 0x00033002:0, // GLFW_STICKY_KEYS + 0x00033003:0, // GLFW_STICKY_MOUSE_BUTTONS + }; + this.buttons = 0; + this.keys = new Array(); + this.domKeys = new Array(); + this.shouldClose = 0; + this.title = null; + this.windowPosFunc = 0; // GLFWwindowposfun + this.windowSizeFunc = 0; // GLFWwindowsizefun + this.windowCloseFunc = 0; // GLFWwindowclosefun + this.windowRefreshFunc = 0; // GLFWwindowrefreshfun + this.windowFocusFunc = 0; // GLFWwindowfocusfun + this.windowIconifyFunc = 0; // GLFWwindowiconifyfun + this.windowMaximizeFunc = 0; // GLFWwindowmaximizefun + this.framebufferSizeFunc = 0; // GLFWframebuffersizefun + this.windowContentScaleFunc = 0; // GLFWwindowcontentscalefun + this.mouseButtonFunc = 0; // GLFWmousebuttonfun + this.cursorPosFunc = 0; // GLFWcursorposfun + this.cursorEnterFunc = 0; // GLFWcursorenterfun + this.scrollFunc = 0; // GLFWscrollfun + this.dropFunc = 0; // GLFWdropfun + this.keyFunc = 0; // GLFWkeyfun + this.charFunc = 0; // GLFWcharfun + this.userptr = 0; + }, + + $GLFW__deps: ['emscripten_get_now', '$GL', '$Browser', '$GLFW_Window', + 'malloc', 'free', + '$MainLoop', + '$stringToNewUTF8', + '$getFullscreenElement', + 'emscripten_set_window_title', +#if FILESYSTEM + '$FS', +#endif + ], + $GLFW: { + WindowFromId: (id) => { + if (id <= 0 || !GLFW.windows) return null; + return GLFW.windows[id - 1]; + }, + + joystickFunc: 0, // GLFWjoystickfun + errorFunc: 0, // GLFWerrorfun + monitorFunc: 0, // GLFWmonitorfun + active: null, // active window + scale: null, + windows: null, + monitors: null, + monitorString: null, + versionString: null, + initialTime: null, + extensions: null, + devicePixelRatioMQL: null, // MediaQueryList from window.matchMedia + hints: null, + primaryTouchId: null, + defaultHints: { + 0x00020001:0, // GLFW_FOCUSED + 0x00020002:0, // GLFW_ICONIFIED + 0x00020003:1, // GLFW_RESIZABLE + 0x00020004:1, // GLFW_VISIBLE + 0x00020005:1, // GLFW_DECORATED + 0x0002000A:0, // GLFW_TRANSPARENT_FRAMEBUFFER + 0x0002200C:0, // GLFW_SCALE_TO_MONITOR + + 0x00021001:8, // GLFW_RED_BITS + 0x00021002:8, // GLFW_GREEN_BITS + 0x00021003:8, // GLFW_BLUE_BITS + 0x00021004:8, // GLFW_ALPHA_BITS + 0x00021005:24, // GLFW_DEPTH_BITS + 0x00021006:8, // GLFW_STENCIL_BITS + 0x00021007:0, // GLFW_ACCUM_RED_BITS + 0x00021008:0, // GLFW_ACCUM_GREEN_BITS + 0x00021009:0, // GLFW_ACCUM_BLUE_BITS + 0x0002100A:0, // GLFW_ACCUM_ALPHA_BITS + 0x0002100B:0, // GLFW_AUX_BUFFERS + 0x0002100C:0, // GLFW_STEREO + 0x0002100D:0, // GLFW_SAMPLES + 0x0002100E:0, // GLFW_SRGB_CAPABLE + 0x0002100F:0, // GLFW_REFRESH_RATE + + 0x00022001:0x00030001, // GLFW_CLIENT_API (GLFW_OPENGL_API) + 0x00022002:1, // GLFW_CONTEXT_VERSION_MAJOR + 0x00022003:0, // GLFW_CONTEXT_VERSION_MINOR + 0x00022004:0, // GLFW_CONTEXT_REVISION + 0x00022005:0, // GLFW_CONTEXT_ROBUSTNESS + 0x00022006:0, // GLFW_OPENGL_FORWARD_COMPAT + 0x00022007:0, // GLFW_OPENGL_DEBUG_CONTEXT + 0x00022008:0, // GLFW_OPENGL_PROFILE + }, + +/******************************************************************************* + * DOM EVENT CALLBACKS + ******************************************************************************/ + + /* https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent and GLFW/glfw3.h */ + DOMToGLFWKeyCode: (keycode) => { + switch (keycode) { + // these keycodes are only defined for GLFW3, assume they are the same for GLFW2 + case 0x20:return 32; // DOM_VK_SPACE -> GLFW_KEY_SPACE + case 0xDE:return 39; // DOM_VK_QUOTE -> GLFW_KEY_APOSTROPHE + case 0xBC:return 44; // DOM_VK_COMMA -> GLFW_KEY_COMMA + case 0xAD:return 45; // DOM_VK_HYPHEN_MINUS -> GLFW_KEY_MINUS + case 0xBD:return 45; // DOM_VK_MINUS -> GLFW_KEY_MINUS + case 0xBE:return 46; // DOM_VK_PERIOD -> GLFW_KEY_PERIOD + case 0xBF:return 47; // DOM_VK_SLASH -> GLFW_KEY_SLASH + case 0x30:return 48; // DOM_VK_0 -> GLFW_KEY_0 + case 0x31:return 49; // DOM_VK_1 -> GLFW_KEY_1 + case 0x32:return 50; // DOM_VK_2 -> GLFW_KEY_2 + case 0x33:return 51; // DOM_VK_3 -> GLFW_KEY_3 + case 0x34:return 52; // DOM_VK_4 -> GLFW_KEY_4 + case 0x35:return 53; // DOM_VK_5 -> GLFW_KEY_5 + case 0x36:return 54; // DOM_VK_6 -> GLFW_KEY_6 + case 0x37:return 55; // DOM_VK_7 -> GLFW_KEY_7 + case 0x38:return 56; // DOM_VK_8 -> GLFW_KEY_8 + case 0x39:return 57; // DOM_VK_9 -> GLFW_KEY_9 + case 0x3B:return 59; // DOM_VK_SEMICOLON -> GLFW_KEY_SEMICOLON + case 0x3D:return 61; // DOM_VK_EQUALS -> GLFW_KEY_EQUAL + case 0xBB:return 61; // DOM_VK_EQUALS -> GLFW_KEY_EQUAL + case 0x41:return 65; // DOM_VK_A -> GLFW_KEY_A + case 0x42:return 66; // DOM_VK_B -> GLFW_KEY_B + case 0x43:return 67; // DOM_VK_C -> GLFW_KEY_C + case 0x44:return 68; // DOM_VK_D -> GLFW_KEY_D + case 0x45:return 69; // DOM_VK_E -> GLFW_KEY_E + case 0x46:return 70; // DOM_VK_F -> GLFW_KEY_F + case 0x47:return 71; // DOM_VK_G -> GLFW_KEY_G + case 0x48:return 72; // DOM_VK_H -> GLFW_KEY_H + case 0x49:return 73; // DOM_VK_I -> GLFW_KEY_I + case 0x4A:return 74; // DOM_VK_J -> GLFW_KEY_J + case 0x4B:return 75; // DOM_VK_K -> GLFW_KEY_K + case 0x4C:return 76; // DOM_VK_L -> GLFW_KEY_L + case 0x4D:return 77; // DOM_VK_M -> GLFW_KEY_M + case 0x4E:return 78; // DOM_VK_N -> GLFW_KEY_N + case 0x4F:return 79; // DOM_VK_O -> GLFW_KEY_O + case 0x50:return 80; // DOM_VK_P -> GLFW_KEY_P + case 0x51:return 81; // DOM_VK_Q -> GLFW_KEY_Q + case 0x52:return 82; // DOM_VK_R -> GLFW_KEY_R + case 0x53:return 83; // DOM_VK_S -> GLFW_KEY_S + case 0x54:return 84; // DOM_VK_T -> GLFW_KEY_T + case 0x55:return 85; // DOM_VK_U -> GLFW_KEY_U + case 0x56:return 86; // DOM_VK_V -> GLFW_KEY_V + case 0x57:return 87; // DOM_VK_W -> GLFW_KEY_W + case 0x58:return 88; // DOM_VK_X -> GLFW_KEY_X + case 0x59:return 89; // DOM_VK_Y -> GLFW_KEY_Y + case 0x5a:return 90; // DOM_VK_Z -> GLFW_KEY_Z + case 0xDB:return 91; // DOM_VK_OPEN_BRACKET -> GLFW_KEY_LEFT_BRACKET + case 0xDC:return 92; // DOM_VK_BACKSLASH -> GLFW_KEY_BACKSLASH + case 0xDD:return 93; // DOM_VK_CLOSE_BRACKET -> GLFW_KEY_RIGHT_BRACKET + case 0xC0:return 96; // DOM_VK_BACK_QUOTE -> GLFW_KEY_GRAVE_ACCENT + +#if USE_GLFW == 2 + //#define GLFW_KEY_SPECIAL 256 + case 0x1B:return (256+1); // DOM_VK_ESCAPE -> GLFW_KEY_ESC + case 0x70:return (256+2); // DOM_VK_F1 -> GLFW_KEY_F1 + case 0x71:return (256+3); // DOM_VK_F2 -> GLFW_KEY_F2 + case 0x72:return (256+4); // DOM_VK_F3 -> GLFW_KEY_F3 + case 0x73:return (256+5); // DOM_VK_F4 -> GLFW_KEY_F4 + case 0x74:return (256+6); // DOM_VK_F5 -> GLFW_KEY_F5 + case 0x75:return (256+7); // DOM_VK_F6 -> GLFW_KEY_F6 + case 0x76:return (256+8); // DOM_VK_F7 -> GLFW_KEY_F7 + case 0x77:return (256+9); // DOM_VK_F8 -> GLFW_KEY_F8 + case 0x78:return (256+10); // DOM_VK_F9 -> GLFW_KEY_F9 + case 0x79:return (256+11); // DOM_VK_F10 -> GLFW_KEY_F10 + case 0x7A:return (256+12); // DOM_VK_F11 -> GLFW_KEY_F11 + case 0x7B:return (256+13); // DOM_VK_F12 -> GLFW_KEY_F12 + case 0x7C:return (256+14); // DOM_VK_F13 -> GLFW_KEY_F13 + case 0x7D:return (256+15); // DOM_VK_F14 -> GLFW_KEY_F14 + case 0x7E:return (256+16); // DOM_VK_F15 -> GLFW_KEY_F15 + case 0x7F:return (256+17); // DOM_VK_F16 -> GLFW_KEY_F16 + case 0x80:return (256+18); // DOM_VK_F17 -> GLFW_KEY_F17 + case 0x81:return (256+19); // DOM_VK_F18 -> GLFW_KEY_F18 + case 0x82:return (256+20); // DOM_VK_F19 -> GLFW_KEY_F19 + case 0x83:return (256+21); // DOM_VK_F20 -> GLFW_KEY_F20 + case 0x84:return (256+22); // DOM_VK_F21 -> GLFW_KEY_F21 + case 0x85:return (256+23); // DOM_VK_F22 -> GLFW_KEY_F22 + case 0x86:return (256+24); // DOM_VK_F23 -> GLFW_KEY_F23 + case 0x87:return (256+25); // DOM_VK_F24 -> GLFW_KEY_F24 + case 0x88:return (256+26); // 0x88 (not used?) -> GLFW_KEY_F25 + case 0x27:return (256+30); // DOM_VK_RIGHT -> GLFW_KEY_RIGHT + case 0x25:return (256+29); // DOM_VK_LEFT -> GLFW_KEY_LEFT + case 0x28:return (256+28); // DOM_VK_DOWN -> GLFW_KEY_DOWN + case 0x26:return (256+27); // DOM_VK_UP -> GLFW_KEY_UP + case 0x10:return (256+31); // DOM_VK_SHIFT -> GLFW_KEY_LSHIFT + // #define GLFW_KEY_RSHIFT (GLFW_KEY_SPECIAL+32) + case 0x11:return (256+33); // DOM_VK_CONTROL -> GLFW_KEY_LCTRL + // #define GLFW_KEY_RCTRL (GLFW_KEY_SPECIAL+34) + case 0x12:return (256+35); // DOM_VK_ALT -> GLFW_KEY_LALT + // #define GLFW_KEY_RALT (GLFW_KEY_SPECIAL+36) + case 0x09:return (256+37); // DOM_VK_TAB -> GLFW_KEY_TAB + case 0x0D:return (256+38); // DOM_VK_RETURN -> GLFW_KEY_ENTER + case 0x08:return (256+39); // DOM_VK_BACK -> GLFW_KEY_BACKSPACE + case 0x2D:return (256+40); // DOM_VK_INSERT -> GLFW_KEY_INSERT + case 0x2E:return (256+41); // DOM_VK_DELETE -> GLFW_KEY_DEL + case 0x21:return (256+42); // DOM_VK_PAGE_UP -> GLFW_KEY_PAGEUP + case 0x22:return (256+43); // DOM_VK_PAGE_DOWN -> GLFW_KEY_PAGEDOWN + case 0x24:return (256+44); // DOM_VK_HOME -> GLFW_KEY_HOME + case 0x23:return (256+45); // DOM_VK_END -> GLFW_KEY_END + case 0x60:return (256+46); // DOM_VK_NUMPAD0 -> GLFW_KEY_KP_0 + case 0x61:return (256+47); // DOM_VK_NUMPAD1 -> GLFW_KEY_KP_1 + case 0x62:return (256+48); // DOM_VK_NUMPAD2 -> GLFW_KEY_KP_2 + case 0x63:return (256+49); // DOM_VK_NUMPAD3 -> GLFW_KEY_KP_3 + case 0x64:return (256+50); // DOM_VK_NUMPAD4 -> GLFW_KEY_KP_4 + case 0x65:return (256+51); // DOM_VK_NUMPAD5 -> GLFW_KEY_KP_5 + case 0x66:return (256+52); // DOM_VK_NUMPAD6 -> GLFW_KEY_KP_6 + case 0x67:return (256+53); // DOM_VK_NUMPAD7 -> GLFW_KEY_KP_7 + case 0x68:return (256+54); // DOM_VK_NUMPAD8 -> GLFW_KEY_KP_8 + case 0x69:return (256+55); // DOM_VK_NUMPAD9 -> GLFW_KEY_KP_9 + case 0x6F:return (256+56); // DOM_VK_DIVIDE -> GLFW_KEY_KP_DIVIDE + case 0x6A:return (256+57); // DOM_VK_MULTIPLY -> GLFW_KEY_KP_MULTIPLY + case 0x6D:return (256+58); // DOM_VK_SUBTRACT -> GLFW_KEY_KP_SUBTRACT + case 0x6B:return (256+59); // DOM_VK_ADD -> GLFW_KEY_KP_ADD + case 0x6E:return (256+60); // DOM_VK_DECIMAL -> GLFW_KEY_KP_DECIMAL + // #define GLFW_KEY_KP_EQUAL (GLFW_KEY_SPECIAL+61) + // #define GLFW_KEY_KP_ENTER (GLFW_KEY_SPECIAL+62) + case 0x90:return (256+63); // DOM_VK_NUM_LOCK -> GLFW_KEY_KP_NUM_LOCK + case 0x14:return (256+64); // DOM_VK_CAPS_LOCK -> GLFW_KEY_CAPS_LOCK + case 0x91:return (256+65); // DOM_VK_SCROLL_LOCK -> GLFW_KEY_SCROLL_LOCK + case 0x13:return (256+66); // DOM_VK_PAUSE -> GLFW_KEY_PAUSE + case 0x5B:return (256+67); // DOM_VK_WIN -> GLFW_KEY_LSUPER + // #define GLFW_KEY_RSUPER (GLFW_KEY_SPECIAL+68) + case 0x5D:return (256+69); // DOM_VK_CONTEXT_MENU -> GLFW_KEY_MENU +#endif + +#if USE_GLFW == 3 + case 0x1B:return 256; // DOM_VK_ESCAPE -> GLFW_KEY_ESCAPE + case 0x0D:return 257; // DOM_VK_RETURN -> GLFW_KEY_ENTER + case 0x09:return 258; // DOM_VK_TAB -> GLFW_KEY_TAB + case 0x08:return 259; // DOM_VK_BACK -> GLFW_KEY_BACKSPACE + case 0x2D:return 260; // DOM_VK_INSERT -> GLFW_KEY_INSERT + case 0x2E:return 261; // DOM_VK_DELETE -> GLFW_KEY_DELETE + case 0x27:return 262; // DOM_VK_RIGHT -> GLFW_KEY_RIGHT + case 0x25:return 263; // DOM_VK_LEFT -> GLFW_KEY_LEFT + case 0x28:return 264; // DOM_VK_DOWN -> GLFW_KEY_DOWN + case 0x26:return 265; // DOM_VK_UP -> GLFW_KEY_UP + case 0x21:return 266; // DOM_VK_PAGE_UP -> GLFW_KEY_PAGE_UP + case 0x22:return 267; // DOM_VK_PAGE_DOWN -> GLFW_KEY_PAGE_DOWN + case 0x24:return 268; // DOM_VK_HOME -> GLFW_KEY_HOME + case 0x23:return 269; // DOM_VK_END -> GLFW_KEY_END + case 0x14:return 280; // DOM_VK_CAPS_LOCK -> GLFW_KEY_CAPS_LOCK + case 0x91:return 281; // DOM_VK_SCROLL_LOCK -> GLFW_KEY_SCROLL_LOCK + case 0x90:return 282; // DOM_VK_NUM_LOCK -> GLFW_KEY_NUM_LOCK + case 0x2C:return 283; // DOM_VK_SNAPSHOT -> GLFW_KEY_PRINT_SCREEN + case 0x13:return 284; // DOM_VK_PAUSE -> GLFW_KEY_PAUSE + case 0x70:return 290; // DOM_VK_F1 -> GLFW_KEY_F1 + case 0x71:return 291; // DOM_VK_F2 -> GLFW_KEY_F2 + case 0x72:return 292; // DOM_VK_F3 -> GLFW_KEY_F3 + case 0x73:return 293; // DOM_VK_F4 -> GLFW_KEY_F4 + case 0x74:return 294; // DOM_VK_F5 -> GLFW_KEY_F5 + case 0x75:return 295; // DOM_VK_F6 -> GLFW_KEY_F6 + case 0x76:return 296; // DOM_VK_F7 -> GLFW_KEY_F7 + case 0x77:return 297; // DOM_VK_F8 -> GLFW_KEY_F8 + case 0x78:return 298; // DOM_VK_F9 -> GLFW_KEY_F9 + case 0x79:return 299; // DOM_VK_F10 -> GLFW_KEY_F10 + case 0x7A:return 300; // DOM_VK_F11 -> GLFW_KEY_F11 + case 0x7B:return 301; // DOM_VK_F12 -> GLFW_KEY_F12 + case 0x7C:return 302; // DOM_VK_F13 -> GLFW_KEY_F13 + case 0x7D:return 303; // DOM_VK_F14 -> GLFW_KEY_F14 + case 0x7E:return 304; // DOM_VK_F15 -> GLFW_KEY_F15 + case 0x7F:return 305; // DOM_VK_F16 -> GLFW_KEY_F16 + case 0x80:return 306; // DOM_VK_F17 -> GLFW_KEY_F17 + case 0x81:return 307; // DOM_VK_F18 -> GLFW_KEY_F18 + case 0x82:return 308; // DOM_VK_F19 -> GLFW_KEY_F19 + case 0x83:return 309; // DOM_VK_F20 -> GLFW_KEY_F20 + case 0x84:return 310; // DOM_VK_F21 -> GLFW_KEY_F21 + case 0x85:return 311; // DOM_VK_F22 -> GLFW_KEY_F22 + case 0x86:return 312; // DOM_VK_F23 -> GLFW_KEY_F23 + case 0x87:return 313; // DOM_VK_F24 -> GLFW_KEY_F24 + case 0x88:return 314; // 0x88 (not used?) -> GLFW_KEY_F25 + case 0x60:return 320; // DOM_VK_NUMPAD0 -> GLFW_KEY_KP_0 + case 0x61:return 321; // DOM_VK_NUMPAD1 -> GLFW_KEY_KP_1 + case 0x62:return 322; // DOM_VK_NUMPAD2 -> GLFW_KEY_KP_2 + case 0x63:return 323; // DOM_VK_NUMPAD3 -> GLFW_KEY_KP_3 + case 0x64:return 324; // DOM_VK_NUMPAD4 -> GLFW_KEY_KP_4 + case 0x65:return 325; // DOM_VK_NUMPAD5 -> GLFW_KEY_KP_5 + case 0x66:return 326; // DOM_VK_NUMPAD6 -> GLFW_KEY_KP_6 + case 0x67:return 327; // DOM_VK_NUMPAD7 -> GLFW_KEY_KP_7 + case 0x68:return 328; // DOM_VK_NUMPAD8 -> GLFW_KEY_KP_8 + case 0x69:return 329; // DOM_VK_NUMPAD9 -> GLFW_KEY_KP_9 + case 0x6E:return 330; // DOM_VK_DECIMAL -> GLFW_KEY_KP_DECIMAL + case 0x6F:return 331; // DOM_VK_DIVIDE -> GLFW_KEY_KP_DIVIDE + case 0x6A:return 332; // DOM_VK_MULTIPLY -> GLFW_KEY_KP_MULTIPLY + case 0x6D:return 333; // DOM_VK_SUBTRACT -> GLFW_KEY_KP_SUBTRACT + case 0x6B:return 334; // DOM_VK_ADD -> GLFW_KEY_KP_ADD + // case 0x0D:return 335; // DOM_VK_RETURN -> GLFW_KEY_KP_ENTER (DOM_KEY_LOCATION_RIGHT) + // case 0x61:return 336; // DOM_VK_EQUALS -> GLFW_KEY_KP_EQUAL (DOM_KEY_LOCATION_RIGHT) + case 0x10:return 340; // DOM_VK_SHIFT -> GLFW_KEY_LEFT_SHIFT + case 0x11:return 341; // DOM_VK_CONTROL -> GLFW_KEY_LEFT_CONTROL + case 0x12:return 342; // DOM_VK_ALT -> GLFW_KEY_LEFT_ALT + case 0x5B:return 343; // DOM_VK_WIN -> GLFW_KEY_LEFT_SUPER + case 0xE0:return 343; // DOM_VK_META -> GLFW_KEY_LEFT_SUPER + // case 0x10:return 344; // DOM_VK_SHIFT -> GLFW_KEY_RIGHT_SHIFT (DOM_KEY_LOCATION_RIGHT) + // case 0x11:return 345; // DOM_VK_CONTROL -> GLFW_KEY_RIGHT_CONTROL (DOM_KEY_LOCATION_RIGHT) + // case 0x12:return 346; // DOM_VK_ALT -> GLFW_KEY_RIGHT_ALT (DOM_KEY_LOCATION_RIGHT) + // case 0x5B:return 347; // DOM_VK_WIN -> GLFW_KEY_RIGHT_SUPER (DOM_KEY_LOCATION_RIGHT) + case 0x5D:return 348; // DOM_VK_CONTEXT_MENU -> GLFW_KEY_MENU + // XXX: GLFW_KEY_WORLD_1, GLFW_KEY_WORLD_2 what are these? +#endif + default:return -1; // GLFW_KEY_UNKNOWN + }; + }, + + getModBits: (win) => { + var mod = 0; + if (win.keys[340]) mod |= 0x0001; // GLFW_MOD_SHIFT + if (win.keys[341]) mod |= 0x0002; // GLFW_MOD_CONTROL + if (win.keys[342]) mod |= 0x0004; // GLFW_MOD_ALT + if (win.keys[343] || win.keys[348]) mod |= 0x0008; // GLFW_MOD_SUPER + // add caps and num lock keys? only if lock_key_mod is set + return mod; + }, + + onKeyPress: (event) => { + if (!GLFW.active || !GLFW.active.charFunc) return; + if (event.ctrlKey || event.metaKey) return; + + // correct unicode charCode is only available with onKeyPress event + var charCode = event.charCode; + if (charCode == 0 || (charCode >= 0x00 && charCode <= 0x1F)) return; + +#if USE_GLFW == 2 + {{{ makeDynCall('vii', 'GLFW.active.charFunc') }}}(charCode, 1); +#endif +#if USE_GLFW == 3 + {{{ makeDynCall('vpi', 'GLFW.active.charFunc') }}}(GLFW.active.id, charCode); +#endif + }, + + onKeyChanged: (keyCode, status) => { + if (!GLFW.active) return; + + var key = GLFW.DOMToGLFWKeyCode(keyCode); + if (key == -1) return; + +#if USE_GLFW == 3 + var repeat = status && GLFW.active.keys[key]; +#endif + GLFW.active.keys[key] = status; + GLFW.active.domKeys[keyCode] = status; + + if (GLFW.active.keyFunc) { +#if USE_GLFW == 2 + {{{ makeDynCall('vii', 'GLFW.active.keyFunc') }}}(key, status); +#endif +#if USE_GLFW == 3 + if (repeat) status = 2; // GLFW_REPEAT + {{{ makeDynCall('vpiiii', 'GLFW.active.keyFunc') }}}(GLFW.active.id, key, keyCode, status, GLFW.getModBits(GLFW.active)); +#endif + } + }, + + onGamepadConnected: (event) => { + GLFW.refreshJoysticks(); + }, + + onGamepadDisconnected: (event) => { + GLFW.refreshJoysticks(); + }, + + onKeydown: (event) => { + GLFW.onKeyChanged(event.keyCode, 1); // GLFW_PRESS or GLFW_REPEAT + + // This logic comes directly from the sdl implementation. We cannot + // call preventDefault on all keydown events otherwise onKeyPress will + // not get called + if (event.key == 'Backspace' || event.key == 'Tab') { + event.preventDefault(); + } + }, + + onKeyup: (event) => { + GLFW.onKeyChanged(event.keyCode, 0); // GLFW_RELEASE + }, + + onBlur: (event) => { + if (!GLFW.active) return; + + for (var i = 0; i < GLFW.active.domKeys.length; ++i) { + if (GLFW.active.domKeys[i]) { + GLFW.onKeyChanged(i, 0); // GLFW_RELEASE + } + } + }, + + onMousemove: (event) => { + if (!GLFW.active) return; + + if (event.type === 'touchmove') { + // Handling for touch events that are being converted to mouse input. + + // Don't let the browser fire a duplicate mouse event. + event.preventDefault(); + + let primaryChanged = false; + for (let i of event.changedTouches) { + // If our chosen primary touch moved, update Browser mouse coords + if (GLFW.primaryTouchId === i.identifier) { + Browser.setMouseCoords(i.pageX, i.pageY); + primaryChanged = true; + break; + } + } + + if (!primaryChanged) { + // Do not send mouse events if some touch other than the primary triggered this. + return; + } + + } else { + // Handling for non-touch mouse input events. + Browser.calculateMouseEvent(event); + } + + if (event.target != Browser.getCanvas() || !GLFW.active.cursorPosFunc) return; + + if (GLFW.active.cursorPosFunc) { +#if USE_GLFW == 2 + {{{ makeDynCall('vii', 'GLFW.active.cursorPosFunc') }}}(Browser.mouseX, Browser.mouseY); +#endif +#if USE_GLFW == 3 + {{{ makeDynCall('vpdd', 'GLFW.active.cursorPosFunc') }}}(GLFW.active.id, Browser.mouseX, Browser.mouseY); +#endif + } + }, + + DOMToGLFWMouseButton: (event) => { + // DOM and glfw have different button codes. + // See http://www.w3schools.com/jsref/event_button.asp. + var eventButton = event['button']; + if (eventButton > 0) { + if (eventButton == 1) { + eventButton = 2; + } else { + eventButton = 1; + } + } + return eventButton; + }, + + onMouseenter: (event) => { + if (!GLFW.active) return; + + if (event.target != Browser.getCanvas()) return; + +#if USE_GLFW == 3 + if (GLFW.active.cursorEnterFunc) { + {{{ makeDynCall('vpi', 'GLFW.active.cursorEnterFunc') }}}(GLFW.active.id, 1); + } +#endif + }, + + onMouseleave: (event) => { + if (!GLFW.active) return; + + if (event.target != Browser.getCanvas()) return; + +#if USE_GLFW == 3 + if (GLFW.active.cursorEnterFunc) { + {{{ makeDynCall('vpi', 'GLFW.active.cursorEnterFunc') }}}(GLFW.active.id, 0); + } +#endif + }, + + onMouseButtonChanged: (event, status) => { + if (!GLFW.active) return; + + if (event.target != Browser.getCanvas()) return; + + // Is this from a touch event? + const isTouchType = event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchcancel'; + + // Only emulating mouse left-click behavior for touches. + let eventButton = 0; + if (isTouchType) { + // Handling for touch events that are being converted to mouse input. + + // Don't let the browser fire a duplicate mouse event. + event.preventDefault(); + + let primaryChanged = false; + + // Set a primary touch if we have none. + if (GLFW.primaryTouchId === null && event.type === 'touchstart' && event.targetTouches.length > 0) { + // Pick the first touch that started in the canvas and treat it as primary. + const chosenTouch = event.targetTouches[0]; + GLFW.primaryTouchId = chosenTouch.identifier; + + Browser.setMouseCoords(chosenTouch.pageX, chosenTouch.pageY); + primaryChanged = true; + } else if (event.type === 'touchend' || event.type === 'touchcancel') { + // Clear the primary touch if it ended. + for (let i of event.changedTouches) { + // If our chosen primary touch ended, remove it. + if (GLFW.primaryTouchId === i.identifier) { + GLFW.primaryTouchId = null; + primaryChanged = true; + break; + } + } + } + + if (!primaryChanged) { + // Do not send mouse events if some touch other than the primary triggered this. + return; + } + + } else { + // Handling for non-touch mouse input events. + Browser.calculateMouseEvent(event); + eventButton = GLFW.DOMToGLFWMouseButton(event); + } + + if (status == 1) { // GLFW_PRESS + GLFW.active.buttons |= (1 << eventButton); + try { + event.target.setCapture(); + } catch (e) {} + } else { // GLFW_RELEASE + GLFW.active.buttons &= ~(1 << eventButton); + } + + // Send mouse event to GLFW. + if (GLFW.active.mouseButtonFunc) { +#if USE_GLFW == 2 + {{{ makeDynCall('vii', 'GLFW.active.mouseButtonFunc') }}}(eventButton, status); +#endif +#if USE_GLFW == 3 + {{{ makeDynCall('vpiii', 'GLFW.active.mouseButtonFunc') }}}(GLFW.active.id, eventButton, status, GLFW.getModBits(GLFW.active)); +#endif + } + }, + + onMouseButtonDown: (event) => { + if (!GLFW.active) return; + GLFW.onMouseButtonChanged(event, 1); // GLFW_PRESS + }, + + onMouseButtonUp: (event) => { + if (!GLFW.active) return; + GLFW.onMouseButtonChanged(event, 0); // GLFW_RELEASE + }, + + onMouseWheel: (event) => { + // 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. + GLFW.wheelPos += delta; + + if (!GLFW.active || !GLFW.active.scrollFunc || event.target != Browser.getCanvas()) return; +#if USE_GLFW == 2 + {{{ makeDynCall('vi', 'GLFW.active.scrollFunc') }}}(GLFW.wheelPos); +#endif +#if USE_GLFW == 3 + var sx = 0; + var sy = delta; + if (event.type == 'mousewheel') { + sx = event.wheelDeltaX; + } else { + sx = event.deltaX; + } + + {{{ makeDynCall('vpdd', 'GLFW.active.scrollFunc') }}}(GLFW.active.id, sx, sy); +#endif + + event.preventDefault(); + }, + + // width/height are the dimensions in screen coordinates the user interact with (ex: drawing, mouse coordinates...) + // framebufferWidth/framebufferHeight are the dimensions in pixel coordinates used for rendering + // in a HiDPI scenario framebufferWidth = devicePixelRatio * width + onCanvasResize: (width, height, framebufferWidth, framebufferHeight) => { + if (!GLFW.active) return; + + var resizeNeeded = false; + + // If the client is requesting fullscreen mode + if (getFullscreenElement()) { + if (!GLFW.active.fullscreen) { + resizeNeeded = width != screen.width || height != screen.height; + GLFW.active.storedX = GLFW.active.x; + GLFW.active.storedY = GLFW.active.y; + GLFW.active.storedWidth = GLFW.active.width; + GLFW.active.storedHeight = GLFW.active.height; + GLFW.active.x = GLFW.active.y = 0; + GLFW.active.width = screen.width; + GLFW.active.height = screen.height; + GLFW.active.fullscreen = true; + } + // If the client is reverting from fullscreen mode + } else if (GLFW.active.fullscreen == true) { + resizeNeeded = width != GLFW.active.storedWidth || height != GLFW.active.storedHeight; + GLFW.active.x = GLFW.active.storedX; + GLFW.active.y = GLFW.active.storedY; + GLFW.active.width = GLFW.active.storedWidth; + GLFW.active.height = GLFW.active.storedHeight; + GLFW.active.fullscreen = false; + } + + if (resizeNeeded) { + // width or height is changed (fullscreen / exit fullscreen) which will call this listener back + // with proper framebufferWidth/framebufferHeight + Browser.setCanvasSize(GLFW.active.width, GLFW.active.height); + } else if (GLFW.active.width != width || + GLFW.active.height != height || + GLFW.active.framebufferWidth != framebufferWidth || + GLFW.active.framebufferHeight != framebufferHeight) { + GLFW.active.width = width; + GLFW.active.height = height; + GLFW.active.framebufferWidth = framebufferWidth; + GLFW.active.framebufferHeight = framebufferHeight; + GLFW.onWindowSizeChanged(); + GLFW.onFramebufferSizeChanged(); + } + }, + + onWindowSizeChanged: () => { + if (!GLFW.active) return; + + if (GLFW.active.windowSizeFunc) { +#if USE_GLFW == 2 + {{{ makeDynCall('vii', 'GLFW.active.windowSizeFunc') }}}(GLFW.active.width, GLFW.active.height); +#endif +#if USE_GLFW == 3 + {{{ makeDynCall('vpii', 'GLFW.active.windowSizeFunc') }}}(GLFW.active.id, GLFW.active.width, GLFW.active.height); +#endif + } + }, + + onFramebufferSizeChanged: () => { + if (!GLFW.active) return; + +#if USE_GLFW == 3 + if (GLFW.active.framebufferSizeFunc) { + {{{ makeDynCall('vpii', 'GLFW.active.framebufferSizeFunc') }}}(GLFW.active.id, GLFW.active.framebufferWidth, GLFW.active.framebufferHeight); + } +#endif + }, + + onWindowContentScaleChanged: (scale) => { + GLFW.scale = scale; + if (!GLFW.active) return; + +#if USE_GLFW == 3 + if (GLFW.active.windowContentScaleFunc) { + {{{ makeDynCall('vpff', 'GLFW.active.windowContentScaleFunc') }}}(GLFW.active.id, GLFW.scale, GLFW.scale); + } +#endif + }, + + getTime: () => _emscripten_get_now() / 1000, + + /* GLFW2 wrapping */ + + setWindowTitle: (winid, title) => { + var win = GLFW.WindowFromId(winid); + if (!win) return; + + win.title = title; + if (GLFW.active.id == win.id) { + _emscripten_set_window_title(title); + } + }, + + setJoystickCallback: (cbfun) => { + var prevcbfun = GLFW.joystickFunc; + GLFW.joystickFunc = cbfun; + GLFW.refreshJoysticks(); + return prevcbfun; + }, + + joys: {}, // glfw joystick data + lastGamepadState: [], + lastGamepadStateFrame: null, // The integer value of MainLoop.currentFrameNumber of when the last gamepad state was produced. + + refreshJoysticks: () => { + // Produce a new Gamepad API sample if we are ticking a new game frame, or if not using emscripten_set_main_loop() at all to drive animation. + if (MainLoop.currentFrameNumber !== GLFW.lastGamepadStateFrame || !MainLoop.currentFrameNumber) { + GLFW.lastGamepadState = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads || []); + GLFW.lastGamepadStateFrame = MainLoop.currentFrameNumber; + + for (var joy = 0; joy < GLFW.lastGamepadState.length; ++joy) { + var gamepad = GLFW.lastGamepadState[joy]; + + if (gamepad) { + if (!GLFW.joys[joy]) { + out('glfw joystick connected:',joy); + GLFW.joys[joy] = { + id: stringToNewUTF8(gamepad.id), + buttonsCount: gamepad.buttons.length, + axesCount: gamepad.axes.length, + buttons: _malloc(gamepad.buttons.length), + axes: _malloc(gamepad.axes.length*4), + }; + + if (GLFW.joystickFunc) { + {{{ makeDynCall('vii', 'GLFW.joystickFunc') }}}(joy, 0x00040001); // GLFW_CONNECTED + } + } + + var data = GLFW.joys[joy]; + + for (var i = 0; i < gamepad.buttons.length; ++i) { + {{{ makeSetValue('data.buttons + i', '0', 'gamepad.buttons[i].pressed', 'i8') }}}; + } + + for (var i = 0; i < gamepad.axes.length; ++i) { + {{{ makeSetValue('data.axes + i*4', '0', 'gamepad.axes[i]', 'float') }}}; + } + } else { + if (GLFW.joys[joy]) { + out('glfw joystick disconnected',joy); + + if (GLFW.joystickFunc) { + {{{ makeDynCall('vii', 'GLFW.joystickFunc') }}}(joy, 0x00040002); // GLFW_DISCONNECTED + } + + _free(GLFW.joys[joy].id); + _free(GLFW.joys[joy].buttons); + _free(GLFW.joys[joy].axes); + + delete GLFW.joys[joy]; + } + } + } + } + }, + + setKeyCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.keyFunc; + win.keyFunc = cbfun; + return prevcbfun; + }, + + setCharCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.charFunc; + win.charFunc = cbfun; + return prevcbfun; + }, + + setMouseButtonCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.mouseButtonFunc; + win.mouseButtonFunc = cbfun; + return prevcbfun; + }, + + setCursorPosCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.cursorPosFunc; + win.cursorPosFunc = cbfun; + return prevcbfun; + }, + + setScrollCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.scrollFunc; + win.scrollFunc = cbfun; + return prevcbfun; + }, + + setDropCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.dropFunc; + win.dropFunc = cbfun; + return prevcbfun; + }, + + onDrop: (event) => { + if (!GLFW.active || !GLFW.active.dropFunc) return; + if (!event.dataTransfer || !event.dataTransfer.files || event.dataTransfer.files.length == 0) return; + + event.preventDefault(); + +#if FILESYSTEM + var filenames = _malloc(event.dataTransfer.files.length * {{{ POINTER_SIZE }}}); + var filenamesArray = []; + var count = event.dataTransfer.files.length; + + // Read and save the files to emscripten's FS + var written = 0; + var drop_dir = '.glfw_dropped_files'; + FS.createPath('/', drop_dir); + + function save(file) { + var path = '/' + drop_dir + '/' + file.name.replace(/\//g, '_'); + var reader = new FileReader(); + reader.onloadend = (e) => { + if (reader.readyState != 2) { // not DONE + ++written; + out('failed to read dropped file: '+file.name+': '+reader.error); + return; + } + + var data = e.target.result; + FS.writeFile(path, new Uint8Array(data)); + if (++written === count) { + {{{ makeDynCall('vpip', 'GLFW.active.dropFunc') }}}(GLFW.active.id, count, filenames); + + for (var i = 0; i < filenamesArray.length; ++i) { + _free(filenamesArray[i]); + } + _free(filenames); + } + }; + reader.readAsArrayBuffer(file); + + var filename = stringToNewUTF8(path); + filenamesArray.push(filename); + {{{ makeSetValue('filenames', `i*${POINTER_SIZE}` , 'filename', '*') }}}; + } + + for (var i = 0; i < count; ++i) { + save(event.dataTransfer.files[i]); + } +#endif // FILESYSTEM + + return false; + }, + + onDragover: (event) => { + if (!GLFW.active || !GLFW.active.dropFunc) return; + + event.preventDefault(); + return false; + }, + + setWindowSizeCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.windowSizeFunc; + win.windowSizeFunc = cbfun; + +#if USE_GLFW == 2 + // As documented in GLFW2 API (http://www.glfw.org/GLFWReference27.pdf#page=22), when size + // callback function is set, it will be called with the current window size before this + // function returns. + // GLFW3 on the over hand doesn't have this behavior (https://github.com/glfw/glfw/issues/62). + if (!win.windowSizeFunc) return null; + {{{ makeDynCall('vii', 'win.windowSizeFunc') }}}(win.width, win.height); +#endif + + return prevcbfun; + }, + + setWindowCloseCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.windowCloseFunc; + win.windowCloseFunc = cbfun; + return prevcbfun; + }, + + setWindowRefreshCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.windowRefreshFunc; + win.windowRefreshFunc = cbfun; + return prevcbfun; + }, + + onClickRequestPointerLock: (e) => { + var canvas = Browser.getCanvas(); + if (!Browser.pointerLock && canvas.requestPointerLock) { + canvas.requestPointerLock(); + e.preventDefault(); + } + }, + + setInputMode: (winid, mode, value) => { + var win = GLFW.WindowFromId(winid); + if (!win) return; + + switch (mode) { + case 0x00033001: { // GLFW_CURSOR + var canvas = Browser.getCanvas(); + switch (value) { + case 0x00034001: { // GLFW_CURSOR_NORMAL + win.inputModes[mode] = value; + canvas.removeEventListener('click', GLFW.onClickRequestPointerLock, true); + document.exitPointerLock(); + break; + } + case 0x00034002: { // GLFW_CURSOR_HIDDEN + err('glfwSetInputMode called with GLFW_CURSOR_HIDDEN value not implemented'); + break; + } + case 0x00034003: { // GLFW_CURSOR_DISABLED + win.inputModes[mode] = value; + canvas.addEventListener('click', GLFW.onClickRequestPointerLock, true); + canvas.requestPointerLock(); + break; + } + default: { + err(`glfwSetInputMode called with unknown value parameter value: ${value}`); + break; + } + } + break; + } + case 0x00033002: { // GLFW_STICKY_KEYS + err('glfwSetInputMode called with GLFW_STICKY_KEYS mode not implemented'); + break; + } + case 0x00033003: { // GLFW_STICKY_MOUSE_BUTTONS + err('glfwSetInputMode called with GLFW_STICKY_MOUSE_BUTTONS mode not implemented'); + break; + } + case 0x00033004: { // GLFW_LOCK_KEY_MODS + err('glfwSetInputMode called with GLFW_LOCK_KEY_MODS mode not implemented'); + break; + } + case 0x000330005: { // GLFW_RAW_MOUSE_MOTION + err('glfwSetInputMode called with GLFW_RAW_MOUSE_MOTION mode not implemented'); + break; + } + default: { + err(`glfwSetInputMode called with unknown mode parameter value: ${mode}`); + break; + } + } + }, + + getKey: (winid, key) => { + var win = GLFW.WindowFromId(winid); + if (!win) return 0; + return win.keys[key]; + }, + + getMouseButton: (winid, button) => { + var win = GLFW.WindowFromId(winid); + if (!win) return 0; + return (win.buttons & (1 << button)) > 0; + }, + + getCursorPos: (winid, x, y) => { + {{{ makeSetValue('x', '0', 'Browser.mouseX', 'double') }}}; + {{{ makeSetValue('y', '0', 'Browser.mouseY', 'double') }}}; + }, + + getMousePos: (winid, x, y) => { + {{{ makeSetValue('x', '0', 'Browser.mouseX', 'i32') }}}; + {{{ makeSetValue('y', '0', 'Browser.mouseY', 'i32') }}}; + }, + + setCursorPos: (winid, x, y) => { + }, + + getWindowPos: (winid, x, y) => { + var wx = 0; + var wy = 0; + + var win = GLFW.WindowFromId(winid); + if (win) { + wx = win.x; + wy = win.y; + } + + if (x) { + {{{ makeSetValue('x', '0', 'wx', 'i32') }}}; + } + + if (y) { + {{{ makeSetValue('y', '0', 'wy', 'i32') }}}; + } + }, + + setWindowPos: (winid, x, y) => { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.x = x; + win.y = y; + }, + + getWindowSize: (winid, width, height) => { + var ww = 0; + var wh = 0; + + var win = GLFW.WindowFromId(winid); + if (win) { + ww = win.width; + wh = win.height; + } + + if (width) { + {{{ makeSetValue('width', '0', 'ww', 'i32') }}}; + } + + if (height) { + {{{ makeSetValue('height', '0', 'wh', 'i32') }}}; + } + }, + + setWindowSize: (winid, width, height) => { + var win = GLFW.WindowFromId(winid); + if (!win) return; + + if (GLFW.active.id == win.id) { + Browser.setCanvasSize(width, height); // triggers the listener (onCanvasResize) + windowSizeFunc + } + }, + + defaultWindowHints: () => { + GLFW.hints = {...GLFW.defaultHints}; + }, + + createWindow: (width, height, title, monitor, share) => { + var i, id; + for (i = 0; i < GLFW.windows.length && GLFW.windows[i] !== null; i++) { + // no-op + } + if (i > 0) abort("glfwCreateWindow only supports one window at time currently"); + + // id for window + id = i + 1; + + // not valid + if (width <= 0 || height <= 0) return 0; + + if (monitor) { + Browser.requestFullscreen(); + } else { + Browser.setCanvasSize(width, height); + } + + // Create context when there are no existing alive windows + for (i = 0; i < GLFW.windows.length && GLFW.windows[i] == null; i++) { + // no-op + } + + const canvas = Browser.getCanvas(); + + var useWebGL = GLFW.hints[0x00022001] > 0; // Use WebGL when we are told to based on GLFW_CLIENT_API + if (i == GLFW.windows.length) { + if (useWebGL) { + var contextAttributes = { + antialias: (GLFW.hints[0x0002100D] > 1), // GLFW_SAMPLES + depth: (GLFW.hints[0x00021005] > 0), // GLFW_DEPTH_BITS + stencil: (GLFW.hints[0x00021006] > 0), // GLFW_STENCIL_BITS + alpha: (GLFW.hints[0x00021004] > 0) // GLFW_ALPHA_BITS + } +#if OFFSCREEN_FRAMEBUFFER + // TODO: Make GLFW explicitly aware of whether it is being proxied or not, and set these to true only when proxying is being performed. + GL.enableOffscreenFramebufferAttributes(contextAttributes); +#endif + Browser.createContext(canvas, /*useWebGL=*/true, /*setInModule=*/true, contextAttributes); + } else { + Browser.init(); + } + } + + // If context creation failed, do not return a valid window + if (!Module['ctx'] && useWebGL) return 0; + + // Initializes the framebuffer size from the canvas + var win = new GLFW_Window(id, width, height, canvas.width, canvas.height, title, monitor, share); + + // Set window to array + if (id - 1 == GLFW.windows.length) { + GLFW.windows.push(win); + } else { + GLFW.windows[id - 1] = win; + } + + GLFW.active = win; + GLFW.adjustCanvasDimensions(); + return win.id; + }, + + destroyWindow: (winid) => { + var win = GLFW.WindowFromId(winid); + if (!win) return; + +#if USE_GLFW == 3 + if (win.windowCloseFunc) { + {{{ makeDynCall('vp', 'win.windowCloseFunc') }}}(win.id); + } +#endif + + GLFW.windows[win.id - 1] = null; + if (GLFW.active.id == win.id) { + GLFW.active = null; + } + + // Destroy context when no alive windows + for (win of GLFW.windows) { + if (win !== null) return; + } + + delete Module['ctx']; + }, + + swapBuffers: (winid) => { + }, + + // Overrides Browser.requestFullscreen to notify listeners even if Browser.resizeCanvas is false + 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); + Browser.updateResizeListeners(); + } + } 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); + Browser.updateResizeListeners(); + } + } + 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(); + }, + + // Overrides Browser.updateCanvasDimensions to account for hi dpi scaling + updateCanvasDimensions(canvas, wNative, hNative) { + const scale = GLFW.getHiDPIScale(); + + 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'] && 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) { + wNative = w; + hNative = h; + } + const wNativeScaled = Math.floor(wNative * scale); + const hNativeScaled = Math.floor(hNative * scale); + if (canvas.width != wNativeScaled) canvas.width = wNativeScaled; + if (canvas.height != hNativeScaled) canvas.height = hNativeScaled; + if (typeof canvas.style != 'undefined') { + if (!GLFW.isCSSScalingEnabled()) { + canvas.style.setProperty( "width", wNative + "px", "important"); + canvas.style.setProperty("height", hNative + "px", "important"); + } else { + canvas.style.removeProperty( "width"); + canvas.style.removeProperty("height"); + } + } + }, + + // Overrides Browser.calculateMouseCoords to account for HiDPI scaling and CSS scaling + calculateMouseCoords(pageX, pageY) { + // Calculate the movement based on the changes + // in the coordinates. + const rect = Browser.getCanvas().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); +#if ASSERTIONS + // If this assert lands, it's likely because the browser doesn't support scrollX or pageXOffset + // and we have no viable fallback. + assert((typeof scrollX != 'undefined') && (typeof scrollY != 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.'); +#endif + var adjustedX = pageX - (scrollX + rect.left); + var adjustedY = pageY - (scrollY + rect.top); + + // getBoundingClientRect() returns dimension affected by CSS, so as a result: + // - when CSS scaling is enabled, this will fix the mouse coordinates to match the width/height of the window + // - otherwise the CSS width/height are forced to the width/height of the GLFW window (see updateCanvasDimensions), + // so there is no need to adjust the position + if (GLFW.isCSSScalingEnabled() && GLFW.active) { + adjustedX = adjustedX * (GLFW.active.width / rect.width); + adjustedY = adjustedY * (GLFW.active.height / rect.height); + } + + return { x: adjustedX, y: adjustedY }; + }, + + setWindowAttrib: (winid, attrib, value) => { + var win = GLFW.WindowFromId(winid); + if (!win) return; + const isHiDPIAware = GLFW.isHiDPIAware(); + win.attributes[attrib] = value; + if (isHiDPIAware !== GLFW.isHiDPIAware()) + GLFW.adjustCanvasDimensions(); + }, + + getDevicePixelRatio() { + return (typeof devicePixelRatio == 'number' && devicePixelRatio) || 1.0; + }, + + isHiDPIAware() { + if (GLFW.active) + return GLFW.active.attributes[0x0002200C] > 0; // GLFW_SCALE_TO_MONITOR + else + return false; + }, + + /** + * CSS Scaling is a feature that is NOT part of the GLFW API, but for historical reasons, it is available + * in Emscripten. + * It is automatically disabled when using Hi DPI (the library overrides CSS sizes). */ + isCSSScalingEnabled() { + return !GLFW.isHiDPIAware(); + }, + + adjustCanvasDimensions() { + if (GLFW.active) { + Browser.updateCanvasDimensions(Browser.getCanvas(), GLFW.active.width, GLFW.active.height); + Browser.updateResizeListeners(); + } + }, + + getHiDPIScale() { + return GLFW.isHiDPIAware() ? GLFW.scale : 1.0; + }, + + onDevicePixelRatioChange() { + GLFW.onWindowContentScaleChanged(GLFW.getDevicePixelRatio()); + GLFW.adjustCanvasDimensions(); + }, + + GLFW2ParamToGLFW3Param: (param) => { + var table = { + 0x00030001:0, // GLFW_MOUSE_CURSOR + 0x00030002:0, // GLFW_STICKY_KEYS + 0x00030003:0, // GLFW_STICKY_MOUSE_BUTTONS + 0x00030004:0, // GLFW_SYSTEM_KEYS + 0x00030005:0, // GLFW_KEY_REPEAT + 0x00030006:0, // GLFW_AUTO_POLL_EVENTS + 0x00020001:0, // GLFW_OPENED + 0x00020002:0, // GLFW_ACTIVE + 0x00020003:0, // GLFW_ICONIFIED + 0x00020004:0, // GLFW_ACCELERATED + 0x00020005:0x00021001, // GLFW_RED_BITS + 0x00020006:0x00021002, // GLFW_GREEN_BITS + 0x00020007:0x00021003, // GLFW_BLUE_BITS + 0x00020008:0x00021004, // GLFW_ALPHA_BITS + 0x00020009:0x00021005, // GLFW_DEPTH_BITS + 0x0002000A:0x00021006, // GLFW_STENCIL_BITS + 0x0002000B:0x0002100F, // GLFW_REFRESH_RATE + 0x0002000C:0x00021007, // GLFW_ACCUM_RED_BITS + 0x0002000D:0x00021008, // GLFW_ACCUM_GREEN_BITS + 0x0002000E:0x00021009, // GLFW_ACCUM_BLUE_BITS + 0x0002000F:0x0002100A, // GLFW_ACCUM_ALPHA_BITS + 0x00020010:0x0002100B, // GLFW_AUX_BUFFERS + 0x00020011:0x0002100C, // GLFW_STEREO + 0x00020012:0, // GLFW_WINDOW_NO_RESIZE + 0x00020013:0x0002100D, // GLFW_FSAA_SAMPLES + 0x00020014:0x00022002, // GLFW_OPENGL_VERSION_MAJOR + 0x00020015:0x00022003, // GLFW_OPENGL_VERSION_MINOR + 0x00020016:0x00022006, // GLFW_OPENGL_FORWARD_COMPAT + 0x00020017:0x00022007, // GLFW_OPENGL_DEBUG_CONTEXT + 0x00020018:0x00022008, // GLFW_OPENGL_PROFILE + }; + return table[param]; + } + }, + +/******************************************************************************* + * GLFW FUNCTIONS + ******************************************************************************/ + glfwInit: () => { + if (GLFW.windows) return 1; // GL_TRUE + + GLFW.initialTime = GLFW.getTime(); + GLFW.defaultWindowHints(); + GLFW.windows = new Array() + GLFW.active = null; + GLFW.scale = GLFW.getDevicePixelRatio(); + + + window.addEventListener('gamepadconnected', GLFW.onGamepadConnected, true); + window.addEventListener('gamepaddisconnected', GLFW.onGamepadDisconnected, true); + window.addEventListener('keydown', GLFW.onKeydown, true); + window.addEventListener('keypress', GLFW.onKeyPress, true); + window.addEventListener('keyup', GLFW.onKeyup, true); + window.addEventListener('blur', GLFW.onBlur, true); + + // watch for devicePixelRatio changes + GLFW.devicePixelRatioMQL = window.matchMedia('(resolution: ' + GLFW.getDevicePixelRatio() + 'dppx)'); + GLFW.devicePixelRatioMQL.addEventListener('change', GLFW.onDevicePixelRatioChange); + + var canvas = Browser.getCanvas(); + canvas.addEventListener('touchmove', GLFW.onMousemove, true); + canvas.addEventListener('touchstart', GLFW.onMouseButtonDown, true); + canvas.addEventListener('touchcancel', GLFW.onMouseButtonUp, true); + canvas.addEventListener('touchend', GLFW.onMouseButtonUp, true); + canvas.addEventListener('mousemove', GLFW.onMousemove, true); + canvas.addEventListener('mousedown', GLFW.onMouseButtonDown, true); + canvas.addEventListener("mouseup", GLFW.onMouseButtonUp, true); + canvas.addEventListener('wheel', GLFW.onMouseWheel, true); + canvas.addEventListener('mousewheel', GLFW.onMouseWheel, true); + canvas.addEventListener('mouseenter', GLFW.onMouseenter, true); + canvas.addEventListener('mouseleave', GLFW.onMouseleave, true); + canvas.addEventListener('drop', GLFW.onDrop, true); + canvas.addEventListener('dragover', GLFW.onDragover, true); + + // Overriding implementation to account for HiDPI + Browser.requestFullscreen = GLFW.requestFullscreen; + Browser.calculateMouseCoords = GLFW.calculateMouseCoords; + Browser.updateCanvasDimensions = GLFW.updateCanvasDimensions; + + Browser.resizeListeners.push((width, height) => { + if (GLFW.isHiDPIAware()) { + var canvas = Browser.getCanvas(); + GLFW.onCanvasResize(canvas.clientWidth, canvas.clientHeight, width, height); + } else { + GLFW.onCanvasResize(width, height, width, height); + } + }); + + return 1; // GL_TRUE + }, + + glfwTerminate: () => { + window.removeEventListener('gamepadconnected', GLFW.onGamepadConnected, true); + window.removeEventListener('gamepaddisconnected', GLFW.onGamepadDisconnected, true); + window.removeEventListener('keydown', GLFW.onKeydown, true); + window.removeEventListener('keypress', GLFW.onKeyPress, true); + window.removeEventListener('keyup', GLFW.onKeyup, true); + window.removeEventListener('blur', GLFW.onBlur, true); + var canvas = Browser.getCanvas(); + canvas.removeEventListener('touchmove', GLFW.onMousemove, true); + canvas.removeEventListener('touchstart', GLFW.onMouseButtonDown, true); + canvas.removeEventListener('touchcancel', GLFW.onMouseButtonUp, true); + canvas.removeEventListener('touchend', GLFW.onMouseButtonUp, true); + canvas.removeEventListener('mousemove', GLFW.onMousemove, true); + canvas.removeEventListener('mousedown', GLFW.onMouseButtonDown, true); + canvas.removeEventListener('mouseup', GLFW.onMouseButtonUp, true); + canvas.removeEventListener('wheel', GLFW.onMouseWheel, true); + canvas.removeEventListener('mousewheel', GLFW.onMouseWheel, true); + canvas.removeEventListener('mouseenter', GLFW.onMouseenter, true); + canvas.removeEventListener('mouseleave', GLFW.onMouseleave, true); + canvas.removeEventListener('drop', GLFW.onDrop, true); + canvas.removeEventListener('dragover', GLFW.onDragover, true); + + if (GLFW.devicePixelRatioMQL) + GLFW.devicePixelRatioMQL.removeEventListener('change', GLFW.onDevicePixelRatioChange); + + canvas.width = canvas.height = 1; + GLFW.windows = null; + GLFW.active = null; + }, + + glfwGetVersion: (major, minor, rev) => { +#if USE_GLFW == 2 + {{{ makeSetValue('major', '0', '2', 'i32') }}}; + {{{ makeSetValue('minor', '0', '7', 'i32') }}}; + {{{ makeSetValue('rev', '0', '7', 'i32') }}}; +#endif + +#if USE_GLFW == 3 + {{{ makeSetValue('major', '0', '3', 'i32') }}}; + {{{ makeSetValue('minor', '0', '2', 'i32') }}}; + {{{ makeSetValue('rev', '0', '1', 'i32') }}}; +#endif + }, + + glfwPollEvents: () => 0, + + glfwWaitEvents: () => 0, + + glfwGetTime: () => GLFW.getTime() - GLFW.initialTime, + + glfwSetTime: (time) => { + GLFW.initialTime = GLFW.getTime() - time; + }, + + glfwExtensionSupported__deps: ['glGetString', '$webglGetExtensions'], + glfwExtensionSupported: (extension) => { + GLFW.extensions ||= webglGetExtensions(); + + if (GLFW.extensions.includes(extension)) return 1; + + // extensions from GLEmulations do not come unprefixed + // so, try with prefix + return (GLFW.extensions.includes("GL_" + extension)); + }, + + glfwSwapInterval__deps: ['emscripten_set_main_loop_timing'], + glfwSwapInterval: (interval) => { + interval = Math.abs(interval); // GLFW uses negative values to enable GLX_EXT_swap_control_tear, which we don't have, so just treat negative and positive the same. + if (interval == 0) _emscripten_set_main_loop_timing({{{ cDefs.EM_TIMING_SETTIMEOUT }}}, 0); + else _emscripten_set_main_loop_timing({{{ cDefs.EM_TIMING_RAF }}}, interval); + }, + +#if USE_GLFW == 3 + glfwGetVersionString: () => { + GLFW.versionString ||= stringToNewUTF8("3.2.1 JS WebGL Emscripten"); + return GLFW.versionString; + }, + + glfwSetErrorCallback: (cbfun) => { + var prevcbfun = GLFW.errorFunc; + GLFW.errorFunc = cbfun; + return prevcbfun; + }, + + glfwWaitEventsTimeout: (timeout) => 0, + + glfwPostEmptyEvent: () => 0, + + glfwGetMonitors__deps: ['malloc'], + glfwGetMonitors: (count) => { + {{{ makeSetValue('count', '0', '1', 'i32') }}}; + if (!GLFW.monitors) { + GLFW.monitors = _malloc({{{ POINTER_SIZE }}}); + {{{ makeSetValue('GLFW.monitors', '0', '1', 'i32') }}}; + } + return GLFW.monitors; + }, + + glfwGetPrimaryMonitor: () => 1, + + glfwGetMonitorPos: (monitor, x, y) => { + {{{ makeSetValue('x', '0', '0', 'i32') }}}; + {{{ makeSetValue('y', '0', '0', 'i32') }}}; + }, + + glfwGetMonitorWorkarea: (monitor, x, y, w, h) => { + {{{ makeSetValue('x', '0', '0', 'i32') }}}; + {{{ makeSetValue('y', '0', '0', 'i32') }}}; + + {{{ makeSetValue('w', '0', 'screen.availWidth', 'i32') }}}; + {{{ makeSetValue('h', '0', 'screen.availHeight', 'i32') }}}; + }, + + glfwGetMonitorPhysicalSize: (monitor, width, height) => { + // AFAIK there is no way to do this in javascript + // Maybe with platform specific ccalls? + // + // Lets report 0 now which is wrong as it can get for end user. + {{{ makeSetValue('width', '0', '0', 'i32') }}}; + {{{ makeSetValue('height', '0', '0', 'i32') }}}; + }, + + glfwGetMonitorContentScale: (monitor, x, y) => { + {{{ makeSetValue('x', '0', 'GLFW.scale', 'float') }}}; + {{{ makeSetValue('y', '0', 'GLFW.scale', 'float') }}}; + }, + + glfwGetMonitorName: (mon) => { + GLFW.monitorString ||= stringToNewUTF8("HTML5 WebGL Canvas"); + return GLFW.monitorString; + }, + + glfwSetMonitorCallback: (cbfun) => { + var prevcbfun = GLFW.monitorFunc; + GLFW.monitorFunc = cbfun; + return prevcbfun; + }, + + // TODO: implement + glfwGetVideoModes: (monitor, count) => { + {{{ makeSetValue('count', '0', '0', 'i32') }}}; + return 0; + }, + + // TODO: implement + glfwGetVideoMode: (monitor) => 0, + + // TODO: implement + glfwSetGamma: (monitor, gamma) => 0, + + glfwGetGammaRamp: (monitor) => abort("glfwGetGammaRamp not implemented."), + + glfwSetGammaRamp: (monitor, ramp) => abort("glfwSetGammaRamp not implemented."), + + glfwDefaultWindowHints: () => GLFW.defaultWindowHints(), + + glfwWindowHint: (target, hint) => { + GLFW.hints[target] = hint; + }, + + glfwWindowHintString: (hint, value) => { + // from glfw docs -> we just ignore this. + // Some hints are platform specific. These may be set on any platform but they + // will only affect their specific platform. Other platforms will ignore them. + }, + + glfwCreateWindow: (width, height, title, monitor, share) => GLFW.createWindow(width, height, title, monitor, share), + + glfwDestroyWindow: (winid) => GLFW.destroyWindow(winid), + + glfwWindowShouldClose: (winid) => { + var win = GLFW.WindowFromId(winid); + if (!win) return 0; + return win.shouldClose; + }, + + glfwSetWindowShouldClose: (winid, value) => { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.shouldClose = value; + }, + + glfwSetWindowTitle: (winid, title) => GLFW.setWindowTitle(winid, title), + + glfwGetWindowPos: (winid, x, y) => GLFW.getWindowPos(winid, x, y), + + glfwSetWindowPos: (winid, x, y) => GLFW.setWindowPos(winid, x, y), + + glfwGetWindowSize: (winid, width, height) => GLFW.getWindowSize(winid, width, height), + + glfwSetWindowSize: (winid, width, height) => GLFW.setWindowSize(winid, width, height), + + glfwGetFramebufferSize: (winid, width, height) => { + var ww = 0; + var wh = 0; + + var win = GLFW.WindowFromId(winid); + if (win) { + ww = win.framebufferWidth; + wh = win.framebufferHeight; + } + + if (width) { + {{{ makeSetValue('width', '0', 'ww', 'i32') }}}; + } + + if (height) { + {{{ makeSetValue('height', '0', 'wh', 'i32') }}}; + } + }, + + glfwGetWindowContentScale: (winid, x, y) => { + // winid doesn't matter. all windows will use same scale anyway. + // hope i used this makeSetValue correctly + {{{ makeSetValue('x', '0', 'GLFW.scale', 'float') }}}; + {{{ makeSetValue('y', '0', 'GLFW.scale', 'float') }}}; + }, + + glfwGetWindowOpacity: (winid) => 1.0, + + glfwSetWindowOpacity: (winid, opacity) => { /* error */ }, + + glfwIconifyWindow: (winid) => { +#if ASSERTIONS + warnOnce('glfwIconifyWindow is not implemented'); +#endif + }, + + glfwRestoreWindow: (winid) => { +#if ASSERTIONS + warnOnce('glfwRestoreWindow is not implemented'); +#endif + }, + + glfwShowWindow: (winid) => 0, + + glfwHideWindow: (winid) => 0, + + glfwGetWindowMonitor: (winid) => { + var win = GLFW.WindowFromId(winid); + if (!win) return 0; + return win.monitor; + }, + + glfwGetWindowAttrib: (winid, attrib) => { + var win = GLFW.WindowFromId(winid); + if (!win) return 0; + return win.attributes[attrib]; + }, + + glfwSetWindowAttrib: (winid, attrib, value) => GLFW.setWindowAttrib(winid, attrib, value), + + glfwSetWindowUserPointer: (winid, ptr) => { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.userptr = ptr; + }, + + glfwGetWindowUserPointer: (winid) => { + var win = GLFW.WindowFromId(winid); + if (!win) return 0; + return win.userptr; + }, + + glfwSetWindowPosCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.windowPosFunc; + win.windowPosFunc = cbfun; + return prevcbfun; + }, + + glfwSetWindowSizeCallback: (winid, cbfun) => GLFW.setWindowSizeCallback(winid, cbfun), + + glfwSetWindowCloseCallback: (winid, cbfun) => GLFW.setWindowCloseCallback(winid, cbfun), + + glfwSetWindowRefreshCallback: (winid, cbfun) => GLFW.setWindowRefreshCallback(winid, cbfun), + + glfwSetWindowFocusCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.windowFocusFunc; + win.windowFocusFunc = cbfun; + return prevcbfun; + }, + + glfwSetWindowIconifyCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.windowIconifyFunc; + win.windowIconifyFunc = cbfun; + return prevcbfun; + }, + + glfwSetWindowMaximizeCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.windowMaximizeFunc; + win.windowMaximizeFunc = cbfun; + return prevcbfun; + }, + + glfwSetWindowIcon: (winid, count, images) => 0, + + glfwSetWindowSizeLimits: (winid, minwidth, minheight, maxwidth, maxheight) => 0, + + glfwSetWindowAspectRatio: (winid, numer, denom) => 0, + + glfwGetWindowFrameSize: (winid, left, top, right, bottom) => abort("glfwGetWindowFrameSize not implemented."), + + glfwMaximizeWindow: (winid) => 0, + + glfwFocusWindow: (winid) => 0, + + glfwRequestWindowAttention: (winid) => 0, // maybe do window.focus()? + + glfwSetWindowMonitor: (winid, monitor, xpos, ypos, width, height, refreshRate) => abort("glfwSetWindowMonitor not implemented."), + + glfwCreateCursor: (image, xhot, yhot) => 0, + + glfwCreateStandardCursor: (shape) => 0, + + glfwDestroyCursor: (cursor) => 0, + + glfwSetCursor: (winid, cursor) => 0, + + glfwSetFramebufferSizeCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.framebufferSizeFunc; + win.framebufferSizeFunc = cbfun; + return prevcbfun; + }, + + glfwSetWindowContentScaleCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.windowContentScaleFunc; + win.windowContentScaleFunc = cbfun; + return prevcbfun; + }, + + glfwGetInputMode: (winid, mode) => { + var win = GLFW.WindowFromId(winid); + if (!win) return; + + switch (mode) { + case 0x00033001: { // GLFW_CURSOR + if (Browser.pointerLock) { + win.inputModes[mode] = 0x00034003; // GLFW_CURSOR_DISABLED + } else { + win.inputModes[mode] = 0x00034001; // GLFW_CURSOR_NORMAL + } + } + } + + return win.inputModes[mode]; + }, + + glfwSetInputMode: (winid, mode, value) => { + GLFW.setInputMode(winid, mode, value); + }, + + glfwRawMouseMotionSupported: () => 0, + + glfwGetKey: (winid, key) => GLFW.getKey(winid, key), + + glfwGetKeyName: (key, scancode) => abort("glfwGetKeyName not implemented."), + + glfwGetKeyScancode: (key) => abort("glfwGetKeyScancode not implemented."), + + glfwGetMouseButton: (winid, button) => GLFW.getMouseButton(winid, button), + + glfwGetCursorPos: (winid, x, y) => GLFW.getCursorPos(winid, x, y), + + // I believe it is not possible to move the mouse with javascript + glfwSetCursorPos: (winid, x, y) => GLFW.setCursorPos(winid, x, y), + + glfwSetKeyCallback: (winid, cbfun) => GLFW.setKeyCallback(winid, cbfun), + + glfwSetCharCallback: (winid, cbfun) => GLFW.setCharCallback(winid, cbfun), + + glfwSetCharModsCallback: (winid, cbfun) => abort("glfwSetCharModsCallback not implemented."), + + glfwSetMouseButtonCallback: (winid, cbfun) => GLFW.setMouseButtonCallback(winid, cbfun), + + glfwSetCursorPosCallback: (winid, cbfun) => GLFW.setCursorPosCallback(winid, cbfun), + + glfwSetCursorEnterCallback: (winid, cbfun) => { + var win = GLFW.WindowFromId(winid); + if (!win) return null; + var prevcbfun = win.cursorEnterFunc; + win.cursorEnterFunc = cbfun; + return prevcbfun; + }, + + glfwSetScrollCallback: (winid, cbfun) => GLFW.setScrollCallback(winid, cbfun), + + glfwVulkanSupported: () => 0, + + glfwSetDropCallback: (winid, cbfun) => GLFW.setDropCallback(winid, cbfun), + + glfwGetTimerValue: () => abort("glfwGetTimerValue is not implemented."), + + glfwGetTimerFrequency: () => abort("glfwGetTimerFrequency is not implemented."), + + glfwGetRequiredInstanceExtensions: (count) => abort("glfwGetRequiredInstanceExtensions is not implemented."), + + glfwJoystickPresent: (joy) => { + GLFW.refreshJoysticks(); + + return GLFW.joys[joy] !== undefined; + }, + + glfwGetJoystickAxes: (joy, count) => { + GLFW.refreshJoysticks(); + + var state = GLFW.joys[joy]; + if (!state || !state.axes) { + {{{ makeSetValue('count', '0', '0', 'i32') }}}; + return; + } + + {{{ makeSetValue('count', '0', 'state.axesCount', 'i32') }}}; + return state.axes; + }, + + glfwGetJoystickButtons: (joy, count) => { + GLFW.refreshJoysticks(); + + var state = GLFW.joys[joy]; + if (!state || !state.buttons) { + {{{ makeSetValue('count', '0', '0', 'i32') }}}; + return; + } + + {{{ makeSetValue('count', '0', 'state.buttonsCount', 'i32') }}}; + return state.buttons; + }, + + glfwGetJoystickHats: (joy, count) => abort("glfwGetJoystickHats is not implemented"), + + glfwGetJoystickName: (joy) => { + if (GLFW.joys[joy]) { + return GLFW.joys[joy].id; + } + return 0; + }, + + glfwGetJoystickGUID: (jid) => abort("glfwGetJoystickGUID not implemented"), + + glfwSetJoystickUserPointer: (jid, ptr) => abort("glfwSetJoystickUserPointer not implemented"), + + glfwGetJoystickUserPointer: (jid) => abort("glfwGetJoystickUserPointer not implemented"), + + glfwJoystickIsGamepad: (jid) => abort("glfwJoystickIsGamepad not implemented"), + + glfwSetJoystickCallback: (cbfun) => GLFW.setJoystickCallback(cbfun), + + glfwSetClipboardString: (win, string) => 0, + + glfwGetClipboardString: (win) => 0, + + glfwMakeContextCurrent: (winid) => 0, + + glfwGetCurrentContext: () => GLFW.active ? GLFW.active.id : 0, + + glfwSwapBuffers: (winid) => GLFW.swapBuffers(winid), + +#elif USE_GLFW == 2 + glfwOpenWindow: (width, height, redbits, greenbits, bluebits, alphabits, depthbits, stencilbits, mode) => { + GLFW.hints[0x00021001] = redbits; // GLFW_RED_BITS + GLFW.hints[0x00021002] = greenbits; // GLFW_GREEN_BITS + GLFW.hints[0x00021003] = bluebits; // GLFW_BLUE_BITS + GLFW.hints[0x00021004] = alphabits; // GLFW_ALPHA_BITS + GLFW.hints[0x00021005] = depthbits; // GLFW_DEPTH_BITS + GLFW.hints[0x00021006] = stencilbits; // GLFW_STENCIL_BITS + GLFW.createWindow(width, height, "GLFW2 Window", 0, 0); + return 1; // GL_TRUE + }, + + glfwCloseWindow: () => GLFW.destroyWindow(GLFW.active.id), + + glfwOpenWindowHint: (target, hint) => { + target = GLFW.GLFW2ParamToGLFW3Param(target); + GLFW.hints[target] = hint; + }, + + glfwGetWindowSize_v2: (width, height) => GLFW.getWindowSize(GLFW.active.id, width, height), + + glfwSetWindowSize_v2: (width, height) => GLFW.setWindowSize(GLFW.active.id, width, height), + + glfwSetWindowPos_v2: (x, y) => GLFW.setWindowPos(GLFW.active.id, x, y), + + glfwSetWindowTitle_v2: (title) => GLFW.setWindowTitle(GLFW.active.id, title), + + glfwIconifyWindow_v2: () => { +#if ASSERTIONS + warnOnce('glfwIconifyWindow is not implemented'); +#endif + }, + + glfwRestoreWindow_v2: () => { +#if ASSERTIONS + warnOnce('glfwRestoreWindow is not implemented'); +#endif + }, + + glfwSwapBuffers_v2: () => GLFW.swapBuffers(GLFW.active.id), + + glfwGetWindowParam: (param) => { + param = GLFW.GLFW2ParamToGLFW3Param(param); + return GLFW.hints[param]; + }, + + glfwSetWindowSizeCallback_v2: (cbfun) => { + GLFW.setWindowSizeCallback(GLFW.active.id, cbfun); + }, + + glfwSetWindowCloseCallback_v2: (cbfun) => { + GLFW.setWindowCloseCallback(GLFW.active.id, cbfun); + }, + + glfwSetWindowRefreshCallback_v2: (cbfun) => GLFW.setWindowRefreshCallback(GLFW.active.id, cbfun), + + glfwGetKey_v2: (key) => GLFW.getKey(GLFW.active.id, key), + + glfwGetMouseButton_v2: (button) => GLFW.getMouseButton(GLFW.active.id, button), + + glfwGetMousePos: (x, y) => { + GLFW.getMousePos(GLFW.active.id, x, y); + }, + + glfwSetMousePos: (x, y) => { + GLFW.setCursorPos(GLFW.active.id, x, y); + }, + + glfwGetMouseWheel: () => 0, + + glfwSetMouseWheel: (pos) => 0, + + glfwSetKeyCallback_v2: (cbfun) => { + GLFW.setKeyCallback(GLFW.active.id, cbfun); + }, + + glfwSetCharCallback_v2: (cbfun) => { + GLFW.setCharCallback(GLFW.active.id, cbfun); + }, + + glfwSetMouseButtonCallback_v2: (cbfun) => { + GLFW.setMouseButtonCallback(GLFW.active.id, cbfun); + }, + + glfwSetMousePosCallback: (cbfun) => { + GLFW.setCursorPosCallback(GLFW.active.id, cbfun); + }, + + glfwSetMouseWheelCallback: (cbfun) => { + GLFW.setScrollCallback(GLFW.active.id, cbfun); + }, + + glfwGetDesktopMode: (mode) => abort("glfwGetDesktopMode is not implemented."), + + glfwSleep__deps: ['sleep'], + glfwSleep: (time) => _sleep(time), + + glfwEnable: (target) => { + target = GLFW.GLFW2ParamToGLFW3Param(target); + GLFW.hints[target] = false; + }, + + glfwDisable: (target) => { + target = GLFW.GLFW2ParamToGLFW3Param(target); + GLFW.hints[target] = true; + }, + + glfwGetGLVersion: (major, minor, rev) => { + {{{ makeSetValue('major', '0', '0', 'i32') }}}; + {{{ makeSetValue('minor', '0', '0', 'i32') }}}; + {{{ makeSetValue('rev', '0', '1', 'i32') }}}; + }, + + glfwCreateThread: (fun, arg) => { + {{{ makeDynCall('vp', 'fun') }}}(arg); + // One single thread + return 0; + }, + + glfwDestroyThread: (ID) => 0, + + glfwWaitThread: (ID, waitmode) => 0, + + // One single thread + glfwGetThreadID: () => 0, + + glfwCreateMutex: () => abort("glfwCreateMutex is not implemented."), + + glfwDestroyMutex: (mutex) => abort("glfwDestroyMutex is not implemented."), + + glfwLockMutex: (mutex) => abort("glfwLockMutex is not implemented."), + + glfwUnlockMutex: (mutex) => abort("glfwUnlockMutex is not implemented."), + + glfwCreateCond: () => abort("glfwCreateCond is not implemented."), + + glfwDestroyCond: (cond) => abort("glfwDestroyCond is not implemented."), + + glfwWaitCond: (cond, mutex, timeout) => abort("glfwWaitCond is not implemented."), + + glfwSignalCond: (cond) => abort("glfwSignalCond is not implemented."), + + glfwBroadcastCond: (cond) => abort("glfwBroadcastCond is not implemented."), + + glfwGetNumberOfProcessors: () => 1, // Threads are disabled anyway… + + glfwReadImage: (name, img, flags) => abort("glfwReadImage is not implemented."), + + glfwReadMemoryImage: (data, size, img, flags) => abort("glfwReadMemoryImage is not implemented."), + + glfwFreeImage: (img) => abort("glfwFreeImage is not implemented."), + + glfwLoadTexture2D: (name, flags) => abort("glfwLoadTexture2D is not implemented."), + + glfwLoadMemoryTexture2D: (data, size, flags) => abort("glfwLoadMemoryTexture2D is not implemented."), + + glfwLoadTextureImage2D: (img, flags) => abort("glfwLoadTextureImage2D is not implemented."), +#endif // GLFW2 +}; + +autoAddDeps(LibraryGLFW, '$GLFW'); +addToLibrary(LibraryGLFW); diff --git a/src/lib/libglut.js b/src/lib/libglut.js new file mode 100644 index 0000000000000..0527dd8274ae8 --- /dev/null +++ b/src/lib/libglut.js @@ -0,0 +1,660 @@ +/** + * @license + * Copyright 2012 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +var LibraryGLUT = { + $GLUT__deps: ['$Browser', '$getFullscreenElement', 'glutPostRedisplay'], + $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: 0x0000 /*GLUT_RGBA*/ | 0x0002 /*GLUT_DOUBLE*/ | 0x0010 /*GLUT_DEPTH*/, + // Set when going fullscreen + 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); + {{{ makeDynCall('vii', 'GLUT.passiveMotionFunc') }}}(lastX, lastY); + } else if (GLUT.buttons != 0 && GLUT.motionFunc) { + event.preventDefault(); + GLUT.saveModifiers(event); + {{{ makeDynCall('vii', 'GLUT.motionFunc') }}}(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); + {{{ makeDynCall('viii', 'GLUT.specialFunc') }}}(key, Browser.mouseX, Browser.mouseY); + } + } else { + key = GLUT.getASCIIKey(event); + if (key !== null && GLUT.keyboardFunc) { + event.preventDefault(); + GLUT.saveModifiers(event); + {{{ makeDynCall('viii', 'GLUT.keyboardFunc') }}}(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); + {{{ makeDynCall('viii', 'GLUT.specialUpFunc') }}}(key, Browser.mouseX, Browser.mouseY); + } + } else { + key = GLUT.getASCIIKey(event); + if (key !== null && GLUT.keyboardUpFunc) { + event.preventDefault (); + GLUT.saveModifiers(event); + {{{ makeDynCall('viii', 'GLUT.keyboardUpFunc') }}}(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); + {{{ makeDynCall('viiii', 'GLUT.mouseFunc') }}}(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); + {{{ makeDynCall('viiii', 'GLUT.mouseFunc') }}}(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); + {{{ makeDynCall('viiii', 'GLUT.mouseFunc') }}}(button, 0/*GLUT_DOWN*/, Browser.mouseX, Browser.mouseY); + } + }, + + // TODO add fullscreen API ala: + // http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/ + 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); + {{{ makeDynCall('vii', 'GLUT.reshapeFunc') }}}(width, height); + } + _glutPostRedisplay(); + }, + + // Resize callback stage 1: update canvas by setCanvasSize, which notifies resizeListeners including GLUT.reshapeFunc + onResize: () => { + // Update canvas size to clientWidth and clientHeight, which include CSS scaling + var canvas = Browser.getCanvas(); + Browser.setCanvasSize(canvas.clientWidth, canvas.clientHeight, /*noUpdates*/false); + } + }, + + glutGetModifiers__proxy: 'sync', + glutGetModifiers: () => GLUT.modifiers, + + glutInit__deps: ['$Browser', '$addOnExit'], + glutInit__proxy: 'sync', + 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) { + {{{ makeDynCall('vii', 'GLUT.reshapeFunc') }}}(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; + }); + }, + + glutInitWindowSize__proxy: 'sync', + glutInitWindowSize: (width, height) => { + Browser.setCanvasSize( GLUT.initWindowWidth = width, + GLUT.initWindowHeight = height ); + }, + + glutInitWindowPosition__proxy: 'sync', + // Ignore for now + glutInitWindowPosition: (x, y) => {}, + + 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"); + } + }, + + glutIdleFunc__proxy: 'sync', + glutIdleFunc__deps: ['$safeSetTimeout'], + glutIdleFunc: (func) => { + function callback() { + if (GLUT.idleFunc) { + {{{ makeDynCall('v', 'GLUT.idleFunc') }}}(); + 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; + }, + + glutTimerFunc__proxy: 'sync', + glutTimerFunc__deps: ['$safeSetTimeout'], + glutTimerFunc: (msec, func, value) => + safeSetTimeout(() => {{{ makeDynCall('vi', 'func') }}}(value), msec), + + glutDisplayFunc__proxy: 'sync', + glutDisplayFunc: (func) => { + GLUT.displayFunc = func; + }, + + glutKeyboardFunc__proxy: 'sync', + glutKeyboardFunc: (func) => { + GLUT.keyboardFunc = func; + }, + + glutKeyboardUpFunc__proxy: 'sync', + glutKeyboardUpFunc: (func) => { + GLUT.keyboardUpFunc = func; + }, + + glutSpecialFunc__proxy: 'sync', + glutSpecialFunc: (func) => { + GLUT.specialFunc = func; + }, + + glutSpecialUpFunc__proxy: 'sync', + glutSpecialUpFunc: (func) => { + GLUT.specialUpFunc = func; + }, + + glutReshapeFunc__proxy: 'sync', + glutReshapeFunc: (func) => { + GLUT.reshapeFunc = func; + }, + + glutMotionFunc__proxy: 'sync', + glutMotionFunc: (func) => { + GLUT.motionFunc = func; + }, + + glutPassiveMotionFunc__proxy: 'sync', + glutPassiveMotionFunc: (func) => { + GLUT.passiveMotionFunc = func; + }, + + glutMouseFunc__proxy: 'sync', + glutMouseFunc: (func) => { + GLUT.mouseFunc = func; + }, + + glutSetCursor__proxy: 'sync', + 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; + }, + + glutCreateWindow__proxy: 'sync', + glutCreateWindow__deps: ['$Browser'], + 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 OFFSCREEN_FRAMEBUFFER + // TODO: Make glutCreateWindow explicitly aware of whether it is being proxied or not, and set these to true only when proxying is being performed. + GL.enableOffscreenFramebufferAttributes(contextAttributes); +#endif + if (!Browser.createContext(Browser.getCanvas(), /*useWebGL=*/true, /*setInModule=*/true, contextAttributes)) { + return 0; // failure + } + return 1; // a new GLUT window ID for the created context + }, + + glutDestroyWindow__proxy: 'sync', + glutDestroyWindow__deps: ['$Browser'], + glutDestroyWindow: (name) => { + delete Module['ctx']; + return 1; + }, + + glutReshapeWindow__proxy: 'sync', + glutReshapeWindow__deps: ['$GLUT', 'glutPostRedisplay'], + 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) { + {{{ makeDynCall('vii', 'GLUT.reshapeFunc') }}}(width, height); + } + _glutPostRedisplay(); + }, + + glutPositionWindow__proxy: 'sync', + glutPositionWindow__deps: ['$GLUT', 'glutPostRedisplay'], + glutPositionWindow: (x, y) => { + Browser.exitFullscreen(); + /* TODO */ + _glutPostRedisplay(); + }, + + glutFullScreen__proxy: 'sync', + glutFullScreen__deps: ['$GLUT'], + 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); + }, + + glutInitDisplayMode__proxy: 'sync', + glutInitDisplayMode: (mode) => GLUT.initDisplayMode = mode, + + glutSwapBuffers__proxy: 'sync', + glutSwapBuffers: () => {}, + + glutPostRedisplay__proxy: 'sync', + glutPostRedisplay__deps: ['$MainLoop'], + glutPostRedisplay: () => { + if (GLUT.displayFunc && !GLUT.requestedAnimationFrame) { + GLUT.requestedAnimationFrame = true; + MainLoop.requestAnimationFrame(() => { + GLUT.requestedAnimationFrame = false; + MainLoop.runIter(() => {{{ makeDynCall('v', 'GLUT.displayFunc') }}}()); + }); + } + }, + + glutMainLoop__proxy: 'sync', + glutMainLoop__deps: ['$GLUT', 'glutPostRedisplay'], + glutMainLoop: () => { + // Do an initial resize, since there's no window resize event on startup + GLUT.onResize(); + _glutPostRedisplay(); + throw 'unwind'; + }, + +}; + +autoAddDeps(LibraryGLUT, '$GLUT'); +addToLibrary(LibraryGLUT); diff --git a/src/lib/libhtml5.js b/src/lib/libhtml5.js new file mode 100644 index 0000000000000..b7363179d097b --- /dev/null +++ b/src/lib/libhtml5.js @@ -0,0 +1,2386 @@ +/** + * @license + * Copyright 2014 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +var LibraryHTML5 = { + $JSEvents__deps: [ +#if PTHREADS + '_emscripten_run_callback_on_thread', +#endif + '$addOnExit', + ], + $JSEvents: { + +#if USE_CLOSURE_COMPILER + // pointers to structs malloc()ed to Emscripten HEAP for JS->C interop. + batteryEvent: 0, + gamepadEvent: 0, + keyEvent: 0, + mouseEvent: 0, + wheelEvent: 0, + uiEvent: 0, + focusEvent: 0, + deviceOrientationEvent: 0, + orientationChangeEvent: 0, + deviceMotionEvent: 0, + fullscreenChangeEvent: 0, + pointerlockChangeEvent: 0, + visibilityChangeEvent: 0, + touchEvent: 0, +#endif + +/* We do not depend on the exact initial values of falsey member fields - these + fields can be populated on-demand to save code size. + (but still documented here to keep track of what is supposed to be present) + + // When we transition from fullscreen to windowed mode, we remember here the + // element that was just in fullscreen mode so that we can report + // information about that element in the event message. + previousFullscreenElement: null, + + // When the C runtime exits via exit(), we unregister all event handlers + // added by this library to be nice and clean. + // Track in this field whether we have yet registered that onExit handler. + removeEventListenersRegistered: false, + +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + // If we are in an event handler, specifies the event handler object from + // the eventHandlers array that is currently running. + currentEventHandler: null, +#endif +*/ + memcpy(target, src, size) { + HEAP8.set(HEAP8.subarray(src, src + size), target); + }, + + removeAllEventListeners() { + while (JSEvents.eventHandlers.length) { + JSEvents._removeHandler(JSEvents.eventHandlers.length - 1); + } +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + JSEvents.deferredCalls = []; +#endif + }, + +#if EXIT_RUNTIME + registerRemoveEventListeners() { + if (!JSEvents.removeEventListenersRegistered) { + addOnExit(JSEvents.removeAllEventListeners); + JSEvents.removeEventListenersRegistered = true; + } + }, +#endif + +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + // If positive, we are currently executing in a JS event handler. + // (this particular property must be initialized to zero, as we ++/-- it) + inEventHandler: 0, + + deferredCalls: [], + + // Queues the given function call to occur the next time we enter an event handler. + // Existing implementations of pointerlock apis have required that + // the target element is active in fullscreen mode first. Therefore give + // fullscreen mode request a precedence of 1 and pointer lock a precedence of 2 + // and sort by that to always request fullscreen before pointer lock. + 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); + }, + + // Erases all deferred calls to the given target function from the queue list. + 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); + } + }, +#endif + + // Stores objects representing each currently registered JS event handler. + eventHandlers: [], + + // Removes all event handlers on the given DOM element of the given type. + // Pass in eventTypeString == undefined/null to remove all event handlers + // regardless of the type. + 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) { +#if ASSERTIONS + err('registerOrRemoveHandler: the target element for event handler registration does not exist, when processing the following event handler registration:'); + console.dir(eventHandler); +#endif + return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + } + if (eventHandler.callbackfunc) { +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + 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; + }; +#else + eventHandler.eventListenerFunc = eventHandler.handlerFunc; +#endif + + eventHandler.target.addEventListener(eventHandler.eventTypeString, + eventHandler.eventListenerFunc, + eventHandler.useCapture); + JSEvents.eventHandlers.push(eventHandler); +#if EXIT_RUNTIME + JSEvents.registerRemoveEventListeners(); +#endif + } 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 {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + +#if PTHREADS + getTargetThreadForEventCallback(targetThread) { + switch (targetThread) { + case {{{ cDefs.EM_CALLBACK_THREAD_CONTEXT_MAIN_RUNTIME_THREAD }}}: + // The event callback for the current event should be called on the + // main browser thread. (0 == don't proxy) + return 0; + case {{{ cDefs.EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD }}}: + // The event callback for the current event should be backproxied to + // the thread that is registering the event. + // This can be 0 in the case that the caller uses + // EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD but on the main thread + // itself. + return PThread.currentProxiedOperationCallerThread; + default: + // The event callback for the current event should be proxied to the + // given specific thread. + return targetThread; + } + }, +#endif + + getNodeNameForTarget(target) { + if (!target) return ''; + if (target == window) return '#window'; + if (target == screen) return '#screen'; + return target?.nodeName || ''; + }, + + fullscreenEnabled() { + return document.fullscreenEnabled +#if MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED + // Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitFullscreenEnabled. + // TODO: If Safari at some point ships with unprefixed version, update the version check above. + || document.webkitFullscreenEnabled +#endif + ; + }, + }, + + $getFullscreenElement__internal: true, + $getFullscreenElement() { + return document.fullscreenElement || document.mozFullScreenElement || + document.webkitFullscreenElement || document.webkitCurrentFullScreenElement || + document.msFullscreenElement; + }, + + $registerKeyEventCallback__noleakcheck: true, + $registerKeyEventCallback__deps: ['$JSEvents', '$findEventTarget', '$stringToUTF8', 'malloc'], + $registerKeyEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.keyEvent ||= _malloc({{{ C_STRUCTS.EmscriptenKeyboardEvent.__size__ }}}); + + var keyEventHandlerFunc = (e) => { +#if ASSERTIONS + assert(e); +#endif + +#if PTHREADS + var keyEventData = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenKeyboardEvent.__size__ }}}) : JSEvents.keyEvent; // This allocated block is passed as satellite data to the proxied function call, so the call frees up the data block when done. +#else + var keyEventData = JSEvents.keyEvent; +#endif + {{{ makeSetValue('keyEventData', C_STRUCTS.EmscriptenKeyboardEvent.timestamp, 'e.timeStamp', 'double') }}}; + + var idx = {{{ getHeapOffset('keyEventData', 'i32') }}}; + + HEAP32[idx + {{{ C_STRUCTS.EmscriptenKeyboardEvent.location / 4 }}}] = e.location; + HEAP8[keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.ctrlKey }}}] = e.ctrlKey; + HEAP8[keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.shiftKey }}}] = e.shiftKey; + HEAP8[keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.altKey }}}] = e.altKey; + HEAP8[keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.metaKey }}}] = e.metaKey; + HEAP8[keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.repeat }}}] = e.repeat; + HEAP32[idx + {{{ C_STRUCTS.EmscriptenKeyboardEvent.charCode / 4 }}}] = e.charCode; + HEAP32[idx + {{{ C_STRUCTS.EmscriptenKeyboardEvent.keyCode / 4 }}}] = e.keyCode; + HEAP32[idx + {{{ C_STRUCTS.EmscriptenKeyboardEvent.which / 4 }}}] = e.which; + stringToUTF8(e.key || '', keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.key }}}, {{{ cDefs.EM_HTML5_SHORT_STRING_LEN_BYTES }}}); + stringToUTF8(e.code || '', keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.code }}}, {{{ cDefs.EM_HTML5_SHORT_STRING_LEN_BYTES }}}); + stringToUTF8(e.char || '', keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.charValue }}}, {{{ cDefs.EM_HTML5_SHORT_STRING_LEN_BYTES }}}); + stringToUTF8(e.locale || '', keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.locale }}}, {{{ cDefs.EM_HTML5_SHORT_STRING_LEN_BYTES }}}); + +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, keyEventData, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, keyEventData, userData)) e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + callbackfunc, + handlerFunc: keyEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + // In DOM capturing and bubbling sequence, there are two special elements at the top of the event chain that can be of interest + // to register many events to: document and window. These cannot be addressed by using document.querySelector(), so + // a special mechanism to address them is needed. (For any other special object, such as screen.orientation, no general access + // scheme should be needed, but the object-specific event callback registration functions should handle them individually). + // + // Users can also add more special event targets, basically by just doing something like + // specialHTMLTargets["!canvas"] = Module.canvas; + // (that will let !canvas map to the canvas held in Module.canvas). + $specialHTMLTargets__docs: '/** @type {Object} */', +#if ENVIRONMENT_MAY_BE_WORKER || ENVIRONMENT_MAY_BE_NODE || ENVIRONMENT_MAY_BE_SHELL || PTHREADS + $specialHTMLTargets: "[0, typeof document != 'undefined' ? document : 0, typeof window != 'undefined' ? window : 0]", +#else + $specialHTMLTargets: "[0, document, window]", +#endif + +#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + $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; + }, + + // Find a DOM element with the given ID, or null if none is found. + $findEventTarget__deps: ['$maybeCStringToJsString', '$specialHTMLTargets'], + $findEventTarget: (target) => { + target = maybeCStringToJsString(target); +#if ENVIRONMENT_MAY_BE_WORKER || ENVIRONMENT_MAY_BE_NODE + var domElement = specialHTMLTargets[target] || (typeof document != 'undefined' ? document.querySelector(target) : null); +#else + var domElement = specialHTMLTargets[target] || document.querySelector(target); +#endif + return domElement; + }, + +#if OFFSCREENCANVAS_SUPPORT + $findCanvasEventTarget__deps: ['$GL', '$maybeCStringToJsString', '$specialHTMLTargets'], + $findCanvasEventTarget: (target) => { + target = maybeCStringToJsString(target); + + // When compiling with OffscreenCanvas support and looking up a canvas to target, + // we first look up if the target Canvas has been transferred to OffscreenCanvas use. + // These transfers are represented/tracked by GL.offscreenCanvases object, which contain + // the OffscreenCanvas element for each regular Canvas element that has been transferred. + + // Note that each pthread/worker have their own set of GL.offscreenCanvases. That is, + // when an OffscreenCanvas is transferred from a pthread/main thread to another pthread, + // it will move in the GL.offscreenCanvases array between threads. Hence GL.offscreenCanvases + // represents the set of OffscreenCanvases owned by the current calling thread. + + // First check out the list of OffscreenCanvases by CSS selector ID ('#myCanvasID') + return GL.offscreenCanvases[target.slice(1)] // Remove '#' prefix + // If not found, if one is querying by using DOM tag name selector 'canvas', grab the first + // OffscreenCanvas that we can find. + || (target == 'canvas' && Object.values(GL.offscreenCanvases)[0]) + // If not found, check specialHTMLTargets + || specialHTMLTargets[target] + // If that is not found either, query via the regular DOM selector. +#if PTHREADS + || (typeof document != 'undefined' && document.querySelector(target)); +#else + || document.querySelector(target); +#endif + }, +#else + $findCanvasEventTarget: '$findEventTarget', +#endif + +#else + // Find a DOM element with the given ID, or null if none is found. + $findEventTarget__deps: ['$specialHTMLTargets'], + $findEventTarget: (target) => { +#if ASSERTIONS + warnOnce('Rules for selecting event targets in HTML5 API are changing: instead of using document.getElementById() that only can refer to elements by their DOM ID, new event target selection mechanism uses the more flexible function document.querySelector() that can look up element names, classes, and complex CSS selectors. Build with -sDISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR to change to the new lookup rules. See https://github.com/emscripten-core/emscripten/pull/7977 for more details.'); +#endif + // The sensible "default" target varies between events, but use window as the default + // since DOM events mostly can default to that. Specific callback registrations + // override their own defaults. + if (!target) return window; + if (typeof target == "number") target = specialHTMLTargets[target] || UTF8ToString(target); + if (target === '#window') return window; + else if (target === '#document') return document; + else if (target === '#screen') return screen; + else if (target === '#canvas') return Module['canvas']; + else if (typeof target == 'string') +#if ENVIRONMENT_MAY_BE_WORKER || ENVIRONMENT_MAY_BE_NODE + return (typeof document != 'undefined') ? document.getElementById(target) : null; +#else + return document.getElementById(target); +#endif + return target; + }, + + // Like findEventTarget, but looks for OffscreenCanvas elements first + $findCanvasEventTarget__deps: ['$findEventTarget'], + $findCanvasEventTarget: (target) => { + if (typeof target == 'number') target = UTF8ToString(target); + if (!target || target === '#canvas') { + if (typeof GL != 'undefined' && GL.offscreenCanvases['canvas']) return GL.offscreenCanvases['canvas']; // TODO: Remove this line, target '#canvas' should refer only to Module['canvas'], not to GL.offscreenCanvases['canvas'] - but need stricter tests to be able to remove this line. + return Module['canvas']; + } + if (typeof GL != 'undefined' && GL.offscreenCanvases[target]) return GL.offscreenCanvases[target]; + return findEventTarget(target); + }, +#endif + + emscripten_set_keypress_callback_on_thread__proxy: 'sync', + emscripten_set_keypress_callback_on_thread__deps: ['$registerKeyEventCallback'], + emscripten_set_keypress_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_KEYPRESS }}}, "keypress", targetThread), + + emscripten_set_keydown_callback_on_thread__proxy: 'sync', + emscripten_set_keydown_callback_on_thread__deps: ['$registerKeyEventCallback'], + emscripten_set_keydown_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_KEYDOWN }}}, "keydown", targetThread), + + emscripten_set_keyup_callback_on_thread__proxy: 'sync', + emscripten_set_keyup_callback_on_thread__deps: ['$registerKeyEventCallback'], + emscripten_set_keyup_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_KEYUP }}}, "keyup", targetThread), + + // Outline access to function .getBoundingClientRect() since it is a long string. Closure compiler does not outline access to it by itself, but it can inline access if + // there is only one caller to this function. + $getBoundingClientRect__deps: ['$specialHTMLTargets'], + $getBoundingClientRect: (e) => specialHTMLTargets.indexOf(e) < 0 ? e.getBoundingClientRect() : {'left':0,'top':0}, + + // Copies mouse event data from the given JS mouse event 'e' to the specified Emscripten mouse event structure in the HEAP. + // eventStruct: the structure to populate. + // e: The JS mouse event to read data from. + // target: Specifies a target DOM element that will be used as the reference to populate targetX and targetY parameters. + $fillMouseEventData__deps: ['$getBoundingClientRect'], + $fillMouseEventData: (eventStruct, e, target) => { +#if ASSERTIONS + assert(eventStruct % 4 == 0); +#endif + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.timestamp, 'e.timeStamp', 'double') }}}; + var idx = {{{ getHeapOffset('eventStruct', 'i32') }}}; + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.screenX / 4 }}}] = e.screenX; + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.screenY / 4 }}}] = e.screenY; + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.clientX / 4 }}}] = e.clientX; + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.clientY / 4 }}}] = e.clientY; + HEAP8[eventStruct + {{{ C_STRUCTS.EmscriptenMouseEvent.ctrlKey }}}] = e.ctrlKey; + HEAP8[eventStruct + {{{ C_STRUCTS.EmscriptenMouseEvent.shiftKey }}}] = e.shiftKey; + HEAP8[eventStruct + {{{ C_STRUCTS.EmscriptenMouseEvent.altKey }}}] = e.altKey; + HEAP8[eventStruct + {{{ C_STRUCTS.EmscriptenMouseEvent.metaKey }}}] = e.metaKey; + HEAP16[idx*2 + {{{ C_STRUCTS.EmscriptenMouseEvent.button / 2 }}}] = e.button; + HEAP16[idx*2 + {{{ C_STRUCTS.EmscriptenMouseEvent.buttons / 2 }}}] = e.buttons; + + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.movementX / 4 }}}] = e["movementX"]; + + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.movementY / 4 }}}] = e["movementY"]; + +#if !DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + if (Module['canvas']) { + var rect = getBoundingClientRect(Module['canvas']); + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.canvasX / 4 }}}] = e.clientX - (rect.left | 0); + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.canvasY / 4 }}}] = e.clientY - (rect.top | 0); + } else { // Canvas is not initialized, return 0. + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.canvasX / 4 }}}] = 0; + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.canvasY / 4 }}}] = 0; + } +#endif + // 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 + {{{ C_STRUCTS.EmscriptenMouseEvent.targetX / 4 }}}] = e.clientX - (rect.left | 0); + HEAP32[idx + {{{ C_STRUCTS.EmscriptenMouseEvent.targetY / 4 }}}] = e.clientY - (rect.top | 0); + }, + + $registerMouseEventCallback__noleakcheck: true, + $registerMouseEventCallback__deps: ['$JSEvents', '$fillMouseEventData', '$findEventTarget', 'malloc'], + $registerMouseEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.mouseEvent ||= _malloc({{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}); + 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 PTHREADS + if (targetThread) { + var mouseEventData = _malloc({{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}); // This allocated block is passed as satellite data to the proxied function call, so the call frees up the data block when done. + fillMouseEventData(mouseEventData, e, target); + __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, mouseEventData, userData); + } else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, JSEvents.mouseEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target, +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + allowsDeferredCalls: eventTypeString != 'mousemove' && eventTypeString != 'mouseenter' && eventTypeString != 'mouseleave', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them! +#endif + eventTypeString, + callbackfunc, + handlerFunc: mouseEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_click_callback_on_thread__proxy: 'sync', + emscripten_set_click_callback_on_thread__deps: ['$registerMouseEventCallback'], + emscripten_set_click_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_CLICK }}}, "click", targetThread), + + emscripten_set_mousedown_callback_on_thread__proxy: 'sync', + emscripten_set_mousedown_callback_on_thread__deps: ['$registerMouseEventCallback'], + emscripten_set_mousedown_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_MOUSEDOWN }}}, "mousedown", targetThread), + + emscripten_set_mouseup_callback_on_thread__proxy: 'sync', + emscripten_set_mouseup_callback_on_thread__deps: ['$registerMouseEventCallback'], + emscripten_set_mouseup_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_MOUSEUP }}}, "mouseup", targetThread), + + emscripten_set_dblclick_callback_on_thread__proxy: 'sync', + emscripten_set_dblclick_callback_on_thread__deps: ['$registerMouseEventCallback'], + emscripten_set_dblclick_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_DBLCLICK }}}, "dblclick", targetThread), + + emscripten_set_mousemove_callback_on_thread__proxy: 'sync', + emscripten_set_mousemove_callback_on_thread__deps: ['$registerMouseEventCallback'], + emscripten_set_mousemove_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_MOUSEMOVE }}}, "mousemove", targetThread), + + emscripten_set_mouseenter_callback_on_thread__proxy: 'sync', + emscripten_set_mouseenter_callback_on_thread__deps: ['$registerMouseEventCallback'], + emscripten_set_mouseenter_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_MOUSEENTER }}}, "mouseenter", targetThread), + + emscripten_set_mouseleave_callback_on_thread__proxy: 'sync', + emscripten_set_mouseleave_callback_on_thread__deps: ['$registerMouseEventCallback'], + emscripten_set_mouseleave_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_MOUSELEAVE }}}, "mouseleave", targetThread), + + emscripten_set_mouseover_callback_on_thread__proxy: 'sync', + emscripten_set_mouseover_callback_on_thread__deps: ['$registerMouseEventCallback'], + emscripten_set_mouseover_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_MOUSEOVER }}}, "mouseover", targetThread), + + emscripten_set_mouseout_callback_on_thread__proxy: 'sync', + emscripten_set_mouseout_callback_on_thread__deps: ['$registerMouseEventCallback'], + emscripten_set_mouseout_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_MOUSEOUT }}}, "mouseout", targetThread), + + emscripten_get_mouse_status__proxy: 'sync', + emscripten_get_mouse_status__deps: ['$JSEvents'], + emscripten_get_mouse_status: (mouseState) => { + if (!JSEvents.mouseEvent) return {{{ cDefs.EMSCRIPTEN_RESULT_NO_DATA }}}; + // 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, {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $registerWheelEventCallback__noleakcheck: true, + $registerWheelEventCallback__deps: ['$JSEvents', '$fillMouseEventData', 'malloc'], + $registerWheelEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.wheelEvent ||= _malloc({{{ C_STRUCTS.EmscriptenWheelEvent.__size__ }}}); + + // The DOM Level 3 events spec event 'wheel' + var wheelHandlerFunc = (e = event) => { +#if PTHREADS + var wheelEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenWheelEvent.__size__ }}}) : JSEvents.wheelEvent; // This allocated block is passed as satellite data to the proxied function call, so the call frees up the data block when done. +#else + var wheelEvent = JSEvents.wheelEvent; +#endif + fillMouseEventData(wheelEvent, e, target); + {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e["deltaX"]', 'double') }}}; + {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, 'e["deltaY"]', 'double') }}}; + {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, 'e["deltaZ"]', 'double') }}}; + {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, 'e["deltaMode"]', 'i32') }}}; +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, wheelEvent, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, wheelEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target, +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + allowsDeferredCalls: true, +#endif + eventTypeString, + callbackfunc, + handlerFunc: wheelHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_wheel_callback_on_thread__proxy: 'sync', + emscripten_set_wheel_callback_on_thread__deps: ['$registerWheelEventCallback', '$findEventTarget'], + emscripten_set_wheel_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => { + target = findEventTarget(target); + if (!target) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + if (typeof target.onwheel != 'undefined') { + return registerWheelEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_WHEEL }}}, "wheel", targetThread); + } else { + return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + }, + + $registerUiEventCallback__noleakcheck: true, + $registerUiEventCallback__deps: ['$JSEvents', '$findEventTarget', 'malloc'], + $registerUiEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.uiEvent ||= _malloc({{{ C_STRUCTS.EmscriptenUiEvent.__size__ }}}); + +#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + target = findEventTarget(target); +#else + if (eventTypeId == {{{ cDefs.EMSCRIPTEN_EVENT_SCROLL }}} && !target) { + target = document; // By default read scroll events on document rather than window. + } else { + target = findEventTarget(target); + } +#else +#endif + + 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; + } +#if PTHREADS + var uiEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenUiEvent.__size__ }}}) : JSEvents.uiEvent; +#else + var uiEvent = JSEvents.uiEvent; +#endif + {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.detail, '0', 'i32') }}}; // always zero for resize and scroll + {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.documentBodyClientWidth, 'b.clientWidth', 'i32') }}}; + {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.documentBodyClientHeight, 'b.clientHeight', 'i32') }}}; + {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.windowInnerWidth, 'innerWidth', 'i32') }}}; + {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.windowInnerHeight, 'innerHeight', 'i32') }}}; + {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.windowOuterWidth, 'outerWidth', 'i32') }}}; + {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.windowOuterHeight, 'outerHeight', 'i32') }}}; + {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.scrollTop, 'pageXOffset | 0', 'i32') }}}; // scroll offsets are float + {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.scrollLeft, 'pageYOffset | 0', 'i32') }}}; +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, uiEvent, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, uiEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: uiEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_resize_callback_on_thread__proxy: 'sync', + emscripten_set_resize_callback_on_thread__deps: ['$registerUiEventCallback'], + emscripten_set_resize_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_RESIZE }}}, "resize", targetThread), + + emscripten_set_scroll_callback_on_thread__proxy: 'sync', + emscripten_set_scroll_callback_on_thread__deps: ['$registerUiEventCallback'], + emscripten_set_scroll_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_SCROLL }}}, "scroll", targetThread), + + $registerFocusEventCallback__noleakcheck: true, + $registerFocusEventCallback__deps: ['$JSEvents', '$findEventTarget', 'malloc', '$stringToUTF8'], + $registerFocusEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.focusEvent ||= _malloc({{{ C_STRUCTS.EmscriptenFocusEvent.__size__ }}}); + + var focusEventHandlerFunc = (e = event) => { + var nodeName = JSEvents.getNodeNameForTarget(e.target); + var id = e.target.id ? e.target.id : ''; + +#if PTHREADS + var focusEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenFocusEvent.__size__ }}}) : JSEvents.focusEvent; +#else + var focusEvent = JSEvents.focusEvent; +#endif + stringToUTF8(nodeName, focusEvent + {{{ C_STRUCTS.EmscriptenFocusEvent.nodeName }}}, {{{ cDefs.EM_HTML5_LONG_STRING_LEN_BYTES }}}); + stringToUTF8(id, focusEvent + {{{ C_STRUCTS.EmscriptenFocusEvent.id }}}, {{{ cDefs.EM_HTML5_LONG_STRING_LEN_BYTES }}}); + +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, focusEvent, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, focusEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + callbackfunc, + handlerFunc: focusEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_blur_callback_on_thread__proxy: 'sync', + emscripten_set_blur_callback_on_thread__deps: ['$registerFocusEventCallback'], + emscripten_set_blur_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_BLUR }}}, "blur", targetThread), + + emscripten_set_focus_callback_on_thread__proxy: 'sync', + emscripten_set_focus_callback_on_thread__deps: ['$registerFocusEventCallback'], + emscripten_set_focus_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_FOCUS }}}, "focus", targetThread), + + emscripten_set_focusin_callback_on_thread__proxy: 'sync', + emscripten_set_focusin_callback_on_thread__deps: ['$registerFocusEventCallback'], + emscripten_set_focusin_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_FOCUSIN }}}, "focusin", targetThread), + + emscripten_set_focusout_callback_on_thread__proxy: 'sync', + emscripten_set_focusout_callback_on_thread__deps: ['$registerFocusEventCallback'], + emscripten_set_focusout_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_FOCUSOUT }}}, "focusout", targetThread), + + $fillDeviceOrientationEventData: (eventStruct, e, target) => { + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceOrientationEvent.alpha, 'e.alpha', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceOrientationEvent.beta, 'e.beta', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceOrientationEvent.gamma, 'e.gamma', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceOrientationEvent.absolute, 'e.absolute', 'i8') }}}; + }, + + $registerDeviceOrientationEventCallback__noleakcheck: true, + $registerDeviceOrientationEventCallback__deps: ['$JSEvents', '$fillDeviceOrientationEventData', '$findEventTarget'], + $registerDeviceOrientationEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.deviceOrientationEvent ||= _malloc({{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}); + + var deviceOrientationEventHandlerFunc = (e = event) => { + fillDeviceOrientationEventData(JSEvents.deviceOrientationEvent, e, target); // TODO: Thread-safety with respect to emscripten_get_deviceorientation_status() + +#if PTHREADS + if (targetThread) { + var deviceOrientationEvent = _malloc({{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}); + fillDeviceOrientationEventData(deviceOrientationEvent, e, target); + __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, deviceOrientationEvent, userData); + } else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, JSEvents.deviceOrientationEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + callbackfunc, + handlerFunc: deviceOrientationEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_deviceorientation_callback_on_thread__proxy: 'sync', + emscripten_set_deviceorientation_callback_on_thread__deps: ['$registerDeviceOrientationEventCallback'], + emscripten_set_deviceorientation_callback_on_thread: (userData, useCapture, callbackfunc, targetThread) => { + return registerDeviceOrientationEventCallback({{{ cDefs.EMSCRIPTEN_EVENT_TARGET_WINDOW }}}, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_DEVICEORIENTATION }}}, "deviceorientation", targetThread); + }, + + emscripten_get_deviceorientation_status__proxy: 'sync', + emscripten_get_deviceorientation_status__deps: ['$JSEvents'], + emscripten_get_deviceorientation_status: (orientationState) => { + if (!JSEvents.deviceOrientationEvent) return {{{ cDefs.EMSCRIPTEN_RESULT_NO_DATA }}}; + // 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, {{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $fillDeviceMotionEventData: (eventStruct, e, target) => { + var supportedFields = 0; + var a = e['acceleration']; + supportedFields |= a && {{{ cDefs.EMSCRIPTEN_DEVICE_MOTION_EVENT_SUPPORTS_ACCELERATION }}}; + var ag = e['accelerationIncludingGravity']; + supportedFields |= ag && {{{ cDefs.EMSCRIPTEN_DEVICE_MOTION_EVENT_SUPPORTS_ACCELERATION_INCLUDING_GRAVITY }}}; + var rr = e['rotationRate']; + supportedFields |= rr && {{{ cDefs.EMSCRIPTEN_DEVICE_MOTION_EVENT_SUPPORTS_ROTATION_RATE }}}; + a = a || {}; + ag = ag || {}; + rr = rr || {}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationX, 'a["x"]', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationY, 'a["y"]', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationZ, 'a["z"]', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityX, 'ag["x"]', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityY, 'ag["y"]', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityZ, 'ag["z"]', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateAlpha, 'rr["alpha"]', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateBeta, 'rr["beta"]', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateGamma, 'rr["gamma"]', 'double') }}}; + }, + + $registerDeviceMotionEventCallback__noleakcheck: true, + $registerDeviceMotionEventCallback__deps: ['$JSEvents', '$fillDeviceMotionEventData', '$findEventTarget', 'malloc'], + $registerDeviceMotionEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.deviceMotionEvent ||= _malloc({{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}); + + var deviceMotionEventHandlerFunc = (e = event) => { + fillDeviceMotionEventData(JSEvents.deviceMotionEvent, e, target); // TODO: Thread-safety with respect to emscripten_get_devicemotion_status() + +#if PTHREADS + if (targetThread) { + var deviceMotionEvent = _malloc({{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}); + fillDeviceMotionEventData(deviceMotionEvent, e, target); + __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, deviceMotionEvent, userData); + } else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, JSEvents.deviceMotionEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + callbackfunc, + handlerFunc: deviceMotionEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_devicemotion_callback_on_thread__proxy: 'sync', + emscripten_set_devicemotion_callback_on_thread__deps: ['$registerDeviceMotionEventCallback'], + emscripten_set_devicemotion_callback_on_thread: (userData, useCapture, callbackfunc, targetThread) => + registerDeviceMotionEventCallback({{{ cDefs.EMSCRIPTEN_EVENT_TARGET_WINDOW }}}, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_DEVICEMOTION }}}, "devicemotion", targetThread), + + emscripten_get_devicemotion_status__proxy: 'sync', + emscripten_get_devicemotion_status__deps: ['$JSEvents'], + emscripten_get_devicemotion_status: (motionState) => { + if (!JSEvents.deviceMotionEvent) return {{{ cDefs.EMSCRIPTEN_RESULT_NO_DATA }}}; + // 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, {{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $screenOrientation: () => { + if (!window.screen) return undefined; + return screen.orientation || screen['mozOrientation'] || screen['webkitOrientation']; + }, + + $fillOrientationChangeEventData__deps: ['$screenOrientation'], + $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 = {{{ cDefs.EMSCRIPTEN_ORIENTATION_UNSUPPORTED }}}; + 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; + } +#if MIN_SAFARI_VERSION < 160400 + else { + // fallback for Safari earlier than 16.4 (March 2023) + orientationAngle = window.orientation; + } +#endif + + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenOrientationChangeEvent.orientationIndex, 'orientationIndex', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenOrientationChangeEvent.orientationAngle, 'orientationAngle', 'i32') }}}; + }, + + $registerOrientationChangeEventCallback__noleakcheck: true, + $registerOrientationChangeEventCallback__deps: ['$JSEvents', '$fillOrientationChangeEventData', 'malloc'], + $registerOrientationChangeEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.orientationChangeEvent ||= _malloc({{{ C_STRUCTS.EmscriptenOrientationChangeEvent.__size__ }}}); + + var orientationChangeEventHandlerFunc = (e = event) => { +#if PTHREADS + var orientationChangeEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenOrientationChangeEvent.__size__ }}}) : JSEvents.orientationChangeEvent; +#else + var orientationChangeEvent = JSEvents.orientationChangeEvent; +#endif + + fillOrientationChangeEventData(orientationChangeEvent); + +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, orientationChangeEvent, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, orientationChangeEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: orientationChangeEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_orientationchange_callback_on_thread__proxy: 'sync', + emscripten_set_orientationchange_callback_on_thread__deps: ['$registerOrientationChangeEventCallback'], + emscripten_set_orientationchange_callback_on_thread: (userData, useCapture, callbackfunc, targetThread) => { + if (!window.screen || !screen.orientation) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + return registerOrientationChangeEventCallback(screen.orientation, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_ORIENTATIONCHANGE }}}, 'change', targetThread); + }, + + emscripten_get_orientation_status__proxy: 'sync', + emscripten_get_orientation_status__deps: ['$fillOrientationChangeEventData', '$screenOrientation'], + emscripten_get_orientation_status: (orientationChangeEvent) => { + // screenOrientation() resolving standard, window.orientation being the deprecated mobile-only + if (!screenOrientation() && typeof orientation == 'undefined') return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + fillOrientationChangeEventData(orientationChangeEvent); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_lock_orientation__proxy: 'sync', + 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 {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + if (succeeded) { + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + } + return {{{ cDefs.EMSCRIPTEN_RESULT_FAILED }}}; + }, + + emscripten_unlock_orientation__proxy: 'sync', + emscripten_unlock_orientation: () => { + if (screen.unlockOrientation) { + screen.unlockOrientation(); + } else if (screen.mozUnlockOrientation) { + screen.mozUnlockOrientation(); + } else if (screen.webkitUnlockOrientation) { + screen.webkitUnlockOrientation(); + } else { + return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $fillFullscreenChangeEventData__deps: ['$JSEvents', '$stringToUTF8', '$getFullscreenElement'], + $fillFullscreenChangeEventData: (eventStruct) => { + var fullscreenElement = getFullscreenElement(); + var isFullscreen = !!fullscreenElement; + // Assigning a boolean to HEAP32 with expected type coercion. + /** @suppress{checkTypes} */ + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.isFullscreen, 'isFullscreen', 'i8') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.fullscreenEnabled, 'JSEvents.fullscreenEnabled()', 'i8') }}}; + // 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 + {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.nodeName }}}, {{{ cDefs.EM_HTML5_LONG_STRING_LEN_BYTES }}}); + stringToUTF8(id, eventStruct + {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.id }}}, {{{ cDefs.EM_HTML5_LONG_STRING_LEN_BYTES }}}); + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.elementWidth, 'reportedElement ? reportedElement.clientWidth : 0', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.elementHeight, 'reportedElement ? reportedElement.clientHeight : 0', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.screenWidth, 'screen.width', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.screenHeight, 'screen.height', 'i32') }}}; + if (isFullscreen) { + JSEvents.previousFullscreenElement = fullscreenElement; + } + }, + + $registerFullscreenChangeEventCallback__noleakcheck: true, + $registerFullscreenChangeEventCallback__deps: ['$JSEvents', '$fillFullscreenChangeEventData', 'malloc'], + $registerFullscreenChangeEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.fullscreenChangeEvent ||= _malloc({{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.__size__ }}}); + + var fullscreenChangeEventhandlerFunc = (e = event) => { +#if PTHREADS + var fullscreenChangeEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.__size__ }}}) : JSEvents.fullscreenChangeEvent; +#else + var fullscreenChangeEvent = JSEvents.fullscreenChangeEvent; +#endif + + fillFullscreenChangeEventData(fullscreenChangeEvent); + +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, fullscreenChangeEvent, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, fullscreenChangeEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: fullscreenChangeEventhandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_fullscreenchange_callback_on_thread__proxy: 'sync', + emscripten_set_fullscreenchange_callback_on_thread__deps: ['$JSEvents', '$registerFullscreenChangeEventCallback', '$findEventTarget', +#if !DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + '$specialHTMLTargets' +#endif + ], + emscripten_set_fullscreenchange_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => { + if (!JSEvents.fullscreenEnabled()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; +#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + target = findEventTarget(target); +#else + target = target ? findEventTarget(target) : specialHTMLTargets[{{{ cDefs.EMSCRIPTEN_EVENT_TARGET_DOCUMENT }}}]; +#endif + if (!target) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + +#if MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED + // As of Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitfullscreenchange. TODO: revisit this check once Safari ships unprefixed version. + registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_FULLSCREENCHANGE }}}, "webkitfullscreenchange", targetThread); +#endif + + return registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_FULLSCREENCHANGE }}}, "fullscreenchange", targetThread); + }, + + emscripten_get_fullscreen_status__proxy: 'sync', + emscripten_get_fullscreen_status__deps: ['$JSEvents', '$fillFullscreenChangeEventData'], + emscripten_get_fullscreen_status: (fullscreenStatus) => { + if (!JSEvents.fullscreenEnabled()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + fillFullscreenChangeEventData(fullscreenStatus); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $JSEvents_requestFullscreen__deps: ['$JSEvents', '$JSEvents_resizeCanvasForFullscreen'], + $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 != {{{ cDefs.EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT }}} || strategy.canvasResolutionScaleMode != {{{ cDefs.EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE }}}) { + JSEvents_resizeCanvasForFullscreen(target, strategy); + } + + if (target.requestFullscreen) { + target.requestFullscreen(); +#if MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED + } else if (target.webkitRequestFullscreen) { + target.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); +#endif + } else { + return JSEvents.fullscreenEnabled() ? {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_TARGET }}} : {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + + currentFullscreenStrategy = strategy; + + if (strategy.canvasResizedCallback) { +#if PTHREADS + if (strategy.canvasResizedCallbackTargetThread) __emscripten_run_callback_on_thread(strategy.canvasResizedCallbackTargetThread, strategy.canvasResizedCallback, {{{ cDefs.EMSCRIPTEN_EVENT_CANVASRESIZED }}}, 0, strategy.canvasResizedCallbackUserData); + else +#endif + {{{ makeDynCall('iipp', 'strategy.canvasResizedCallback') }}}({{{ cDefs.EMSCRIPTEN_EVENT_CANVASRESIZED }}}, 0, strategy.canvasResizedCallbackUserData); + } + + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $JSEvents_resizeCanvasForFullscreen__deps: ['$registerRestoreOldStyle', '$getCanvasElementSize', '$setLetterbox', '$setCanvasElementSize', '$getBoundingClientRect'], + $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 == {{{ cDefs.EMSCRIPTEN_FULLSCREEN_SCALE_CENTER }}}) { + setLetterbox(target, (cssHeight - windowedCssHeight) / 2, (cssWidth - windowedCssWidth) / 2); + cssWidth = windowedCssWidth; + cssHeight = windowedCssHeight; + } else if (strategy.scaleMode == {{{ cDefs.EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT }}}) { + 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 == {{{ cDefs.EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST }}}) { + 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 == {{{ cDefs.EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF }}}) ? devicePixelRatio : 1; + if (strategy.canvasResolutionScaleMode != {{{ cDefs.EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE }}}) { + 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; + }, + + $registerRestoreOldStyle__deps: ['$getCanvasElementSize', '$setCanvasElementSize', '$currentFullscreenStrategy'], + $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); + +#if MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED + // As of Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitfullscreenchange. TODO: revisit this check once Safari ships unprefixed version. + document.removeEventListener('webkitfullscreenchange', restoreOldStyle); +#endif + + 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) { +#if PTHREADS + if (currentFullscreenStrategy.canvasResizedCallbackTargetThread) __emscripten_run_callback_on_thread(currentFullscreenStrategy.canvasResizedCallbackTargetThread, currentFullscreenStrategy.canvasResizedCallback, {{{ cDefs.EMSCRIPTEN_EVENT_CANVASRESIZED }}}, 0, currentFullscreenStrategy.canvasResizedCallbackUserData); + else +#endif + {{{ makeDynCall('iipp', 'currentFullscreenStrategy.canvasResizedCallback') }}}({{{ cDefs.EMSCRIPTEN_EVENT_CANVASRESIZED }}}, 0, currentFullscreenStrategy.canvasResizedCallbackUserData); + } + } + } + document.addEventListener('fullscreenchange', restoreOldStyle); +#if MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED + // As of Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitfullscreenchange. TODO: revisit this check once Safari ships unprefixed version. + document.addEventListener('webkitfullscreenchange', restoreOldStyle); +#endif + return restoreOldStyle; + }, + + // Walks the DOM tree and hides every element by setting "display: none;" except the given element. + // Returns a list of [{node: element, displayState: oldDisplayStyle}] entries to allow restoring previous + // visibility states after done. + $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; + }, + + // Applies old visibility states, given a list of changes returned by hideEverythingExceptGivenElement(). + $restoreHiddenElements: (hiddenElements) => { + for (var elem of hiddenElements) { + elem.node.style.display = elem.displayState; + } + }, + + // Add letterboxes to a fullscreen element in a cross-browser way. + $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'; + }, + + $currentFullscreenStrategy: {}, + $restoreOldWindowedStyle: null, + + $softFullscreenResizeWebGLRenderTarget__deps: ['$setLetterbox', '$currentFullscreenStrategy', '$getCanvasElementSize', '$setCanvasElementSize', '$jstoi_q'], + $softFullscreenResizeWebGLRenderTarget: () => { + var dpr = devicePixelRatio; + var inHiDPIFullscreenMode = currentFullscreenStrategy.canvasResolutionScaleMode == {{{ cDefs.EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF }}}; + var inAspectRatioFixedFullscreenMode = currentFullscreenStrategy.scaleMode == {{{ cDefs.EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT }}}; + var inPixelPerfectFullscreenMode = currentFullscreenStrategy.canvasResolutionScaleMode != {{{ cDefs.EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE }}}; + var inCenteredWithoutScalingFullscreenMode = currentFullscreenStrategy.scaleMode == {{{ cDefs.EMSCRIPTEN_FULLSCREEN_SCALE_CENTER }}}; + 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) { +#if PTHREADS + if (currentFullscreenStrategy.canvasResizedCallbackTargetThread) __emscripten_run_callback_on_thread(currentFullscreenStrategy.canvasResizedCallbackTargetThread, currentFullscreenStrategy.canvasResizedCallback, {{{ cDefs.EMSCRIPTEN_EVENT_CANVASRESIZED }}}, 0, currentFullscreenStrategy.canvasResizedCallbackUserData); + else +#endif + {{{ makeDynCall('iipp', 'currentFullscreenStrategy.canvasResizedCallback') }}}({{{ cDefs.EMSCRIPTEN_EVENT_CANVASRESIZED }}}, 0, currentFullscreenStrategy.canvasResizedCallbackUserData); + } + }, + + // https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode + $doRequestFullscreen__deps: ['$JSEvents', '$JSEvents_requestFullscreen', '$findEventTarget'], + $doRequestFullscreen: (target, strategy) => { + if (!JSEvents.fullscreenEnabled()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; +#if !DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + target ||= '#canvas'; +#endif + target = findEventTarget(target); + if (!target) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + + if (!target.requestFullscreen +#if MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED + && !target.webkitRequestFullscreen +#endif + ) { + return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_TARGET }}}; + } + +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + // 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 {{{ cDefs.EMSCRIPTEN_RESULT_DEFERRED }}}; + } + return {{{ cDefs.EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED }}}; + } +#endif + + return JSEvents_requestFullscreen(target, strategy); + }, + + emscripten_request_fullscreen__deps: ['$doRequestFullscreen'], + emscripten_request_fullscreen__proxy: 'sync', + emscripten_request_fullscreen: (target, deferUntilInEventHandler) => { + var strategy = { + // These options perform no added logic, but just bare request fullscreen. + scaleMode: {{{ cDefs.EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT }}}, + canvasResolutionScaleMode: {{{ cDefs.EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE }}}, + filteringMode: {{{ cDefs.EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT }}}, +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + deferUntilInEventHandler, +#endif + canvasResizedCallbackTargetThread: {{{ cDefs.EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD }}} + }; + return doRequestFullscreen(target, strategy); + }, + + emscripten_request_fullscreen_strategy__deps: ['$doRequestFullscreen'], + emscripten_request_fullscreen_strategy__proxy: 'sync', + emscripten_request_fullscreen_strategy: (target, deferUntilInEventHandler, fullscreenStrategy) => { + var strategy = { + scaleMode: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.scaleMode, 'i32') }}}, + canvasResolutionScaleMode: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResolutionScaleMode, 'i32') }}}, + filteringMode: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.filteringMode, 'i32') }}}, +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + deferUntilInEventHandler, +#endif +#if PTHREADS + canvasResizedCallbackTargetThread: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResizedCallbackTargetThread, 'i32') }}}, +#endif + canvasResizedCallback: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResizedCallback, 'i32') }}}, + canvasResizedCallbackUserData: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResizedCallbackUserData, 'i32') }}} + }; + + return doRequestFullscreen(target, strategy); + }, + + emscripten_enter_soft_fullscreen__deps: ['$JSEvents', '$hideEverythingExceptGivenElement', '$restoreOldWindowedStyle', '$restoreHiddenElements', '$currentFullscreenStrategy', '$softFullscreenResizeWebGLRenderTarget', '$JSEvents_resizeCanvasForFullscreen', '$findEventTarget'], + emscripten_enter_soft_fullscreen__proxy: 'sync', + emscripten_enter_soft_fullscreen: (target, fullscreenStrategy) => { +#if !DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + target ||= '#canvas'; +#endif + target = findEventTarget(target); + if (!target) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + + var strategy = { + scaleMode: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.scaleMode, 'i32') }}}, + canvasResolutionScaleMode: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResolutionScaleMode, 'i32') }}}, + filteringMode: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.filteringMode, 'i32') }}}, + canvasResizedCallback: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResizedCallback, 'i32') }}}, + canvasResizedCallbackUserData: {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResizedCallbackUserData, 'i32') }}}, +#if PTHREADS + canvasResizedCallbackTargetThread: JSEvents.getTargetThreadForEventCallback(), +#endif + 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) { +#if PTHREADS + if (strategy.canvasResizedCallbackTargetThread) __emscripten_run_callback_on_thread(strategy.canvasResizedCallbackTargetThread, strategy.canvasResizedCallback, {{{ cDefs.EMSCRIPTEN_EVENT_CANVASRESIZED }}}, 0, strategy.canvasResizedCallbackUserData); + else +#endif + {{{ makeDynCall('iipp', 'strategy.canvasResizedCallback') }}}({{{ cDefs.EMSCRIPTEN_EVENT_CANVASRESIZED }}}, 0, strategy.canvasResizedCallbackUserData); + } + currentFullscreenStrategy = 0; + } + restoreOldWindowedStyle = restoreWindowedState; + currentFullscreenStrategy = strategy; + addEventListener('resize', softFullscreenResizeWebGLRenderTarget); + + // Inform the caller that the canvas size has changed. + if (strategy.canvasResizedCallback) { +#if PTHREADS + if (strategy.canvasResizedCallbackTargetThread) __emscripten_run_callback_on_thread(strategy.canvasResizedCallbackTargetThread, strategy.canvasResizedCallback, {{{ cDefs.EMSCRIPTEN_EVENT_CANVASRESIZED }}}, 0, strategy.canvasResizedCallbackUserData); + else +#endif + {{{ makeDynCall('iipp', 'strategy.canvasResizedCallback') }}}({{{ cDefs.EMSCRIPTEN_EVENT_CANVASRESIZED }}}, 0, strategy.canvasResizedCallbackUserData); + } + + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_exit_soft_fullscreen__deps: ['$restoreOldWindowedStyle'], + emscripten_exit_soft_fullscreen__proxy: 'sync', + emscripten_exit_soft_fullscreen: () => { + restoreOldWindowedStyle?.(); + restoreOldWindowedStyle = null; + + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_exit_fullscreen__deps: [ + '$JSEvents', + '$specialHTMLTargets', +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + '$JSEvents_requestFullscreen', +#endif + ], + emscripten_exit_fullscreen__proxy: 'sync', + emscripten_exit_fullscreen: () => { + if (!JSEvents.fullscreenEnabled()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + // Make sure no queued up calls will fire after this. + JSEvents.removeDeferredCalls(JSEvents_requestFullscreen); +#endif + + var d = specialHTMLTargets[{{{ cDefs.EMSCRIPTEN_EVENT_TARGET_DOCUMENT }}}]; + if (d.exitFullscreen) { + d.fullscreenElement && d.exitFullscreen(); +#if MIN_SAFARI_VERSION != TARGET_NOT_SUPPORTED // https://caniuse.com/#feat=mdn-api_document_exitfullscreen + } else if (d.webkitExitFullscreen) { + d.webkitFullscreenElement && d.webkitExitFullscreen(); +#endif + } else { + return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $fillPointerlockChangeEventData__deps: ['$JSEvents', '$stringToUTF8'], + $fillPointerlockChangeEventData: (eventStruct) => { + var pointerLockElement = document.pointerLockElement; + var isPointerlocked = !!pointerLockElement; + // Assigning a boolean to HEAP32 with expected type coercion. + /** @suppress{checkTypes} */ + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenPointerlockChangeEvent.isActive, 'isPointerlocked', 'i8') }}}; + var nodeName = JSEvents.getNodeNameForTarget(pointerLockElement); + var id = pointerLockElement?.id || ''; + stringToUTF8(nodeName, eventStruct + {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.nodeName }}}, {{{ cDefs.EM_HTML5_LONG_STRING_LEN_BYTES }}}); + stringToUTF8(id, eventStruct + {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.id }}}, {{{ cDefs.EM_HTML5_LONG_STRING_LEN_BYTES }}}); + }, + + $registerPointerlockChangeEventCallback__noleakcheck: true, + $registerPointerlockChangeEventCallback__deps: ['$JSEvents', '$fillPointerlockChangeEventData', 'malloc'], + $registerPointerlockChangeEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.pointerlockChangeEvent ||= _malloc({{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.__size__ }}}); + + var pointerlockChangeEventHandlerFunc = (e = event) => { +#if PTHREADS + var pointerlockChangeEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.__size__ }}}) : JSEvents.pointerlockChangeEvent; +#else + var pointerlockChangeEvent = JSEvents.pointerlockChangeEvent; +#endif + fillPointerlockChangeEventData(pointerlockChangeEvent); + +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, pointerlockChangeEvent, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, pointerlockChangeEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: pointerlockChangeEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_pointerlockchange_callback_on_thread__proxy: 'sync', + emscripten_set_pointerlockchange_callback_on_thread__deps: ['$registerPointerlockChangeEventCallback', '$findEventTarget', +#if !DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + '$specialHTMLTargets' +#endif + ], + emscripten_set_pointerlockchange_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => { + if (!document.body?.requestPointerLock) { + return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + +#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + target = findEventTarget(target); +#else + target = target ? findEventTarget(target) : specialHTMLTargets[{{{ cDefs.EMSCRIPTEN_EVENT_TARGET_DOCUMENT }}}]; // Pointer lock change events need to be captured from 'document' by default instead of 'window' +#endif + if (!target) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + return registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_POINTERLOCKCHANGE }}}, "pointerlockchange", targetThread); + }, + + $registerPointerlockErrorEventCallback__deps: ['$JSEvents'], + $registerPointerlockErrorEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + + var pointerlockErrorEventHandlerFunc = (e = event) => { +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, 0, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, 0, userData)) e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: pointerlockErrorEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_pointerlockerror_callback_on_thread__proxy: 'sync', + emscripten_set_pointerlockerror_callback_on_thread__deps: ['$registerPointerlockErrorEventCallback', '$findEventTarget', +#if !DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + '$specialHTMLTargets' +#endif + ], + emscripten_set_pointerlockerror_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => { + if (!document.body?.requestPointerLock) { + return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + +#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + target = findEventTarget(target); +#else + target = target ? findEventTarget(target) : specialHTMLTargets[{{{ cDefs.EMSCRIPTEN_EVENT_TARGET_DOCUMENT }}}]; // Pointer lock change events need to be captured from 'document' by default instead of 'window' +#endif + + if (!target) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + return registerPointerlockErrorEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_POINTERLOCKERROR }}}, "pointerlockerror", targetThread); + }, + + emscripten_get_pointerlock_status__proxy: 'sync', + emscripten_get_pointerlock_status__deps: ['$fillPointerlockChangeEventData'], + emscripten_get_pointerlock_status: (pointerlockStatus) => { + if (pointerlockStatus) fillPointerlockChangeEventData(pointerlockStatus); + if (!document.body?.requestPointerLock) { + return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $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 {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_TARGET }}}; + } + return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_request_pointerlock__proxy: 'sync', + emscripten_request_pointerlock__deps: ['$requestPointerLock', '$findEventTarget', +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + '$JSEvents', +#endif + ], + emscripten_request_pointerlock: (target, deferUntilInEventHandler) => { +#if !DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + target ||= '#canvas'; +#endif + target = findEventTarget(target); + if (!target) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + if (!target.requestPointerLock) { + return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + // 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 {{{ cDefs.EMSCRIPTEN_RESULT_DEFERRED }}}; + } + return {{{ cDefs.EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED }}}; + } +#endif + + return requestPointerLock(target); + }, + +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + emscripten_exit_pointerlock__deps: ['$JSEvents', '$requestPointerLock'], +#endif + emscripten_exit_pointerlock__proxy: 'sync', + emscripten_exit_pointerlock: () => { +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + // Make sure no queued up calls will fire after this. + JSEvents.removeDeferredCalls(requestPointerLock); +#endif + if (!document.exitPointerLock) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + document.exitPointerLock(); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_vibrate__proxy: 'sync', + emscripten_vibrate: (msecs) => { + if (!navigator.vibrate) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + navigator.vibrate(msecs); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_vibrate_pattern__proxy: 'sync', + emscripten_vibrate_pattern: (msecsArray, numEntries) => { + if (!navigator.vibrate) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + + var vibrateList = []; + for (var i = 0; i < numEntries; ++i) { + var msecs = {{{ makeGetValue('msecsArray', 'i*4', 'i32') }}}; + vibrateList.push(msecs); + } + navigator.vibrate(vibrateList); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $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} */ + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.hidden, 'document.hidden', 'i8') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.visibilityState, 'visibilityState', 'i32') }}}; + }, + + $registerVisibilityChangeEventCallback__noleakcheck: true, + $registerVisibilityChangeEventCallback__deps: ['$JSEvents', '$fillVisibilityChangeEventData', 'malloc'], + $registerVisibilityChangeEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.visibilityChangeEvent ||= _malloc({{{ C_STRUCTS.EmscriptenVisibilityChangeEvent.__size__ }}}); + + var visibilityChangeEventHandlerFunc = (e = event) => { +#if PTHREADS + var visibilityChangeEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenVisibilityChangeEvent.__size__ }}}) : JSEvents.visibilityChangeEvent; +#else + var visibilityChangeEvent = JSEvents.visibilityChangeEvent; +#endif + + fillVisibilityChangeEventData(visibilityChangeEvent); + +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, visibilityChangeEvent, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, visibilityChangeEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: visibilityChangeEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_visibilitychange_callback_on_thread__proxy: 'sync', + emscripten_set_visibilitychange_callback_on_thread__deps: ['$registerVisibilityChangeEventCallback', '$specialHTMLTargets'], + emscripten_set_visibilitychange_callback_on_thread: (userData, useCapture, callbackfunc, targetThread) => { +#if ENVIRONMENT_MAY_BE_WORKER || ENVIRONMENT_MAY_BE_NODE || ENVIRONMENT_MAY_BE_SHELL + if (!specialHTMLTargets[{{{ cDefs.EMSCRIPTEN_EVENT_TARGET_DOCUMENT }}}]) { + return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + } +#endif + return registerVisibilityChangeEventCallback(specialHTMLTargets[{{{ cDefs.EMSCRIPTEN_EVENT_TARGET_DOCUMENT }}}], userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_VISIBILITYCHANGE }}}, "visibilitychange", targetThread); + }, + + emscripten_get_visibility_status__proxy: 'sync', + emscripten_get_visibility_status__deps: ['$fillVisibilityChangeEventData'], + emscripten_get_visibility_status: (visibilityStatus) => { + if (typeof document.visibilityState == 'undefined' && typeof document.hidden == 'undefined') { + return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } + fillVisibilityChangeEventData(visibilityStatus); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $registerTouchEventCallback__noleakcheck: true, + $registerTouchEventCallback__deps: ['$JSEvents', '$findEventTarget', '$getBoundingClientRect', 'malloc'], + $registerTouchEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.touchEvent ||= _malloc({{{ C_STRUCTS.EmscriptenTouchEvent.__size__ }}}); + + target = findEventTarget(target); + + var touchEventHandlerFunc = (e) => { +#if ASSERTIONS + assert(e); +#endif + 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; + } + +#if PTHREADS + var touchEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenTouchEvent.__size__ }}}) : JSEvents.touchEvent; +#else + var touchEvent = JSEvents.touchEvent; +#endif + {{{ makeSetValue('touchEvent', C_STRUCTS.EmscriptenTouchEvent.timestamp, 'e.timeStamp', 'double') }}}; + HEAP8[touchEvent + {{{ C_STRUCTS.EmscriptenTouchEvent.ctrlKey }}}] = e.ctrlKey; + HEAP8[touchEvent + {{{ C_STRUCTS.EmscriptenTouchEvent.shiftKey }}}] = e.shiftKey; + HEAP8[touchEvent + {{{ C_STRUCTS.EmscriptenTouchEvent.altKey }}}] = e.altKey; + HEAP8[touchEvent + {{{ C_STRUCTS.EmscriptenTouchEvent.metaKey }}}] = e.metaKey; + var idx = touchEvent + {{{ C_STRUCTS.EmscriptenTouchEvent.touches }}}; +#if !DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + var canvasRect = Module['canvas'] ? getBoundingClientRect(Module['canvas']) : undefined; +#endif + var targetRect = getBoundingClientRect(target); + var numTouches = 0; + for (let t of Object.values(touches)) { + var idx32 = {{{ getHeapOffset('idx', 'i32') }}}; // Pre-shift the ptr to index to HEAP32 to save code size + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.identifier / 4 }}}] = t.identifier; + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.screenX / 4 }}}] = t.screenX; + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.screenY / 4 }}}] = t.screenY; + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.clientX / 4 }}}] = t.clientX; + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.clientY / 4 }}}] = t.clientY; + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.pageX / 4 }}}] = t.pageX; + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.pageY / 4 }}}] = t.pageY; + HEAP8[idx + {{{ C_STRUCTS.EmscriptenTouchPoint.isChanged }}}] = t.isChanged; + HEAP8[idx + {{{ C_STRUCTS.EmscriptenTouchPoint.onTarget }}}] = t.onTarget; + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.targetX / 4 }}}] = t.clientX - (targetRect.left | 0); + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.targetY / 4 }}}] = t.clientY - (targetRect.top | 0); +#if !DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.canvasX / 4 }}}] = canvasRect ? t.clientX - (canvasRect.left | 0) : 0; + HEAP32[idx32 + {{{ C_STRUCTS.EmscriptenTouchPoint.canvasY / 4 }}}] = canvasRect ? t.clientY - (canvasRect.top | 0) : 0; +#endif + + idx += {{{ C_STRUCTS.EmscriptenTouchPoint.__size__ }}}; + + if (++numTouches > 31) { + break; + } + } + {{{ makeSetValue('touchEvent', C_STRUCTS.EmscriptenTouchEvent.numTouches, 'numTouches', 'i32') }}}; + +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, touchEvent, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, touchEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target, +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + allowsDeferredCalls: eventTypeString == 'touchstart' || eventTypeString == 'touchend', +#endif + eventTypeString, + callbackfunc, + handlerFunc: touchEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_touchstart_callback_on_thread__proxy: 'sync', + emscripten_set_touchstart_callback_on_thread__deps: ['$registerTouchEventCallback'], + emscripten_set_touchstart_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_TOUCHSTART }}}, "touchstart", targetThread), + + emscripten_set_touchend_callback_on_thread__proxy: 'sync', + emscripten_set_touchend_callback_on_thread__deps: ['$registerTouchEventCallback'], + emscripten_set_touchend_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_TOUCHEND }}}, "touchend", targetThread), + + emscripten_set_touchmove_callback_on_thread__proxy: 'sync', + emscripten_set_touchmove_callback_on_thread__deps: ['$registerTouchEventCallback'], + emscripten_set_touchmove_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_TOUCHMOVE }}}, "touchmove", targetThread), + + emscripten_set_touchcancel_callback_on_thread__proxy: 'sync', + emscripten_set_touchcancel_callback_on_thread__deps: ['$registerTouchEventCallback'], + emscripten_set_touchcancel_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => + registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_TOUCHCANCEL }}}, "touchcancel", targetThread), + + $fillGamepadEventData__deps: ['$stringToUTF8'], + $fillGamepadEventData: (eventStruct, e) => { + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.timestamp, 'e.timestamp', 'double') }}}; + for (var i = 0; i < e.axes.length; ++i) { + {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.EmscriptenGamepadEvent.axis, 'e.axes[i]', 'double') }}}; + } + for (var i = 0; i < e.buttons.length; ++i) { + if (typeof e.buttons[i] == 'object') { + {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.EmscriptenGamepadEvent.analogButton, 'e.buttons[i].value', 'double') }}}; + } else { + {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.EmscriptenGamepadEvent.analogButton, 'e.buttons[i]', 'double') }}}; + } + } + for (var i = 0; i < e.buttons.length; ++i) { + if (typeof e.buttons[i] == 'object') { + {{{ makeSetValue('eventStruct+i', C_STRUCTS.EmscriptenGamepadEvent.digitalButton, 'e.buttons[i].pressed', 'i8') }}}; + } else { + // Assigning a boolean to HEAP32, that's ok, but Closure would like to warn about it: + /** @suppress {checkTypes} */ + {{{ makeSetValue('eventStruct+i', C_STRUCTS.EmscriptenGamepadEvent.digitalButton, 'e.buttons[i] == 1', 'i8') }}}; + } + } + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.connected, 'e.connected', 'i8') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.index, 'e.index', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.numAxes, 'e.axes.length', 'i32') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.numButtons, 'e.buttons.length', 'i32') }}}; + stringToUTF8(e.id, eventStruct + {{{ C_STRUCTS.EmscriptenGamepadEvent.id }}}, {{{ cDefs.EM_HTML5_MEDIUM_STRING_LEN_BYTES }}}); + stringToUTF8(e.mapping, eventStruct + {{{ C_STRUCTS.EmscriptenGamepadEvent.mapping }}}, {{{ cDefs.EM_HTML5_MEDIUM_STRING_LEN_BYTES }}}); + }, + + $registerGamepadEventCallback__noleakcheck: true, + $registerGamepadEventCallback__deps: ['$JSEvents', '$fillGamepadEventData', '$findEventTarget', 'malloc'], + $registerGamepadEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.gamepadEvent ||= _malloc({{{ C_STRUCTS.EmscriptenGamepadEvent.__size__ }}}); + + var gamepadEventHandlerFunc = (e = event) => { +#if PTHREADS + var gamepadEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenGamepadEvent.__size__ }}}) : JSEvents.gamepadEvent; +#else + var gamepadEvent = JSEvents.gamepadEvent; +#endif + fillGamepadEventData(gamepadEvent, e["gamepad"]); + +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, gamepadEvent, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, gamepadEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), +#if HTML5_SUPPORT_DEFERRING_USER_SENSITIVE_REQUESTS + allowsDeferredCalls: true, +#endif + eventTypeString, + callbackfunc, + handlerFunc: gamepadEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_gamepadconnected_callback_on_thread__proxy: 'sync', + emscripten_set_gamepadconnected_callback_on_thread__deps: ['$registerGamepadEventCallback', 'emscripten_sample_gamepad_data'], + emscripten_set_gamepadconnected_callback_on_thread: (userData, useCapture, callbackfunc, targetThread) => { + if (_emscripten_sample_gamepad_data()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + return registerGamepadEventCallback({{{ cDefs.EMSCRIPTEN_EVENT_TARGET_WINDOW }}}, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_GAMEPADCONNECTED }}}, "gamepadconnected", targetThread); + }, + + emscripten_set_gamepaddisconnected_callback_on_thread__proxy: 'sync', + emscripten_set_gamepaddisconnected_callback_on_thread__deps: ['$registerGamepadEventCallback', 'emscripten_sample_gamepad_data'], + emscripten_set_gamepaddisconnected_callback_on_thread: (userData, useCapture, callbackfunc, targetThread) => { + if (_emscripten_sample_gamepad_data()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + return registerGamepadEventCallback({{{ cDefs.EMSCRIPTEN_EVENT_TARGET_WINDOW }}}, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED }}}, "gamepaddisconnected", targetThread); + }, + + emscripten_sample_gamepad_data__docs: '/** @suppress {checkTypes} */', // We assign null to navigator.getGamepads, which Closure would like to complain about. + emscripten_sample_gamepad_data__proxy: 'sync', + emscripten_sample_gamepad_data__deps: ['$JSEvents'], + emscripten_sample_gamepad_data: () => { + try { + if (navigator.getGamepads) return (JSEvents.lastGamepadState = navigator.getGamepads()) + ? {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}} : {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + } catch(e) { +#if ASSERTIONS + err(`navigator.getGamepads() exists, but failed to execute with exception ${e}. Disabling Gamepad access.`); +#endif + navigator.getGamepads = null; // Disable getGamepads() so that it won't be attempted to be used again. + } + return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + }, + + emscripten_get_num_gamepads__proxy: 'sync', + emscripten_get_num_gamepads__deps: ['$JSEvents'], + emscripten_get_num_gamepads: () => { +#if ASSERTIONS + assert(JSEvents.lastGamepadState, 'emscripten_get_num_gamepads() can only be called after having first called emscripten_sample_gamepad_data() and that function has returned EMSCRIPTEN_RESULT_SUCCESS!'); +#endif + // 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_gamepad_status__proxy: 'sync', + emscripten_get_gamepad_status__deps: ['$JSEvents', '$fillGamepadEventData'], + emscripten_get_gamepad_status: (index, gamepadState) => { +#if ASSERTIONS + assert(JSEvents.lastGamepadState, 'emscripten_get_gamepad_status() can only be called after having first called emscripten_sample_gamepad_data() and that function has returned EMSCRIPTEN_RESULT_SUCCESS!'); +#endif + // INVALID_PARAM is returned on a Gamepad index that never was there. + if (index < 0 || index >= JSEvents.lastGamepadState.length) return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}}; + + // 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 {{{ cDefs.EMSCRIPTEN_RESULT_NO_DATA }}}; + + fillGamepadEventData(gamepadState, JSEvents.lastGamepadState[index]); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $registerBeforeUnloadEventCallback__deps: ['$JSEvents', '$findEventTarget'], + $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 = {{{ makeDynCall('pipp', 'callbackfunc') }}}(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); + }, + + emscripten_set_beforeunload_callback_on_thread__proxy: 'sync', + emscripten_set_beforeunload_callback_on_thread__deps: ['$registerBeforeUnloadEventCallback'], + emscripten_set_beforeunload_callback_on_thread: (userData, callbackfunc, targetThread) => { + if (typeof onbeforeunload == 'undefined') return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + // 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 !== {{{ cDefs.EM_CALLBACK_THREAD_CONTEXT_MAIN_RUNTIME_THREAD }}}) return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}}; + return registerBeforeUnloadEventCallback({{{ cDefs.EMSCRIPTEN_EVENT_TARGET_WINDOW }}}, userData, true, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_BEFOREUNLOAD }}}, "beforeunload"); + }, + + $fillBatteryEventData: (eventStruct, battery) => { + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.chargingTime, 'battery.chargingTime', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.dischargingTime, 'battery.dischargingTime', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.level, 'battery.level', 'double') }}}; + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.charging, 'battery.charging', 'i8') }}}; + }, + + $hasBatteryAPI: () => typeof navigator != 'undefined' && navigator.getBattery, + $hasBatteryAPI__internal: true, + + $registerBatteryEventCallback__noleakcheck: true, + $registerBatteryEventCallback__deps: ['$JSEvents', '$fillBatteryEventData', 'malloc'], + $registerBatteryEventCallback: (battery, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + JSEvents.batteryEvent ||= _malloc({{{ C_STRUCTS.EmscriptenBatteryEvent.__size__ }}}); + + var batteryEventHandlerFunc = (e = event) => { +#if PTHREADS + var batteryEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenBatteryEvent.__size__ }}}) : JSEvents.batteryEvent; +#else + var batteryEvent = JSEvents.batteryEvent; +#endif + fillBatteryEventData(batteryEvent, battery); + +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, batteryEvent, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, batteryEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target: battery, + eventTypeString, + callbackfunc, + handlerFunc: batteryEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_batterychargingchange_callback_on_thread__proxy: 'sync', + emscripten_set_batterychargingchange_callback_on_thread__deps: ['$registerBatteryEventCallback', '$hasBatteryAPI'], + emscripten_set_batterychargingchange_callback_on_thread: (userData, callbackfunc, targetThread) => { + if (!hasBatteryAPI()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + navigator.getBattery().then((b) => { + registerBatteryEventCallback(b, userData, true, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE }}}, "chargingchange", targetThread); + }); + }, + + emscripten_set_batterylevelchange_callback_on_thread__proxy: 'sync', + emscripten_set_batterylevelchange_callback_on_thread__deps: ['$registerBatteryEventCallback', '$hasBatteryAPI'], + emscripten_set_batterylevelchange_callback_on_thread: (userData, callbackfunc, targetThread) => { + if (!hasBatteryAPI()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + navigator.getBattery().then((b) => { + registerBatteryEventCallback(b, userData, true, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE }}}, "levelchange", targetThread); + }); + }, + + $batteryManager: undefined, + $batteryManager__internal: true, + + emscripten_get_battery_status__proxy: 'sync', + emscripten_get_battery_status__deps: ['$fillBatteryEventData', '$hasBatteryAPI', '$batteryManager'], + emscripten_get_battery_status: (batteryState) => { + if (!hasBatteryAPI()) return {{{ cDefs.EMSCRIPTEN_RESULT_NOT_SUPPORTED }}}; + if (!batteryManager) { + navigator.getBattery().then((b) => { + batteryManager = b; + }); + return {{{ cDefs.EMSCRIPTEN_RESULT_NO_DATA }}}; + } + fillBatteryEventData(batteryState, batteryManager); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + +#if PTHREADS + $setCanvasElementSizeCallingThread__deps: [ +#if OFFSCREENCANVAS_SUPPORT + '$setOffscreenCanvasSizeOnTargetThread', +#endif + '$findCanvasEventTarget'], + $setCanvasElementSizeCallingThread: (target, width, height) => { + var canvas = findCanvasEventTarget(target); + if (!canvas) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + +#if OFFSCREENCANVAS_SUPPORT + if (canvas.canvasSharedPtr) { + // N.B. We hold the canvasSharedPtr info structure as the authoritative source for specifying the size of a canvas + // since the actual canvas size changes are asynchronous if the canvas is owned by an OffscreenCanvas on another thread. + // Therefore when setting the size, eagerly set the size of the canvas on the calling thread here, though this thread + // might not be the one that actually ends up specifying the size, but the actual size change may be dispatched + // as an asynchronous event below. + {{{ makeSetValue('canvas.canvasSharedPtr', 0, 'width', 'i32') }}}; + {{{ makeSetValue('canvas.canvasSharedPtr', 4, 'height', 'i32') }}}; + } + + if (canvas.offscreenCanvas || !canvas.controlTransferredOffscreen) { + if (canvas.offscreenCanvas) canvas = canvas.offscreenCanvas; +#else + if (!canvas.controlTransferredOffscreen) { +#endif + var autoResizeViewport = false; + if (canvas.GLctxObject?.GLctx) { + var prevViewport = canvas.GLctxObject.GLctx.getParameter(0xBA2 /* GL_VIEWPORT */); + // TODO: Perhaps autoResizeViewport should only be true if FBO 0 is currently active? + autoResizeViewport = (prevViewport[0] === 0 && prevViewport[1] === 0 && prevViewport[2] === canvas.width && prevViewport[3] === canvas.height); +#if GL_DEBUG + dbg(`Resizing canvas from ${canvas.width}x${canvas.height} to ${width}x${height}. Previous GL viewport size was ${prevViewport}, so autoResizeViewport=${autoResizeViewport}`); +#endif + } + canvas.width = width; + canvas.height = height; + if (autoResizeViewport) { +#if GL_DEBUG + dbg(`Automatically resizing GL viewport to cover whole render target ${width}x${height}`); +#endif + // TODO: Add -sCANVAS_RESIZE_SETS_GL_VIEWPORT=0/1 option (default=1). This is commonly done and several graphics engines depend on this, + // but this can be quite disruptive. + canvas.GLctxObject.GLctx.viewport(0, 0, width, height); + } +#if OFFSCREENCANVAS_SUPPORT + } else if (canvas.canvasSharedPtr) { + var targetThread = {{{ makeGetValue('canvas.canvasSharedPtr', 8, '*') }}}; + setOffscreenCanvasSizeOnTargetThread(targetThread, target, width, height); + return {{{ cDefs.EMSCRIPTEN_RESULT_DEFERRED }}}; // This will have to be done asynchronously +#endif + } else { +#if GL_DEBUG + dbg('canvas.controlTransferredOffscreen but we do not own the canvas, and do not know who has (no canvas.canvasSharedPtr present, an internal bug?)!\n'); +#endif + return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + } +#if OFFSCREEN_FRAMEBUFFER + if (canvas.GLctxObject) GL.resizeOffscreenFramebuffer(canvas.GLctxObject); +#endif + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + +#if OFFSCREENCANVAS_SUPPORT + $setOffscreenCanvasSizeOnTargetThread__deps: ['$stringToNewUTF8', '_emscripten_set_offscreencanvas_size_on_thread'], + $setOffscreenCanvasSizeOnTargetThread: (targetThread, targetCanvas, width, height) => { + targetCanvas = targetCanvas ? UTF8ToString(targetCanvas) : ''; + var targetCanvasPtr = 0; + if (targetCanvas) { + targetCanvasPtr = stringToNewUTF8(targetCanvas); + } + __emscripten_set_offscreencanvas_size_on_thread(targetThread, targetCanvasPtr, width, height); + }, +#endif + + $setCanvasElementSizeMainThread__proxy: 'sync', + $setCanvasElementSizeMainThread__deps: ['$setCanvasElementSizeCallingThread'], + $setCanvasElementSizeMainThread: (target, width, height) => setCanvasElementSizeCallingThread(target, width, height), + + emscripten_set_canvas_element_size__deps: ['$setCanvasElementSizeCallingThread', '$setCanvasElementSizeMainThread', '$findCanvasEventTarget'], + emscripten_set_canvas_element_size: (target, width, height) => { +#if GL_DEBUG + dbg(`emscripten_set_canvas_element_size(target=${target},width=${width},height=${height}`); +#endif + var canvas = findCanvasEventTarget(target); + if (canvas) { + return setCanvasElementSizeCallingThread(target, width, height); + } + return setCanvasElementSizeMainThread(target, width, height); + }, +#else + emscripten_set_canvas_element_size__deps: ['$findCanvasEventTarget'], + emscripten_set_canvas_element_size: (target, width, height) => { +#if GL_DEBUG + dbg(`emscripten_set_canvas_element_size(target=${target},width=${width},height=${height}`); +#endif + var canvas = findCanvasEventTarget(target); + if (!canvas) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + canvas.width = width; + canvas.height = height; +#if OFFSCREEN_FRAMEBUFFER + if (canvas.GLctxObject) GL.resizeOffscreenFramebuffer(canvas.GLctxObject); +#endif + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, +#endif + + $setCanvasElementSize__deps: ['emscripten_set_canvas_element_size', '$stackSave', '$stackRestore', '$stringToUTF8OnStack'], + $setCanvasElementSize: (target, width, height) => { +#if GL_DEBUG + dbg(`setCanvasElementSize(target=${target},width=${width},height=${height}`); +#endif + 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); + } + }, + +#if PTHREADS + $getCanvasSizeCallingThread__deps: ['$findCanvasEventTarget'], + $getCanvasSizeCallingThread: (target, width, height) => { + var canvas = findCanvasEventTarget(target); + if (!canvas) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + +#if OFFSCREENCANVAS_SUPPORT + if (canvas.canvasSharedPtr) { + // N.B. Reading the size of the Canvas takes priority from our shared state structure, which is not the actual size. + // However if is possible that there is a canvas size set event pending on an OffscreenCanvas owned by another thread, + // so that the real sizes of the canvas have not updated yet. Therefore reading the real values would be racy. + var w = {{{ makeGetValue('canvas.canvasSharedPtr', 0, 'i32') }}}; + var h = {{{ makeGetValue('canvas.canvasSharedPtr', 4, 'i32') }}}; + {{{ makeSetValue('width', 0, 'w', 'i32') }}}; + {{{ makeSetValue('height', 0, 'h', 'i32') }}}; + } else if (canvas.offscreenCanvas) { + {{{ makeSetValue('width', 0, 'canvas.offscreenCanvas.width', 'i32') }}}; + {{{ makeSetValue('height', 0, 'canvas.offscreenCanvas.height', 'i32') }}}; + } else +#endif + if (!canvas.controlTransferredOffscreen) { + {{{ makeSetValue('width', 0, 'canvas.width', 'i32') }}}; + {{{ makeSetValue('height', 0, 'canvas.height', 'i32') }}}; + } else { +#if GL_DEBUG + dbg('canvas.controlTransferredOffscreen but we do not own the canvas, and do not know who has (no canvas.canvasSharedPtr present, an internal bug?)!\n'); +#endif + return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + } + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + $getCanvasSizeMainThread__proxy: 'sync', + $getCanvasSizeMainThread__deps: ['$getCanvasSizeCallingThread'], + $getCanvasSizeMainThread: (target, width, height) => getCanvasSizeCallingThread(target, width, height), + + emscripten_get_canvas_element_size__deps: ['$getCanvasSizeCallingThread', '$getCanvasSizeMainThread', '$findCanvasEventTarget'], + emscripten_get_canvas_element_size: (target, width, height) => { + var canvas = findCanvasEventTarget(target); + if (canvas) { + return getCanvasSizeCallingThread(target, width, height); + } + return getCanvasSizeMainThread(target, width, height); + }, +#else + emscripten_get_canvas_element_size__deps: ['$findCanvasEventTarget'], + emscripten_get_canvas_element_size: (target, width, height) => { + var canvas = findCanvasEventTarget(target); + if (!canvas) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + {{{ makeSetValue('width', '0', 'canvas.width', 'i32') }}}; + {{{ makeSetValue('height', '0', 'canvas.height', 'i32') }}}; + }, +#endif + + // JavaScript-friendly API, returns pair [width, height] + $getCanvasElementSize__deps: ['emscripten_get_canvas_element_size', '$stackSave', '$stackRestore', '$stringToUTF8OnStack'], + $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 = [{{{ makeGetValue('w', 0, 'i32')}}}, {{{ makeGetValue('h', 0, 'i32')}}}]; + stackRestore(sp); + return size; + }, + + emscripten_set_element_css_size__proxy: 'sync', + emscripten_set_element_css_size__deps: ['$findEventTarget'], + emscripten_set_element_css_size: (target, width, height) => { +#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + target = findEventTarget(target); +#else + target = target ? findEventTarget(target) : Module['canvas']; +#endif + if (!target) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + + target.style.width = width + "px"; + target.style.height = height + "px"; + + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_get_element_css_size__proxy: 'sync', + emscripten_get_element_css_size__deps: ['$findEventTarget', '$getBoundingClientRect'], + emscripten_get_element_css_size: (target, width, height) => { +#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + target = findEventTarget(target); +#else + target = target ? findEventTarget(target) : Module['canvas']; +#endif + if (!target) return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}}; + + var rect = getBoundingClientRect(target); + {{{ makeSetValue('width', '0', 'rect.width', 'double') }}}; + {{{ makeSetValue('height', '0', 'rect.height', 'double') }}}; + + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_html5_remove_all_event_listeners__deps: ['$JSEvents'], + emscripten_html5_remove_all_event_listeners: () => JSEvents.removeAllEventListeners(), + + emscripten_request_animation_frame: (cb, userData) => + requestAnimationFrame((timeStamp) => {{{ makeDynCall('idp', 'cb') }}}(timeStamp, userData)), + + emscripten_cancel_animation_frame: (id) => cancelAnimationFrame(id), + + emscripten_request_animation_frame_loop: (cb, userData) => { + function tick(timeStamp) { + if ({{{ makeDynCall('idp', 'cb') }}}(timeStamp, userData)) { + requestAnimationFrame(tick); + } + } + return requestAnimationFrame(tick); + }, + + emscripten_get_device_pixel_ratio__proxy: 'sync', + emscripten_get_device_pixel_ratio: () => { +#if ENVIRONMENT_MAY_BE_NODE || ENVIRONMENT_MAY_BE_SHELL + return (typeof devicePixelRatio == 'number' && devicePixelRatio) || 1.0; +#else // otherwise, on the web and in workers, things are simpler + return devicePixelRatio; +#endif + } +}; + +addToLibrary(LibraryHTML5); diff --git a/src/lib/libhtml5_webgl.js b/src/lib/libhtml5_webgl.js new file mode 100644 index 0000000000000..63f8d2ad39624 --- /dev/null +++ b/src/lib/libhtml5_webgl.js @@ -0,0 +1,634 @@ +/** + * @license + * Copyright 2014 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +var LibraryHtml5WebGL = { + // Writes a JS typed array containing 32-bit floats or ints to memory + $writeGLArray: (arr, dst, dstLength, heapType) => { +#if ASSERTIONS + assert(arr); + assert(typeof arr.length != 'undefined'); +#endif + 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 = {{{ getHeapOffset('dst', 'float') }}}; + for (var i = 0; i < writeLength; ++i) { + heap[dst + i] = arr[i]; + } + return len; + }, + + $webglPowerPreferences__internal: true, + $webglPowerPreferences: ['default', 'low-power', 'high-performance'], + +#if PTHREADS && OFFSCREEN_FRAMEBUFFER + // In offscreen framebuffer mode, we implement a proxied version of the + // emscripten_webgl_create_context() function in JS. + emscripten_webgl_create_context_proxied__proxy: 'sync', + emscripten_webgl_create_context_proxied__deps: ['emscripten_webgl_do_create_context'], + emscripten_webgl_create_context_proxied: (target, attributes) => + _emscripten_webgl_do_create_context(target, attributes), + + // The other proxied GL commands are defined in C (guarded by the + // __EMSCRIPTEN_OFFSCREEN_FRAMEBUFFER__ definition). +#else + // When not in offscreen framebuffer mode, these functions are implemented + // in JS and forwarded without any proxying. + emscripten_webgl_create_context: 'emscripten_webgl_do_create_context', + + emscripten_webgl_get_current_context: 'emscripten_webgl_do_get_current_context', + + emscripten_webgl_commit_frame: 'emscripten_webgl_do_commit_frame', +#endif + +#if OFFSCREENCANVAS_SUPPORT + emscripten_webgl_do_create_context__postset: ` + registerPreMainLoop(() => { + // If the current GL context is an OffscreenCanvas, but it was initialized + // with implicit swap mode, perform the swap on behalf of the user. + if (GL.currentContext && !GL.currentContextIsProxied && !GL.currentContext.attributes.explicitSwapControl && GL.currentContext.GLctx.commit) { + GL.currentContext.GLctx.commit(); + } + });`, +#endif + + emscripten_webgl_do_create_context__deps: [ +#if OFFSCREENCANVAS_SUPPORT + '$registerPreMainLoop', + 'malloc', + 'emscripten_supports_offscreencanvas', +#endif +#if PTHREADS && OFFSCREEN_FRAMEBUFFER + 'emscripten_webgl_create_context_proxied', +#endif + '$webglPowerPreferences', '$findCanvasEventTarget'], + // This function performs proxying manually, depending on the style of context that is to be created. + emscripten_webgl_do_create_context: (target, attributes) => { +#if ASSERTIONS + assert(attributes); +#endif + var attr32 = {{{ getHeapOffset('attributes', 'i32') }}}; + var powerPreference = HEAP32[attr32 + ({{{ C_STRUCTS.EmscriptenWebGLContextAttributes.powerPreference }}}>>2)]; + var contextAttributes = { + 'alpha': !!HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.alpha }}}], + 'depth': !!HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.depth }}}], + 'stencil': !!HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.stencil }}}], + 'antialias': !!HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.antialias }}}], + 'premultipliedAlpha': !!HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.premultipliedAlpha }}}], + 'preserveDrawingBuffer': !!HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.preserveDrawingBuffer }}}], + 'powerPreference': webglPowerPreferences[powerPreference], + 'failIfMajorPerformanceCaveat': !!HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.failIfMajorPerformanceCaveat }}}], + // The following are not predefined WebGL context attributes in the WebGL specification, so the property names can be minified by Closure. + majorVersion: HEAP32[attr32 + ({{{ C_STRUCTS.EmscriptenWebGLContextAttributes.majorVersion }}}>>2)], + minorVersion: HEAP32[attr32 + ({{{ C_STRUCTS.EmscriptenWebGLContextAttributes.minorVersion }}}>>2)], + enableExtensionsByDefault: HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.enableExtensionsByDefault }}}], + explicitSwapControl: HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.explicitSwapControl }}}], + proxyContextToMainThread: HEAP32[attr32 + ({{{ C_STRUCTS.EmscriptenWebGLContextAttributes.proxyContextToMainThread }}}>>2)], + renderViaOffscreenBackBuffer: HEAP8[attributes + {{{ C_STRUCTS.EmscriptenWebGLContextAttributes.renderViaOffscreenBackBuffer }}}] + }; + + +#if ASSERTIONS + // TODO: Make these into hard errors at some point in the future + if (contextAttributes.majorVersion !== 1 && contextAttributes.majorVersion !== 2) { + err(`Invalid WebGL version requested: ${contextAttributes.majorVersion}`); + } +#if MIN_WEBGL_VERSION >= 2 + if (contextAttributes.majorVersion !== 2) { + err('WebGL 1 requested but only WebGL 2 is supported (MIN_WEBGL_VERSION is 2)'); + } +#elif MAX_WEBGL_VERSION == 1 + if (contextAttributes.majorVersion !== 1) { + err('WebGL 2 requested but only WebGL 1 is supported (set -sMAX_WEBGL_VERSION=2 to fix the problem)'); + } +#endif +#endif + + var canvas = findCanvasEventTarget(target); +#if OFFSCREENCANVAS_SUPPORT + // If our canvas from findCanvasEventTarget is actually an offscreen canvas record, we should extract the inner canvas. + if (canvas?.canvas) { canvas = canvas.canvas; } +#endif +#if GL_DEBUG + var targetStr = UTF8ToString(target); +#endif + +#if PTHREADS && OFFSCREEN_FRAMEBUFFER + // Create a WebGL context that is proxied to main thread if canvas was not found on worker, or if explicitly requested to do so. + if (ENVIRONMENT_IS_PTHREAD) { + if (contextAttributes.proxyContextToMainThread === {{{ cDefs.EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS }}} || + (!canvas && contextAttributes.proxyContextToMainThread === {{{ cDefs.EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK }}})) { + // When WebGL context is being proxied via the main thread, we must render using an offscreen FBO render target to avoid WebGL's + // "implicit swap when callback exits" behavior. TODO: If OffscreenCanvas is supported, explicitSwapControl=true and still proxying, + // then this can be avoided, since OffscreenCanvas enables explicit swap control. +#if GL_DEBUG + if (contextAttributes.proxyContextToMainThread === {{{ cDefs.EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS }}}) dbg('EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS enabled, proxying WebGL rendering from pthread to main thread.'); + if (!canvas && contextAttributes.proxyContextToMainThread === {{{ cDefs.EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK }}}) dbg(`Specified canvas target "${targetStr}" is not an OffscreenCanvas in the current pthread, but EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK is set. Proxying WebGL rendering from pthread to main thread.`); + dbg('Performance warning: forcing renderViaOffscreenBackBuffer=true and preserveDrawingBuffer=true since proxying WebGL rendering.'); +#endif + // We will be proxying - if OffscreenCanvas is supported, we can proxy a bit more efficiently by avoiding having to create an Offscreen FBO. + if (!_emscripten_supports_offscreencanvas()) { + {{{ makeSetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.renderViaOffscreenBackBuffer, '1', 'i8') }}}; + {{{ makeSetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.preserveDrawingBuffer, '1', 'i8') }}}; + } + return _emscripten_webgl_create_context_proxied(target, attributes); + } + } +#endif + + if (!canvas) { +#if GL_DEBUG + dbg(`emscripten_webgl_create_context failed: Unknown canvas target "${targetStr}"!`); +#endif + return 0; + } + +#if OFFSCREENCANVAS_SUPPORT + if (canvas.offscreenCanvas) canvas = canvas.offscreenCanvas; + +#if GL_DEBUG + if (_emscripten_supports_offscreencanvas() && canvas instanceof OffscreenCanvas) dbg(`emscripten_webgl_create_context: Creating an OffscreenCanvas-based WebGL context on target "${targetStr}"`); + else if (typeof HTMLCanvasElement != 'undefined' && canvas instanceof HTMLCanvasElement) dbg(`emscripten_webgl_create_context: Creating an HTMLCanvasElement-based WebGL context on target "${targetStr}"`); +#endif + + if (contextAttributes.explicitSwapControl) { + var supportsOffscreenCanvas = canvas.transferControlToOffscreen || (_emscripten_supports_offscreencanvas() && canvas instanceof OffscreenCanvas); + + if (!supportsOffscreenCanvas) { +#if OFFSCREEN_FRAMEBUFFER + if (!contextAttributes.renderViaOffscreenBackBuffer) { + contextAttributes.renderViaOffscreenBackBuffer = true; +#if GL_DEBUG + dbg('emscripten_webgl_create_context: Performance warning, OffscreenCanvas is not supported but explicitSwapControl was requested, so force-enabling renderViaOffscreenBackBuffer=true to allow explicit swapping!'); +#endif + } +#else +#if GL_DEBUG + dbg('emscripten_webgl_create_context failed: OffscreenCanvas is not supported but explicitSwapControl was requested!'); +#endif + return 0; +#endif + } + + if (canvas.transferControlToOffscreen) { +#if GL_DEBUG + dbg(`explicitSwapControl requested: canvas.transferControlToOffscreen() on canvas "${targetStr}" to get .commit() function and not rely on implicit WebGL swap`); +#endif + if (!canvas.controlTransferredOffscreen) { + GL.offscreenCanvases[canvas.id] = { + canvas: canvas.transferControlToOffscreen(), + canvasSharedPtr: _malloc(12), + id: canvas.id + }; + canvas.controlTransferredOffscreen = true; + } else if (!GL.offscreenCanvases[canvas.id]) { +#if GL_DEBUG + dbg(`OffscreenCanvas is supported, and canvas "${canvas.id}" has already before been transferred offscreen, but there is no known OffscreenCanvas with that name!`); +#endif + return 0; + } + canvas = GL.offscreenCanvases[canvas.id].canvas; + } + } +#else // !OFFSCREENCANVAS_SUPPORT +#if OFFSCREEN_FRAMEBUFFER + if (contextAttributes.explicitSwapControl && !contextAttributes.renderViaOffscreenBackBuffer) { + contextAttributes.renderViaOffscreenBackBuffer = true; +#if GL_DEBUG + dbg('emscripten_webgl_create_context: Performance warning, not building with OffscreenCanvas support enabled but explicitSwapControl was requested, so force-enabling renderViaOffscreenBackBuffer=true to allow explicit swapping!'); +#endif + } +#else + if (contextAttributes.explicitSwapControl) { +#if GL_DEBUG + dbg('emscripten_webgl_create_context failed: explicitSwapControl is not supported, please rebuild with -sOFFSCREENCANVAS_SUPPORT to enable targeting the experimental OffscreenCanvas specification, or rebuild with -sOFFSCREEN_FRAMEBUFFER to emulate explicitSwapControl in the absence of OffscreenCanvas support!'); +#endif + return 0; + } +#endif // ~!OFFSCREEN_FRAMEBUFFER + +#endif // ~!OFFSCREENCANVAS_SUPPORT + + var contextHandle = GL.createContext(canvas, contextAttributes); + return contextHandle; + }, + +#if PTHREADS && OFFSCREEN_FRAMEBUFFER + // Runs on the calling thread, proxies if needed. + emscripten_webgl_make_context_current_calling_thread__sig: 'ip', + emscripten_webgl_make_context_current_calling_thread: (contextHandle) => { + var success = GL.makeContextCurrent(contextHandle); + if (success) GL.currentContextIsProxied = false; // If succeeded above, we will have a local GL context from this thread (worker or main). + return success ? {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}} : {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}}; + }, + // This function gets called in a pthread, after it has successfully activated (with make_current()) a proxied GL context to itself from the main thread. + // In this scenario, the pthread does not hold a high-level JS object to the GL context, because it lives on the main thread, in which case we record + // an integer pointer as a token value to represent the GL context activation from another thread. (when this function is called, the main browser thread + // has already accepted the GL context activation for our pthread, so that side is good) +#if GL_SUPPORT_EXPLICIT_SWAP_CONTROL + _emscripten_proxied_gl_context_activated_from_main_browser_thread__deps: ['$registerPreMainLoop'], + _emscripten_proxied_gl_context_activated_from_main_browser_thread__postjs: ` + // If the current GL context is a proxied regular WebGL context, and was + // initialized with implicit swap mode on the main thread, and we are on the + // parent thread, perform the swap on behalf of the user. + registerPreMainLoop(() => { + if (GL.currentContext && GL.currentContextIsProxied) { + var explicitSwapControl = {{{ makeGetValue('GL.currentContext', 0, 'i32') }}}; + if (!explicitSwapControl) _emscripten_webgl_commit_frame(); + } + });`, +#endif + _emscripten_proxied_gl_context_activated_from_main_browser_thread: (contextHandle) => { + GLctx = Module['ctx'] = GL.currentContext = contextHandle; + GL.currentContextIsProxied = true; + }, +#else + emscripten_webgl_make_context_current: (contextHandle) => { + var success = GL.makeContextCurrent(contextHandle); + return success ? {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}} : {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}}; + }, +#endif + + emscripten_webgl_do_get_current_context: () => GL.currentContext ? GL.currentContext.handle : 0, + + emscripten_webgl_get_drawing_buffer_size__proxy: 'sync_on_webgl_context_handle_thread', + emscripten_webgl_get_drawing_buffer_size: (contextHandle, width, height) => { + var GLContext = GL.getContext(contextHandle); + + if (!GLContext || !GLContext.GLctx || !width || !height) { + return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}}; + } + {{{ makeSetValue('width', '0', 'GLContext.GLctx.drawingBufferWidth', 'i32') }}}; + {{{ makeSetValue('height', '0', 'GLContext.GLctx.drawingBufferHeight', 'i32') }}}; + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_webgl_do_commit_frame: () => { +#if TRACE_WEBGL_CALLS + var threadId = (typeof _pthread_self != 'undefined') ? _pthread_self : () => 1; + err(`[Thread ${threadId()}, GL ctx: ${GL.currentContext.handle}]: emscripten_webgl_do_commit_frame()`); +#endif + if (!GL.currentContext || !GL.currentContext.GLctx) { +#if GL_DEBUG + dbg('emscripten_webgl_commit_frame() failed: no GL context set current via emscripten_webgl_make_context_current()!'); +#endif + return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_TARGET }}}; + } + +#if OFFSCREEN_FRAMEBUFFER + if (GL.currentContext.defaultFbo) { + GL.blitOffscreenFramebuffer(GL.currentContext); +#if GL_DEBUG && OFFSCREENCANVAS_SUPPORT + if (GL.currentContext.GLctx.commit) dbg('emscripten_webgl_commit_frame(): Offscreen framebuffer should never have gotten created when canvas is in OffscreenCanvas mode, since it is redundant and not necessary'); +#endif + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + } +#endif + if (!GL.currentContext.attributes.explicitSwapControl) { +#if GL_DEBUG + dbg('emscripten_webgl_commit_frame() cannot be called for canvases with implicit swap control mode!'); +#endif + return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_TARGET }}}; + } + // 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 {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_webgl_get_context_attributes__proxy: 'sync_on_webgl_context_handle_thread', + emscripten_webgl_get_context_attributes__deps: ['$webglPowerPreferences'], + emscripten_webgl_get_context_attributes: (c, a) => { + if (!a) return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}}; + c = GL.contexts[c]; + if (!c) return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_TARGET }}}; + var t = c.GLctx; + if (!t) return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_TARGET }}}; + t = t.getContextAttributes(); + + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.alpha, 't.alpha', 'i8') }}}; + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.depth, 't.depth', 'i8') }}}; + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.stencil, 't.stencil', 'i8') }}}; + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.antialias, 't.antialias', 'i8') }}}; + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.premultipliedAlpha, 't.premultipliedAlpha', 'i8') }}}; + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.preserveDrawingBuffer, 't.preserveDrawingBuffer', 'i8') }}}; + var power = t['powerPreference'] && webglPowerPreferences.indexOf(t['powerPreference']); + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.powerPreference, 'power', 'i32') }}}; + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.failIfMajorPerformanceCaveat, 't.failIfMajorPerformanceCaveat', 'i8') }}}; + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.majorVersion, 'c.version', 'i32') }}}; + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.minorVersion, 0, 'i32') }}}; +#if GL_SUPPORT_AUTOMATIC_ENABLE_EXTENSIONS + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.enableExtensionsByDefault, 'c.attributes.enableExtensionsByDefault', 'i8') }}}; +#endif +#if GL_SUPPORT_EXPLICIT_SWAP_CONTROL + {{{ makeSetValue('a', C_STRUCTS.EmscriptenWebGLContextAttributes.explicitSwapControl, 'c.attributes.explicitSwapControl', 'i8') }}}; +#endif + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_webgl_destroy_context__proxy: 'sync_on_webgl_context_handle_thread', + emscripten_webgl_destroy_context: (contextHandle) => { + if (GL.currentContext == contextHandle) GL.currentContext = 0; + GL.deleteContext(contextHandle); + }, + +#if PTHREADS + // Special function that will be invoked on the thread calling emscripten_webgl_destroy_context(), before routing + // the call over to the target thread. + $emscripten_webgl_destroy_context_before_on_calling_thread__deps: ['emscripten_webgl_get_current_context', 'emscripten_webgl_make_context_current'], + $emscripten_webgl_destroy_context_before_on_calling_thread: (contextHandle) => { + if (_emscripten_webgl_get_current_context() == contextHandle) _emscripten_webgl_make_context_current(0); + }, +#endif + + emscripten_webgl_enable_extension__deps: [ +#if GL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS +#if MIN_WEBGL_VERSION == 1 + '$webgl_enable_ANGLE_instanced_arrays', + '$webgl_enable_OES_vertex_array_object', + '$webgl_enable_WEBGL_draw_buffers', +#endif +#if MAX_WEBGL_VERSION >= 2 + '$webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance', + '$webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance', +#endif + '$webgl_enable_EXT_polygon_offset_clamp', + '$webgl_enable_EXT_clip_control', + '$webgl_enable_WEBGL_polygon_mode', + '$webgl_enable_WEBGL_multi_draw', +#endif + ], + emscripten_webgl_enable_extension__proxy: 'sync_on_webgl_context_handle_thread', + emscripten_webgl_enable_extension: (contextHandle, extension) => { + var context = GL.getContext(contextHandle); + var extString = UTF8ToString(extension); +#if GL_EXTENSIONS_IN_PREFIXED_FORMAT + if (extString.startsWith('GL_')) extString = extString.slice(3); // Allow enabling extensions both with "GL_" prefix and without. +#endif + +#if GL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS + // 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. + +#if MIN_WEBGL_VERSION == 1 + // 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); +#endif + +#if MAX_WEBGL_VERSION >= 2 + if (extString == 'WEBGL_draw_instanced_base_vertex_base_instance') webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx); + if (extString == 'WEBGL_multi_draw_instanced_base_vertex_base_instance') webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx); +#endif + + 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); + +#elif ASSERTIONS || GL_ASSERTIONS + if (['ANGLE_instanced_arrays', + 'OES_vertex_array_object', + 'WEBGL_draw_buffers', + 'WEBGL_multi_draw', + 'EXT_polygon_offset_clamp', + 'EXT_clip_control', + 'WEBGL_polygon_mode', + 'WEBGL_draw_instanced_base_vertex_base_instance', + 'WEBGL_multi_draw_instanced_base_vertex_base_instance'].includes(extString)) { + err('When building with -sGL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS=0, function emscripten_webgl_enable_extension() cannot be used to enable extension ' + + extString + '! Use one of the functions emscripten_webgl_enable_*() to enable it!'); + } +#endif + + var ext = context.GLctx.getExtension(extString); + return !!ext; + }, + + 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. +#if OFFSCREENCANVAS_SUPPORT + typeof OffscreenCanvas != 'undefined' +#else + 0 +#endif + , + + $registerWebGlEventCallback__deps: ['$JSEvents', '$findEventTarget'], + $registerWebGlEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { +#if PTHREADS + targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); +#endif + +#if !DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR + target ||= Module['canvas']; +#endif + + var webGlEventHandlerFunc = (e = event) => { +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, 0, userData); + else +#endif + if ({{{ makeDynCall('iiii', 'callbackfunc') }}}(eventTypeId, 0, userData)) e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + callbackfunc, + handlerFunc: webGlEventHandlerFunc, + useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + emscripten_set_webglcontextlost_callback_on_thread__proxy: 'sync', + emscripten_set_webglcontextlost_callback_on_thread__deps: ['$registerWebGlEventCallback'], + emscripten_set_webglcontextlost_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => { + registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST }}}, "webglcontextlost", targetThread); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_set_webglcontextrestored_callback_on_thread__proxy: 'sync', + emscripten_set_webglcontextrestored_callback_on_thread__deps: ['$registerWebGlEventCallback'], + emscripten_set_webglcontextrestored_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => { + registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED }}}, "webglcontextrestored", targetThread); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + + emscripten_is_webgl_context_lost__proxy: 'sync_on_webgl_context_handle_thread', + emscripten_is_webgl_context_lost: (contextHandle) => + !GL.contexts[contextHandle] || GL.contexts[contextHandle].GLctx.isContextLost(), // No context ~> lost context. + + emscripten_webgl_get_supported_extensions__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_supported_extensions__deps: ['$stringToNewUTF8'], + // Here we report the full list of extensions supported by WebGL rather than + // using getEmscriptenSupportedExtensions which filters the list based on + // what is has explicit support in. + emscripten_webgl_get_supported_extensions: () => + stringToNewUTF8(GLctx.getSupportedExtensions().join(' ')), + + emscripten_webgl_get_program_parameter_d__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_program_parameter_d: (program, param) => + GLctx.getProgramParameter(GL.programs[program], param), + + emscripten_webgl_get_program_info_log_utf8__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_program_info_log_utf8__deps: ['$stringToNewUTF8'], + emscripten_webgl_get_program_info_log_utf8: (program) => + stringToNewUTF8(GLctx.getProgramInfoLog(GL.programs[program])), + + emscripten_webgl_get_shader_parameter_d__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_shader_parameter_d: (shader, param) => + GLctx.getShaderParameter(GL.shaders[shader], param), + + emscripten_webgl_get_shader_info_log_utf8__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_shader_info_log_utf8__deps: ['$stringToNewUTF8'], + emscripten_webgl_get_shader_info_log_utf8: (shader) => + stringToNewUTF8(GLctx.getShaderInfoLog(GL.shaders[shader])), + + emscripten_webgl_get_shader_source_utf8__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_shader_source_utf8__deps: ['$stringToNewUTF8'], + emscripten_webgl_get_shader_source_utf8: (shader) => + stringToNewUTF8(GLctx.getShaderSource(GL.shaders[shader])), + + emscripten_webgl_get_vertex_attrib_d__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_vertex_attrib_d: (index, param) => + GLctx.getVertexAttrib(index, param), + + emscripten_webgl_get_vertex_attrib_o__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_vertex_attrib_o: (index, param) => { + var obj = GLctx.getVertexAttrib(index, param); + return obj?.name; + }, + + emscripten_webgl_get_vertex_attrib_v__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_vertex_attrib_v__deps: ['$writeGLArray'], + emscripten_webgl_get_vertex_attrib_v: (index, param, dst, dstLength, dstType) => + writeGLArray(GLctx.getVertexAttrib(index, param), dst, dstLength, dstType), + + emscripten_webgl_get_uniform_d__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_uniform_d__deps: ['$webglGetUniformLocation'], + emscripten_webgl_get_uniform_d: (program, location) => + GLctx.getUniform(GL.programs[program], webglGetUniformLocation(location)), + + emscripten_webgl_get_uniform_v__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_uniform_v__deps: ['$writeGLArray', '$webglGetUniformLocation'], + emscripten_webgl_get_uniform_v: (program, location, dst, dstLength, dstType) => + writeGLArray(GLctx.getUniform(GL.programs[program], webglGetUniformLocation(location)), dst, dstLength, dstType), + + emscripten_webgl_get_parameter_v__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_parameter_v__deps: ['$writeGLArray'], + emscripten_webgl_get_parameter_v: (param, dst, dstLength, dstType) => + writeGLArray(GLctx.getParameter(param), dst, dstLength, dstType), + + emscripten_webgl_get_parameter_d__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_parameter_d: (param) => GLctx.getParameter(param), + + emscripten_webgl_get_parameter_o__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_parameter_o: (param) => { + var obj = GLctx.getParameter(param); + return obj?.name; + }, + + emscripten_webgl_get_parameter_utf8__deps: ['$stringToNewUTF8'], + emscripten_webgl_get_parameter_utf8__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_parameter_utf8: (param) => stringToNewUTF8(GLctx.getParameter(param)), + + emscripten_webgl_get_parameter_i64v__proxy: 'sync_on_current_webgl_context_thread', + emscripten_webgl_get_parameter_i64v__deps: ['$writeI53ToI64'], + emscripten_webgl_get_parameter_i64v: (param, dst) => writeI53ToI64(dst, GLctx.getParameter(param)), +}; + + +function handleWebGLProxying(funcs) { +#if PTHREADS + // Process 'sync_on_webgl_context_handle_thread' and + // 'sync_on_current_webgl_context_thread' pseudo-proxying modes to appropriate + // proxying mechanism, either proxying on-demand, unconditionally, or never, + // depending on build modes. + // 'sync_on_webgl_context_handle_thread' is used for function signatures that + // take a HTML5 WebGL context handle object as the first argument. + // 'sync_on_current_webgl_context_thread' is used for functions that operate on + // the implicit "current WebGL context" as activated via + // emscripten_webgl_make_current() function. + function listOfNFunctionArgs(func) { + const args = []; + for (var i = 0; i < func.length; ++i) { + args.push('p' + i); + } + return args; + } + + const targetingOffscreenCanvas = {{{ OFFSCREENCANVAS_SUPPORT }}}; + const targetingOffscreenFramebuffer = {{{ OFFSCREEN_FRAMEBUFFER }}}; + + for (const i in funcs) { + // Is this a function that takes GL context handle as first argument? + const proxyContextHandle = funcs[i + '__proxy'] == 'sync_on_webgl_context_handle_thread'; + + // Is this a function that operates on the implicit current GL context object? + const proxyCurrentContext = funcs[i + '__proxy'] == 'sync_on_current_webgl_context_thread'; + + if (!proxyContextHandle && !proxyCurrentContext) { + continue; // no resolving of pseudo-proxying needed for this function. + } + + if (targetingOffscreenCanvas && (targetingOffscreenFramebuffer || proxyContextHandle)) { + // Dynamically check at runtime whether the current thread owns the GL context + // handle/current GL context object. If not, proxy the call to main thread. + // TODO: this handles the calling pthread and main thread cases, but not yet + // the case from pthread->pthread. + const sig = funcs[i + '__sig'] || LibraryManager.library[i + '__sig'] + assert(sig); + funcs[i + '_calling_thread'] = funcs[i]; + funcs[i + '_main_thread'] = i + '_calling_thread'; + funcs[i + '_main_thread__proxy'] = 'sync'; + funcs[i + '_main_thread__sig'] = sig; + funcs[i + '__deps'] ??= []; + funcs[i + '__deps'].push(i + '_calling_thread'); + funcs[i + '__deps'].push(i + '_main_thread'); + delete funcs[i + '__proxy']; + const funcArgs = listOfNFunctionArgs(funcs[i]); + const funcArgsString = funcArgs.join(','); + const retStatement = sig[0] != 'v' ? 'return' : ''; + const contextCheck = proxyContextHandle ? 'GL.contexts[p0]' : 'GLctx'; + var funcBody = `${retStatement} ${contextCheck} ? _${i}_calling_thread(${funcArgsString}) : _${i}_main_thread(${funcArgsString});`; + if (funcs[i + '_before_on_calling_thread']) { + funcs[i + '__deps'].push('$' + i + '_before_on_calling_thread'); + funcBody = `${i}_before_on_calling_thread(${funcArgsString}); ` + funcBody; + } + funcs[i] = new Function(funcArgs, funcBody); + } else if (targetingOffscreenFramebuffer) { + // When targeting only OFFSCREEN_FRAMEBUFFER, unconditionally proxy all GL + // calls to main thread. + funcs[i + '__proxy'] = 'sync'; + } else { + // Building without OFFSCREENCANVAS_SUPPORT or OFFSCREEN_FRAMEBUFFER; or building + // with OFFSCREENCANVAS_SUPPORT and no OFFSCREEN_FRAMEBUFFER: the application + // will only utilize WebGL in the main browser thread, and in the calling thread. + // Remove the WebGL proxying directives. + delete funcs[i + '__proxy']; + } + } +#else + // In single threaded mode just delete our custom __proxy addributes, otherwise + // they will causes errors in the JS compiler. + for (const i in funcs) { + delete funcs[i + '__proxy']; + } +#endif // PTHREADS +} + +handleWebGLProxying(LibraryHtml5WebGL); + +#if LibraryManager.has('libwebgl.js') +autoAddDeps(LibraryHtml5WebGL, '$GL'); +#endif + +addToLibrary(LibraryHtml5WebGL); diff --git a/src/lib/libhtml5_webgpu.js b/src/lib/libhtml5_webgpu.js new file mode 100644 index 0000000000000..22710ac4d050e --- /dev/null +++ b/src/lib/libhtml5_webgpu.js @@ -0,0 +1,92 @@ +{{{ + // Helper functions for code generation +const html5_gpu = { + makeImportExport: (snake_case, CamelCase) => { + return ` +LibraryHTML5WebGPU.emscripten_webgpu_import_${snake_case}__deps = ['$WebGPU', '$JsValStore']; +LibraryHTML5WebGPU.emscripten_webgpu_import_${snake_case} = (handle) => + WebGPU.mgr${CamelCase}.create(JsValStore.get(handle)); + +LibraryHTML5WebGPU.emscripten_webgpu_export_${snake_case}__deps = ['$WebGPU', '$JsValStore']; +LibraryHTML5WebGPU.emscripten_webgpu_export_${snake_case} = (handle) => + JsValStore.add(WebGPU.mgr${CamelCase}.get(handle));` + }, +}; +}}} + + +var LibraryHTML5WebGPU = { + $JsValStore: { + values: {}, + next_id: 1, + + add(js_val) { + var id; + do { + id = JsValStore.next_id++; + if (JsValStore.next_id > 2147483647) JsValStore.next_id = 1; // Wraparound signed int32. + } while (id in JsValStore.values); + + JsValStore.values[id] = js_val; + return id; + }, + remove(id) { +#if ASSERTIONS + assert(id in JsValStore.values); +#endif + delete JsValStore.values[id]; + }, + get(id) { +#if ASSERTIONS + assert(id === 0 || id in JsValStore.values); +#endif + return JsValStore.values[id]; + }, + }, + + emscripten_webgpu_release_js_handle__deps: ['$JsValStore'], + emscripten_webgpu_release_js_handle: (id) => JsValStore.remove(id), + + emscripten_webgpu_get_device__deps: ['$WebGPU'], + emscripten_webgpu_get_device: () => { +#if ASSERTIONS + assert(Module['preinitializedWebGPUDevice']); +#endif + if (WebGPU.preinitializedDeviceId === undefined) { + var device = Module['preinitializedWebGPUDevice']; + var deviceWrapper = { queueId: WebGPU.mgrQueue.create(device["queue"]) }; + WebGPU.preinitializedDeviceId = WebGPU.mgrDevice.create(device, deviceWrapper); + } + WebGPU.mgrDevice.reference(WebGPU.preinitializedDeviceId); + return WebGPU.preinitializedDeviceId; + }, +}; + +{{{ html5_gpu.makeImportExport('surface', 'Surface') }}} +{{{ html5_gpu.makeImportExport('swap_chain', 'SwapChain') }}} + +{{{ html5_gpu.makeImportExport('device', 'Device') }}} +{{{ html5_gpu.makeImportExport('queue', 'Queue') }}} + +{{{ html5_gpu.makeImportExport('command_buffer', 'CommandBuffer') }}} +{{{ html5_gpu.makeImportExport('command_encoder', 'CommandEncoder') }}} +{{{ html5_gpu.makeImportExport('render_pass_encoder', 'RenderPassEncoder') }}} +{{{ html5_gpu.makeImportExport('compute_pass_encoder', 'ComputePassEncoder') }}} + +{{{ html5_gpu.makeImportExport('bind_group', 'BindGroup') }}} +{{{ html5_gpu.makeImportExport('buffer', 'Buffer') }}} +{{{ html5_gpu.makeImportExport('sampler', 'Sampler') }}} +{{{ html5_gpu.makeImportExport('texture', 'Texture') }}} +{{{ html5_gpu.makeImportExport('texture_view', 'TextureView') }}} +{{{ html5_gpu.makeImportExport('query_set', 'QuerySet') }}} + +{{{ html5_gpu.makeImportExport('bind_group_layout', 'BindGroupLayout') }}} +{{{ html5_gpu.makeImportExport('pipeline_layout', 'PipelineLayout') }}} +{{{ html5_gpu.makeImportExport('render_pipeline', 'RenderPipeline') }}} +{{{ html5_gpu.makeImportExport('compute_pipeline', 'ComputePipeline') }}} +{{{ html5_gpu.makeImportExport('shader_module', 'ShaderModule') }}} + +{{{ html5_gpu.makeImportExport('render_bundle_encoder', 'RenderBundleEncoder') }}} +{{{ html5_gpu.makeImportExport('render_bundle', 'RenderBundle') }}} + +addToLibrary(LibraryHTML5WebGPU); diff --git a/src/library_icasefs.js b/src/lib/libicasefs.js similarity index 100% rename from src/library_icasefs.js rename to src/lib/libicasefs.js diff --git a/src/lib/libidbfs.js b/src/lib/libidbfs.js new file mode 100644 index 0000000000000..417b9c757b401 --- /dev/null +++ b/src/lib/libidbfs.js @@ -0,0 +1,391 @@ +/** + * @license + * Copyright 2013 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + $IDBFS__deps: ['$FS', '$MEMFS', '$PATH'], + $IDBFS__postset: () => { + addAtExit('IDBFS.quit();'); + return ''; + }, + $IDBFS: { + dbs: {}, + indexedDB: () => { +#if ASSERTIONS + assert(typeof indexedDB != 'undefined', 'IDBFS used, but indexedDB not supported'); +#endif + return indexedDB; + }, + DB_VERSION: 21, + DB_STORE_NAME: 'FILE_DATA', + + // Queues a new VFS -> IDBFS synchronization operation + queuePersist: (mount) => { + function onPersistComplete() { + if (mount.idbPersistState === 'again') startPersist(); // If a new sync request has appeared in between, kick off a new sync + else mount.idbPersistState = 0; // Otherwise reset sync state back to idle to wait for a new sync later + } + function startPersist() { + mount.idbPersistState = 'idb'; // Mark that we are currently running a sync operation + IDBFS.syncfs(mount, /*populate:*/false, onPersistComplete); + } + + if (!mount.idbPersistState) { + // Programs typically write/copy/move multiple files in the in-memory + // filesystem within a single app frame, so when a filesystem sync + // command is triggered, do not start it immediately, but only after + // the current frame is finished. This way all the modified files + // inside the main loop tick will be batched up to the same sync. + mount.idbPersistState = setTimeout(startPersist, 0); + } else if (mount.idbPersistState === 'idb') { + // There is an active IndexedDB sync operation in-flight, but we now + // have accumulated more files to sync. We should therefore queue up + // a new sync after the current one finishes so that all writes + // will be properly persisted. + mount.idbPersistState = 'again'; + } + }, + + mount: (mount) => { + // reuse core MEMFS functionality + var mnt = MEMFS.mount(mount); + // If the automatic IDBFS persistence option has been selected, then automatically persist + // all modifications to the filesystem as they occur. + if (mount?.opts?.autoPersist) { + mount.idbPersistState = 0; // IndexedDB sync starts in idle state + var memfs_node_ops = mnt.node_ops; + mnt.node_ops = {...mnt.node_ops}; // Clone node_ops to inject write tracking + mnt.node_ops.mknod = (parent, name, mode, dev) => { + var node = memfs_node_ops.mknod(parent, name, mode, dev); + // Propagate injected node_ops to the newly created child node + node.node_ops = mnt.node_ops; + // Remember for each IDBFS node which IDBFS mount point they came from so we know which mount to persist on modification. + node.idbfs_mount = mnt.mount; + // Remember original MEMFS stream_ops for this node + node.memfs_stream_ops = node.stream_ops; + // Clone stream_ops to inject write tracking + node.stream_ops = {...node.stream_ops}; + + // Track all file writes + node.stream_ops.write = (stream, buffer, offset, length, position, canOwn) => { + // This file has been modified, we must persist IndexedDB when this file closes + stream.node.isModified = true; + return node.memfs_stream_ops.write(stream, buffer, offset, length, position, canOwn); + }; + + // Persist IndexedDB on file close + node.stream_ops.close = (stream) => { + var n = stream.node; + if (n.isModified) { + IDBFS.queuePersist(n.idbfs_mount); + n.isModified = false; + } + if (n.memfs_stream_ops.close) return n.memfs_stream_ops.close(stream); + }; + + // Persist the node we just created to IndexedDB + IDBFS.queuePersist(mnt.mount); + + return node; + }; + // Also kick off persisting the filesystem on other operations that modify the filesystem. + mnt.node_ops.rmdir = (...args) => (IDBFS.queuePersist(mnt.mount), memfs_node_ops.rmdir(...args)); + mnt.node_ops.symlink = (...args) => (IDBFS.queuePersist(mnt.mount), memfs_node_ops.symlink(...args)); + mnt.node_ops.unlink = (...args) => (IDBFS.queuePersist(mnt.mount), memfs_node_ops.unlink(...args)); + mnt.node_ops.rename = (...args) => (IDBFS.queuePersist(mnt.mount), memfs_node_ops.rename(...args)); + } + return mnt; + }, + + syncfs: (mount, populate, callback) => { + IDBFS.getLocalSet(mount, (err, local) => { + if (err) return callback(err); + + IDBFS.getRemoteSet(mount, (err, remote) => { + if (err) return callback(err); + + var src = populate ? remote : local; + var dst = populate ? local : remote; + + IDBFS.reconcile(src, dst, callback); + }); + }); + }, + quit: () => { + Object.values(IDBFS.dbs).forEach((value) => value.close()); + IDBFS.dbs = {}; + }, + getDB: (name, callback) => { + // check the cache first + var db = IDBFS.dbs[name]; + if (db) { + return callback(null, db); + } + + var req; + try { + req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION); + } catch (e) { + return callback(e); + } + if (!req) { + return callback("Unable to connect to IndexedDB"); + } + req.onupgradeneeded = (e) => { + var db = /** @type {IDBDatabase} */ (e.target.result); + var transaction = e.target.transaction; + + var fileStore; + + if (db.objectStoreNames.contains(IDBFS.DB_STORE_NAME)) { + fileStore = transaction.objectStore(IDBFS.DB_STORE_NAME); + } else { + fileStore = db.createObjectStore(IDBFS.DB_STORE_NAME); + } + + if (!fileStore.indexNames.contains('timestamp')) { + fileStore.createIndex('timestamp', 'timestamp', { unique: false }); + } + }; + req.onsuccess = () => { + db = /** @type {IDBDatabase} */ (req.result); + + // add to the cache + IDBFS.dbs[name] = db; + callback(null, db); + }; + req.onerror = (e) => { + callback(e.target.error); + e.preventDefault(); + }; + }, + getLocalSet: (mount, callback) => { + var entries = {}; + + function isRealDir(p) { + return p !== '.' && p !== '..'; + }; + function toAbsolute(root) { + return (p) => PATH.join2(root, p); + }; + + var check = FS.readdir(mount.mountpoint).filter(isRealDir).map(toAbsolute(mount.mountpoint)); + + while (check.length) { + var path = check.pop(); + var stat; + + try { + stat = FS.stat(path); + } catch (e) { + return callback(e); + } + + if (FS.isDir(stat.mode)) { + check.push(...FS.readdir(path).filter(isRealDir).map(toAbsolute(path))); + } + + entries[path] = { 'timestamp': stat.mtime }; + } + + return callback(null, { type: 'local', entries: entries }); + }, + getRemoteSet: (mount, callback) => { + var entries = {}; + + IDBFS.getDB(mount.mountpoint, (err, db) => { + if (err) return callback(err); + + try { + var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly'); + transaction.onerror = (e) => { + callback(e.target.error); + e.preventDefault(); + }; + + var store = transaction.objectStore(IDBFS.DB_STORE_NAME); + var index = store.index('timestamp'); + + index.openKeyCursor().onsuccess = (event) => { + var cursor = event.target.result; + + if (!cursor) { + return callback(null, { type: 'remote', db, entries }); + } + + entries[cursor.primaryKey] = { 'timestamp': cursor.key }; + + cursor.continue(); + }; + } catch (e) { + return callback(e); + } + }); + }, + loadLocalEntry: (path, callback) => { + var stat, node; + + try { + var lookup = FS.lookupPath(path); + node = lookup.node; + stat = FS.stat(path); + } catch (e) { + return callback(e); + } + + if (FS.isDir(stat.mode)) { + return callback(null, { 'timestamp': stat.mtime, 'mode': stat.mode }); + } else if (FS.isFile(stat.mode)) { + // Performance consideration: storing a normal JavaScript array to a IndexedDB is much slower than storing a typed array. + // Therefore always convert the file contents to a typed array first before writing the data to IndexedDB. + node.contents = MEMFS.getFileDataAsTypedArray(node); + return callback(null, { 'timestamp': stat.mtime, 'mode': stat.mode, 'contents': node.contents }); + } else { + return callback(new Error('node type not supported')); + } + }, + storeLocalEntry: (path, entry, callback) => { + try { + if (FS.isDir(entry['mode'])) { + FS.mkdirTree(path, entry['mode']); + } else if (FS.isFile(entry['mode'])) { + FS.writeFile(path, entry['contents'], { canOwn: true }); + } else { + return callback(new Error('node type not supported')); + } + + FS.chmod(path, entry['mode']); + FS.utime(path, entry['timestamp'], entry['timestamp']); + } catch (e) { + return callback(e); + } + + callback(null); + }, + removeLocalEntry: (path, callback) => { + try { + var stat = FS.stat(path); + + if (FS.isDir(stat.mode)) { + FS.rmdir(path); + } else if (FS.isFile(stat.mode)) { + FS.unlink(path); + } + } catch (e) { + return callback(e); + } + + callback(null); + }, + loadRemoteEntry: (store, path, callback) => { + var req = store.get(path); + req.onsuccess = (event) => callback(null, event.target.result); + req.onerror = (e) => { + callback(e.target.error); + e.preventDefault(); + }; + }, + storeRemoteEntry: (store, path, entry, callback) => { + try { + var req = store.put(entry, path); + } catch (e) { + callback(e); + return; + } + req.onsuccess = (event) => callback(); + req.onerror = (e) => { + callback(e.target.error); + e.preventDefault(); + }; + }, + removeRemoteEntry: (store, path, callback) => { + var req = store.delete(path); + req.onsuccess = (event) => callback(); + req.onerror = (e) => { + callback(e.target.error); + e.preventDefault(); + }; + }, + reconcile: (src, dst, callback) => { + var total = 0; + + var create = []; + Object.keys(src.entries).forEach((key) => { + var e = src.entries[key]; + var e2 = dst.entries[key]; + if (!e2 || e['timestamp'].getTime() != e2['timestamp'].getTime()) { + create.push(key); + total++; + } + }); + + var remove = []; + Object.keys(dst.entries).forEach((key) => { + if (!src.entries[key]) { + remove.push(key); + total++; + } + }); + + if (!total) { + return callback(null); + } + + var errored = false; + var db = src.type === 'remote' ? src.db : dst.db; + var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite'); + var store = transaction.objectStore(IDBFS.DB_STORE_NAME); + + function done(err) { + if (err && !errored) { + errored = true; + return callback(err); + } + }; + + // transaction may abort if (for example) there is a QuotaExceededError + transaction.onerror = transaction.onabort = (e) => { + done(e.target.error); + e.preventDefault(); + }; + + transaction.oncomplete = (e) => { + if (!errored) { + callback(null); + } + }; + + // sort paths in ascending order so directory entries are created + // before the files inside them + create.sort().forEach((path) => { + if (dst.type === 'local') { + IDBFS.loadRemoteEntry(store, path, (err, entry) => { + if (err) return done(err); + IDBFS.storeLocalEntry(path, entry, done); + }); + } else { + IDBFS.loadLocalEntry(path, (err, entry) => { + if (err) return done(err); + IDBFS.storeRemoteEntry(store, path, entry, done); + }); + } + }); + + // sort paths in descending order so files are deleted before their + // parent directories + remove.sort().reverse().forEach((path) => { + if (dst.type === 'local') { + IDBFS.removeLocalEntry(path, done); + } else { + IDBFS.removeRemoteEntry(store, path, done); + } + }); + } + } +}); + +if (WASMFS) { + error("using -lidbfs is not currently supported in WasmFS."); +} diff --git a/src/lib/libidbstore.js b/src/lib/libidbstore.js new file mode 100644 index 0000000000000..224d1d1d434a8 --- /dev/null +++ b/src/lib/libidbstore.js @@ -0,0 +1,228 @@ +/** + * @license + * Copyright 2015 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +#include "IDBStore.js" + +var LibraryIDBStore = { + // A simple IDB-backed storage mechanism. Suitable for saving and loading + // large files asynchronously. This does *NOT* use the emscripten filesystem, + // intentionally, to avoid overhead. It lets you application define whatever + // filesystem-like layer you want, with the overhead 100% controlled by you. + // At the extremes, you could either just store large files, with almost no + // extra code; or you could implement a file b-tree using posix-compliant + // filesystem on top. + $IDBStore: IDBStore, + emscripten_idb_async_load__deps: ['$UTF8ToString', '$callUserCallback', 'malloc', 'free'], + emscripten_idb_async_load: (db, id, arg, onload, onerror) => { + {{{ runtimeKeepalivePush() }}}; + IDBStore.getFile(UTF8ToString(db), UTF8ToString(id), (error, byteArray) => { + {{{ runtimeKeepalivePop() }}} + callUserCallback(() => { + if (error) { + if (onerror) {{{ makeDynCall('vp', 'onerror') }}}(arg); + return; + } + var buffer = _malloc(byteArray.length); + HEAPU8.set(byteArray, buffer); + {{{ makeDynCall('vppi', 'onload') }}}(arg, buffer, byteArray.length); + _free(buffer); + }); + }); + }, + emscripten_idb_async_store__deps: ['$UTF8ToString', '$callUserCallback'], + 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) {{{ makeDynCall('vp', 'onerror') }}}(arg); + return; + } + if (onstore) {{{ makeDynCall('vp', 'onstore') }}}(arg); + }); + }); + }, + emscripten_idb_async_delete__deps: ['$UTF8ToString', '$callUserCallback'], + emscripten_idb_async_delete: (db, id, arg, ondelete, onerror) => { + {{{ runtimeKeepalivePush() }}}; + IDBStore.deleteFile(UTF8ToString(db), UTF8ToString(id), (error) => { + {{{ runtimeKeepalivePop() }}} + callUserCallback(() => { + if (error) { + if (onerror) {{{ makeDynCall('vp', 'onerror') }}}(arg); + return; + } + if (ondelete) {{{ makeDynCall('vp', 'ondelete') }}}(arg); + }); + }); + }, + emscripten_idb_async_exists__deps: ['$UTF8ToString', '$callUserCallback'], + emscripten_idb_async_exists: (db, id, arg, oncheck, onerror) => { + {{{ runtimeKeepalivePush() }}}; + IDBStore.existsFile(UTF8ToString(db), UTF8ToString(id), (error, exists) => { + {{{ runtimeKeepalivePop() }}} + callUserCallback(() => { + if (error) { + if (onerror) {{{ makeDynCall('vp', 'onerror') }}}(arg); + return; + } + if (oncheck) {{{ makeDynCall('vpi', 'oncheck') }}}(arg, exists); + }); + }); + }, + emscripten_idb_async_clear__deps: ['$UTF8ToString', '$callUserCallback'], + emscripten_idb_async_clear: (db, arg, onclear, onerror) => { + {{{ runtimeKeepalivePush() }}}; + IDBStore.clearStore(UTF8ToString(db), (error) => { + {{{ runtimeKeepalivePop() }}} + callUserCallback(() => { + if (error) { + if (onerror) {{{ makeDynCall('vp', 'onerror') }}}(arg); + return; + } + if (onclear) {{{ makeDynCall('vp', 'onclear') }}}(arg); + }); + }); + }, + +#if ASYNCIFY + emscripten_idb_load__async: true, + emscripten_idb_load__deps: ['malloc'], + emscripten_idb_load: (db, id, pbuffer, pnum, perror) => Asyncify.handleSleep((wakeUp) => { + IDBStore.getFile(UTF8ToString(db), UTF8ToString(id), (error, byteArray) => { + if (error) { + {{{ makeSetValue('perror', 0, '1', 'i32') }}}; + wakeUp(); + return; + } + var buffer = _malloc(byteArray.length); // must be freed by the caller! + HEAPU8.set(byteArray, buffer); + {{{ makeSetValue('pbuffer', 0, 'buffer', '*') }}}; + {{{ makeSetValue('pnum', 0, 'byteArray.length', 'i32') }}}; + {{{ makeSetValue('perror', 0, '0', 'i32') }}}; + wakeUp(); + }); + }), + emscripten_idb_store__async: true, + 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} */ + {{{ makeSetValue('perror', 0, '!!error', 'i32') }}}; + wakeUp(); + }); + }), + emscripten_idb_delete__async: true, + emscripten_idb_delete: (db, id, perror) => Asyncify.handleSleep((wakeUp) => { + IDBStore.deleteFile(UTF8ToString(db), UTF8ToString(id), (error) => { + /** @suppress{checkTypes} */ + {{{ makeSetValue('perror', 0, '!!error', 'i32') }}}; + wakeUp(); + }); + }), + emscripten_idb_exists__async: true, + emscripten_idb_exists: (db, id, pexists, perror) => Asyncify.handleSleep((wakeUp) => { + IDBStore.existsFile(UTF8ToString(db), UTF8ToString(id), (error, exists) => { + /** @suppress{checkTypes} */ + {{{ makeSetValue('pexists', 0, '!!exists', 'i32') }}}; + /** @suppress{checkTypes} */ + {{{ makeSetValue('perror', 0, '!!error', 'i32') }}}; + wakeUp(); + }); + }), + emscripten_idb_clear__async: true, + emscripten_idb_clear: (db, perror) => Asyncify.handleSleep((wakeUp) => { + IDBStore.clearStore(UTF8ToString(db), (error) => { + /** @suppress{checkTypes} */ + {{{ makeSetValue('perror', 0, '!!error', 'i32') }}}; + wakeUp(); + }); + }), + // extra worker methods - proxied + emscripten_idb_load_blob__async: true, + emscripten_idb_load_blob: (db, id, pblob, perror) => Asyncify.handleSleep((wakeUp) => { +#if ASSERTIONS + assert(!IDBStore.pending); +#endif + IDBStore.pending = (msg) => { + IDBStore.pending = null; + var blob = msg.blob; + if (!blob) { + {{{ makeSetValue('perror', 0, '1', 'i32') }}}; + wakeUp(); + return; + } +#if ASSERTIONS + assert(blob instanceof Blob); +#endif + var blobId = IDBStore.blobs.length; + IDBStore.blobs.push(blob); + {{{ makeSetValue('pblob', 0, 'blobId', 'i32') }}}; + wakeUp(); + }; + postMessage({ + target: 'IDBStore', + method: 'loadBlob', + db: UTF8ToString(db), + id: UTF8ToString(id) + }); + }), + emscripten_idb_store_blob__async: true, + emscripten_idb_store_blob: (db, id, ptr, num, perror) => Asyncify.handleSleep((wakeUp) => { +#if ASSERTIONS + assert(!IDBStore.pending); +#endif + IDBStore.pending = (msg) => { + IDBStore.pending = null; + {{{ makeSetValue('perror', 0, '!!msg.error', 'i32') }}}; + wakeUp(); + }; + postMessage({ + target: 'IDBStore', + method: 'storeBlob', + db: UTF8ToString(db), + id: UTF8ToString(id), + blob: new Blob([new Uint8Array(HEAPU8.subarray(ptr, ptr+num))]) + }); + }), + 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_free_blob: (blobId) => { +#if ASSERTIONS + assert(IDBStore.blobs[blobId]); +#endif + IDBStore.blobs[blobId] = null; + }, +#else + emscripten_idb_load: (db, id, pbuffer, pnum, perror) => { + abort('Please compile your program with async support in order to use synchronous operations like emscripten_idb_load, etc.'); + }, + emscripten_idb_store: (db, id, ptr, num, perror) => { + abort('Please compile your program with async support in order to use synchronous operations like emscripten_idb_store, etc.'); + }, + emscripten_idb_delete: (db, id, perror) => { + abort('Please compile your program with async support in order to use synchronous operations like emscripten_idb_delete, etc.'); + }, + emscripten_idb_exists: (db, id, pexists, perror) => { + abort('Please compile your program with async support in order to use synchronous operations like emscripten_idb_exists, etc.'); + }, + emscripten_idb_clear: (db, perror) => { + abort('Please compile your program with async support in order to use synchronous operations like emscripten_idb_clear, etc.'); + }, +#endif // ASYNCIFY +}; + +autoAddDeps(LibraryIDBStore, '$IDBStore'); +addToLibrary(LibraryIDBStore); diff --git a/src/library_int53.js b/src/lib/libint53.js similarity index 100% rename from src/library_int53.js rename to src/lib/libint53.js diff --git a/src/library_jsfilefs.js b/src/lib/libjsfilefs.js similarity index 100% rename from src/library_jsfilefs.js rename to src/lib/libjsfilefs.js diff --git a/src/lib/liblegacy.js b/src/lib/liblegacy.js new file mode 100644 index 0000000000000..abf99d5c92694 --- /dev/null +++ b/src/lib/liblegacy.js @@ -0,0 +1,154 @@ +/** + * @license + * Copyright 2010 The Emscripten Authors + * SPDX-License-Identifier: MIT + * + * Legacy library symbols that are no longer used by emscripten itself but + * could have external users. + * + * Symbols in this file are not available in `-sSTRICT` mode. + * + * Any usage of symbols in this file will result in a `-Wdeprecated` warning. + * + * Symbol in this file should be removed after "enough time" has passed such + * that all external users have been able to transision away. + */ + +legacyFuncs = { + $ALLOC_NORMAL: 0, // Tries to use _malloc() + $ALLOC_STACK: 1, // Lives for the duration of the current function call + + /** + * allocate(): This function is no longer used by emscripten but is kept around to avoid + * breaking external users. + * You should normally not use allocate(), and instead allocate + * memory using _malloc()/stackAlloc(), initialize it with + * setValue(), and so forth. + * @param {(Uint8Array|Array)} slab: An array of data. + * @param {number=} allocator : How to allocate memory, see ALLOC_* + */ + $allocate__deps: ['$ALLOC_STACK', 'malloc', '$stackAlloc'], + $allocate: (slab, allocator) => { + var ret; + #if ASSERTIONS + assert(typeof allocator == 'number', 'allocate no longer takes a type argument') + assert(typeof slab != 'number', 'allocate no longer takes a number as arg0') + #endif + + if (allocator == ALLOC_STACK) { + ret = stackAlloc(slab.length); + } else { + ret = _malloc(slab.length); + } + + if (!slab.subarray && !slab.slice) { + slab = new Uint8Array(slab); + } + HEAPU8.set(slab, ret); + return ret; + }, + + // Deprecated: This function should not be called because it is unsafe and + // does not provide a maximum length limit of how many bytes it is allowed to + // write. Prefer calling the function stringToUTF8Array() instead, which takes + // in a maximum length that can be used to be secure from out of bounds + // writes. + $writeStringToMemory__docs: '/** @deprecated @param {boolean=} dontAddNull */', + $writeStringToMemory__deps: ['$lengthBytesUTF8', '$stringToUTF8'], + $writeStringToMemory: (string, buffer, dontAddNull) => { + warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); + + var /** @type {number} */ lastChar, /** @type {number} */ end; + if (dontAddNull) { + // stringToUTF8 always appends null. If we don't want to do that, remember the + // character that existed at the location where the null will be placed, and restore + // that after the write (below). + end = buffer + lengthBytesUTF8(string); + lastChar = HEAP8[end]; + } + stringToUTF8(string, buffer, Infinity); + if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. + }, + + // Deprecated: Use stringToAscii + $writeAsciiToMemory__docs: '/** @param {boolean=} dontAddNull */', + $writeAsciiToMemory: (str, buffer, dontAddNull) => { + for (var i = 0; i < str.length; ++i) { +#if ASSERTIONS + assert(str.charCodeAt(i) === (str.charCodeAt(i) & 0xff)); +#endif + {{{ makeSetValue('buffer++', 0, 'str.charCodeAt(i)', 'i8') }}}; + } + // Null-terminate the string + if (!dontAddNull) {{{ makeSetValue('buffer', 0, 0, 'i8') }}}; + }, + + $allocateUTF8__deps: ['$stringToNewUTF8'], + $allocateUTF8: (...args) => stringToNewUTF8(...args), + $allocateUTF8OnStack__deps: ['$stringToUTF8OnStack'], + $allocateUTF8OnStack: (...args) => stringToUTF8OnStack(...args), + +#if LINK_AS_CXX + $demangle__deps: ['$withStackSave', '__cxa_demangle', 'free', '$stringToUTF8OnStack'], + $demangle: (func) => { + // If demangle has failed before, stop demangling any further function names + // This avoids an infinite recursion with malloc()->abort()->stackTrace()->demangle()->malloc()->... + demangle.recursionGuard = (demangle.recursionGuard|0)+1; + if (demangle.recursionGuard > 1) return func; + return withStackSave(() => { + try { + var s = func; + if (s.startsWith('__Z')) + s = s.slice(1); + var buf = stringToUTF8OnStack(s); + var status = stackAlloc(4); + var ret = ___cxa_demangle(buf, 0, 0, status); + if ({{{ makeGetValue('status', '0', 'i32') }}} === 0 && ret) { + return UTF8ToString(ret); + } + // otherwise, libcxxabi failed + } catch(e) { + } finally { + _free(ret); + if (demangle.recursionGuard < 2) --demangle.recursionGuard; + } + // failure when using libcxxabi, don't demangle + return func; + }); + }, +#endif + + $stackTrace__deps: ['$jsStackTrace'], + $stackTrace: () => { + var js = jsStackTrace(); + if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace'](); + return js; + }, + + // Legacy names for runtime `out`/`err` symbols. + $print: '=out', + $printErr: '=err', + + // Converts a JS string to an integer base-10. Despite _s, which + // suggests signaling error handling, this returns NaN on error. + // (This was a mistake in the original implementation, and kept + // to avoid breakage.) + $jstoi_s: 'Number', + + $getNativeTypeSize__deps: ['$POINTER_SIZE'], + $getNativeTypeSize: {{{ getNativeTypeSize }}}, +}; + +if (WARN_DEPRECATED && !INCLUDE_FULL_LIBRARY) { + for (const name of Object.keys(legacyFuncs)) { + if (!isDecorator(name)) { + const depsKey = `${name}__deps`; + legacyFuncs[depsKey] ??= [] + legacyFuncs[depsKey].push(() => { + warn(`JS library symbol '${name}' is deprecated. Please open a bug if you have a continuing need for this symbol [-Wdeprecated]`); + }); + } + } +} + +addToLibrary(legacyFuncs); diff --git a/src/lib/liblittle_endian_heap.js b/src/lib/liblittle_endian_heap.js new file mode 100644 index 0000000000000..1f97eb6530010 --- /dev/null +++ b/src/lib/liblittle_endian_heap.js @@ -0,0 +1,127 @@ +var LibraryLittleEndianHeap = { + $LE_HEAP_STORE_U16: (byteOffset, value) => + HEAP_DATA_VIEW.setUint16(byteOffset, value, true), + + $LE_HEAP_STORE_I16: (byteOffset, value) => + HEAP_DATA_VIEW.setInt16(byteOffset, value, true), + + $LE_HEAP_STORE_U32: (byteOffset, value) => + HEAP_DATA_VIEW.setUint32(byteOffset, value, true), + + $LE_HEAP_STORE_I32: (byteOffset, value) => + HEAP_DATA_VIEW.setInt32(byteOffset, value, true), + + $LE_HEAP_STORE_U64: (byteOffset, value) => + HEAP_DATA_VIEW.setBigUint64(byteOffset, value, true), + + $LE_HEAP_STORE_I64: (byteOffset, value) => + HEAP_DATA_VIEW.setBigInt64(byteOffset, value, true), + + $LE_HEAP_STORE_F32: (byteOffset, value) => + HEAP_DATA_VIEW.setFloat32(byteOffset, value, true), + + $LE_HEAP_STORE_F64: (byteOffset, value) => + HEAP_DATA_VIEW.setFloat64(byteOffset, value, true), + + $LE_HEAP_LOAD_U16: (byteOffset) => + HEAP_DATA_VIEW.getUint16(byteOffset, true), + + $LE_HEAP_LOAD_I16: (byteOffset) => + HEAP_DATA_VIEW.getInt16(byteOffset, true), + + $LE_HEAP_LOAD_U32: (byteOffset) => + HEAP_DATA_VIEW.getUint32(byteOffset, true), + + $LE_HEAP_LOAD_I32: (byteOffset) => + HEAP_DATA_VIEW.getInt32(byteOffset, true), + + $LE_HEAP_LOAD_U64: (byteOffset) => + HEAP_DATA_VIEW.getBigUint64(byteOffset, true), + + $LE_HEAP_LOAD_I64: (byteOffset) => + HEAP_DATA_VIEW.getBigInt64(byteOffset, true), + + $LE_HEAP_LOAD_F32: (byteOffset) => + HEAP_DATA_VIEW.getFloat32(byteOffset, true), + + $LE_HEAP_LOAD_F64: (byteOffset) => + HEAP_DATA_VIEW.getFloat64(byteOffset, true), + + $LE_ATOMICS_NATIVE_BYTE_ORDER__postset: ` +LE_ATOMICS_NATIVE_BYTE_ORDER = (new Int8Array(new Int16Array([1]).buffer)[0] === 1) + ? [ /* little endian */ + (x => x), + (x => x), + undefined, + (x => x), + ] + : [ /* big endian */ + (x => x), + (x => (((x & 0xff00) << 8) | ((x & 0xff) << 24)) >> 16), + undefined, + (x => ((x >> 24) & 0xff) | ((x >> 8) & 0xff00) | ((x & 0xff00) << 8) | ((x & 0xff) << 24)), + ]; +function LE_HEAP_UPDATE() { + HEAPU16.unsigned = (x => x & 0xffff); + HEAPU32.unsigned = (x => x >>> 0); +} + `, + $LE_ATOMICS_NATIVE_BYTE_ORDER: [], + + $LE_ATOMICS_ADD: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.add(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_AND: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.and(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_COMPAREEXCHANGE: (heap, offset, expected, replacement) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.compareExchange(heap, offset, order(expected), order(replacement))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_EXCHANGE: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.exchange(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_ISLOCKFREE: (size) => Atomics.isLockFree(size), + $LE_ATOMICS_LOAD: (heap, offset) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.load(heap, offset)); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_NOTIFY: (heap, offset, count) => Atomics.notify(heap, offset, count), + $LE_ATOMICS_OR: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.or(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_STORE: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + Atomics.store(heap, offset, order(value)); + }, + $LE_ATOMICS_SUB: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.sub(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, + $LE_ATOMICS_WAIT: (heap, offset, value, timeout) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + return Atomics.wait(heap, offset, order(value), timeout); + }, + $LE_ATOMICS_WAITASYNC: (heap, offset, value, timeout) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + return Atomics.waitAsync(heap, offset, order(value), timeout); + }, + $LE_ATOMICS_XOR: (heap, offset, value) => { + const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1]; + const res = order(Atomics.xor(heap, offset, order(value))); + return heap.unsigned ? heap.unsigned(res) : res; + }, +} + +addToLibrary(LibraryLittleEndianHeap); diff --git a/src/lib/liblz4.js b/src/lib/liblz4.js new file mode 100644 index 0000000000000..8e4a85dc6748b --- /dev/null +++ b/src/lib/liblz4.js @@ -0,0 +1,209 @@ +/** + * @license + * Copyright 2015 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +#if LZ4 +addToLibrary({ + $LZ4__deps: ['$FS', '$preloadPlugins', '$getUniqueRunDependency', '$addRunDependency', '$removeRunDependency'], + $LZ4: { + DIR_MODE: {{{ cDefs.S_IFDIR | 0o777 }}}, + FILE_MODE: {{{ cDefs.S_IFREG | 0o777 }}}, + CHUNK_SIZE: -1, + codec: null, + init() { + if (LZ4.codec) return; + LZ4.codec = (() => { + {{{ read('../third_party/mini-lz4.js') }}}; + return MiniLZ4; + })(); + LZ4.CHUNK_SIZE = LZ4.codec.CHUNK_SIZE; + }, + loadPackage(pack, preloadPlugin) { + LZ4.init(); + var compressedData = pack['compressedData'] || LZ4.codec.compressPackage(pack['data']); + assert(compressedData['cachedIndexes'].length === compressedData['cachedChunks'].length); + for (var i = 0; i < compressedData['cachedIndexes'].length; i++) { + compressedData['cachedIndexes'][i] = -1; + compressedData['cachedChunks'][i] = compressedData['data'].subarray(compressedData['cachedOffset'] + i*LZ4.CHUNK_SIZE, + compressedData['cachedOffset'] + (i+1)*LZ4.CHUNK_SIZE); + assert(compressedData['cachedChunks'][i].length === LZ4.CHUNK_SIZE); + } + for (var file of pack['metadata'].files) { + var dir = PATH.dirname(file.filename); + var name = PATH.basename(file.filename); + FS.createPath('', dir, true, true); + var parent = FS.analyzePath(dir).object; + LZ4.createNode(parent, name, LZ4.FILE_MODE, 0, { + compressedData, + start: file.start, + end: file.end, + }); + } + // Preload files if necessary. This code is largely similar to + // createPreloadedFile in library_fs.js. However, a main difference here + // is that we only decompress the file if it can be preloaded. + // Abstracting out the common parts seems to be more effort than it is + // worth. + if (preloadPlugin) { + Browser.init(); + for (var file of pack['metadata'].files) { + var fullname = file.filename; + for (var plugin of preloadPlugins) { + if (plugin['canHandle'](fullname)) { + var dep = getUniqueRunDependency('fp ' + fullname); + addRunDependency(dep); + var finish = () => removeRunDependency(dep); + var byteArray = FS.readFile(fullname); +#if ASSERTIONS + assert(plugin['handle'].constructor.name === 'AsyncFunction', 'Filesystem plugin handlers must be async functions (See #24914)') +#endif + plugin['handle'](byteArray, fullname).then(finish).catch(finish); + break; + } + } + } + } + }, + createNode(parent, name, mode, dev, contents, mtime) { + var node = FS.createNode(parent, name, mode); + node.mode = mode; + node.node_ops = LZ4.node_ops; + node.stream_ops = LZ4.stream_ops; + this.atime = this.mtime = this.ctime = (mtime || new Date).getTime(); + assert(LZ4.FILE_MODE !== LZ4.DIR_MODE); + if (mode === LZ4.FILE_MODE) { + node.size = contents.end - contents.start; + node.contents = contents; + } else { + node.size = 4096; + node.contents = {}; + } + if (parent) { + parent.contents[name] = node; + } + return node; + }, + node_ops: { + getattr(node) { + return { + dev: 1, + ino: node.id, + mode: node.mode, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + size: node.size, + atime: new Date(node.atime), + mtime: new Date(node.mtime), + ctime: new Date(node.ctime), + blksize: 4096, + blocks: Math.ceil(node.size / 4096), + }; + }, + setattr(node, attr) { + for (const key of ['mode', 'atime', 'mtime', 'ctime']) { + if (attr[key]) { + node[key] = attr[key]; + } + } + }, + lookup(parent, name) { + throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); + }, + mknod(parent, name, mode, dev) { + throw new FS.ErrnoError({{{ cDefs.EPERM }}}); + }, + rename(oldNode, newDir, newName) { + throw new FS.ErrnoError({{{ cDefs.EPERM }}}); + }, + unlink(parent, name) { + throw new FS.ErrnoError({{{ cDefs.EPERM }}}); + }, + rmdir(parent, name) { + throw new FS.ErrnoError({{{ cDefs.EPERM }}}); + }, + readdir(node) { + throw new FS.ErrnoError({{{ cDefs.EPERM }}}); + }, + symlink(parent, newName, oldPath) { + throw new FS.ErrnoError({{{ cDefs.EPERM }}}); + }, + }, + stream_ops: { + read(stream, buffer, offset, length, position) { + //out('LZ4 read ' + [offset, length, position]); + length = Math.min(length, stream.node.size - position); + if (length <= 0) return 0; + var contents = stream.node.contents; + var compressedData = contents.compressedData; + var written = 0; + while (written < length) { + var start = contents.start + position + written; // start index in uncompressed data + var desired = length - written; + //out('current read: ' + ['start', start, 'desired', desired]); + var chunkIndex = Math.floor(start / LZ4.CHUNK_SIZE); + var compressedStart = compressedData['offsets'][chunkIndex]; + var compressedSize = compressedData['sizes'][chunkIndex]; + var currChunk; + if (compressedData['successes'][chunkIndex]) { + var found = compressedData['cachedIndexes'].indexOf(chunkIndex); + if (found >= 0) { + currChunk = compressedData['cachedChunks'][found]; + } else { + // decompress the chunk + compressedData['cachedIndexes'].pop(); + compressedData['cachedIndexes'].unshift(chunkIndex); + currChunk = compressedData['cachedChunks'].pop(); + compressedData['cachedChunks'].unshift(currChunk); + if (compressedData['debug']) { + out('decompressing chunk ' + chunkIndex); + Module['decompressedChunks'] = (Module['decompressedChunks'] || 0) + 1; + } + var compressed = compressedData['data'].subarray(compressedStart, compressedStart + compressedSize); + //var t = Date.now(); + var originalSize = LZ4.codec.uncompress(compressed, currChunk); + //out('decompress time: ' + (Date.now() - t)); + if (chunkIndex < compressedData['successes'].length-1) assert(originalSize === LZ4.CHUNK_SIZE); // all but the last chunk must be full-size + } + } else { + // uncompressed + currChunk = compressedData['data'].subarray(compressedStart, compressedStart + LZ4.CHUNK_SIZE); + } + var startInChunk = start % LZ4.CHUNK_SIZE; + var endInChunk = Math.min(startInChunk + desired, LZ4.CHUNK_SIZE); + buffer.set(currChunk.subarray(startInChunk, endInChunk), offset + written); + var currWritten = endInChunk - startInChunk; + written += currWritten; + } + return written; + }, + write(stream, buffer, offset, length, position) { + throw new FS.ErrnoError({{{ cDefs.EIO }}}); + }, + llseek(stream, offset, whence) { + var position = offset; + if (whence === {{{ cDefs.SEEK_CUR }}}) { + position += stream.position; + } else if (whence === {{{ cDefs.SEEK_END }}}) { + if (FS.isFile(stream.node.mode)) { + position += stream.node.size; + } + } + if (position < 0) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + return position; + }, + }, + }, +}); +if (LibraryManager.library['$FS__deps']) { + LibraryManager.library['$FS__deps'].push('$LZ4'); // LZ4=1, so auto-include us +} else { + warn('FS does not seem to be in use (no preloaded files etc.), LZ4 will not do anything'); +} +#endif + diff --git a/src/library_math.js b/src/lib/libmath.js similarity index 100% rename from src/library_math.js rename to src/lib/libmath.js diff --git a/src/lib/libmemfs.js b/src/lib/libmemfs.js new file mode 100644 index 0000000000000..87306d9d04cad --- /dev/null +++ b/src/lib/libmemfs.js @@ -0,0 +1,365 @@ +/** + * @license + * Copyright 2013 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + $MEMFS__deps: ['$FS', '$mmapAlloc'], + $MEMFS: { + ops_table: null, + mount(mount) { + return MEMFS.createNode(null, '/', {{{ cDefs.S_IFDIR | 0o777 }}}, 0); + }, + createNode(parent, name, mode, dev) { + if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { + // no supported + throw new FS.ErrnoError({{{ cDefs.EPERM }}}); + } + 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; + }, + + // Given a file node, returns its file data converted to a typed array. + 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); + }, + + // Allocates a new backing store for the given node so that it can fit at least newSize amount of bytes. + // May allocate more, to provide automatic geometric increase and amortized linear performance appending writes. + // Never shrinks the storage. + 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. + }, + + // Performs an exact resize of the backing file storage to the given size, if the size is not exactly this, the storage is fully reallocated. + 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) { +#if ASSERTIONS + throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); +#else + // 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({{{ cDefs.ENOENT }}}); + /** @suppress {checkTypes} */ + MEMFS.doesNotExistError.stack = ''; + } + throw MEMFS.doesNotExistError; +#endif + }, + 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({{{ cDefs.ENOTEMPTY }}}); + } + } + 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({{{ cDefs.ENOTEMPTY }}}); + } + 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 | {{{ cDefs.S_IFLNK }}}, 0); + node.link = oldpath; + return node; + }, + readlink(node) { + if (!FS.isLink(node.mode)) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + 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 ASSERTIONS + assert(size >= 0); +#endif + 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; + }, + + // Writes the byte range (buffer[offset], buffer[offset+length]) to offset 'position' into the file pointed by 'stream' + // canOwn: A boolean that tells if this function can take ownership of the passed in buffer from the subbuffer portion + // that the typed array view 'buffer' points to. The underlying ArrayBuffer can be larger than that, but + // canOwn=true will not take ownership of the portion outside the bytes addressed by the view. This means that + // with canOwn=true, creating a copy of the bytes is avoided, but the caller shouldn't touch the passed in range + // of bytes anymore since their contents now represent file data inside the filesystem. + write(stream, buffer, offset, length, position, canOwn) { +#if ASSERTIONS + // The data buffer should be a typed array view + assert(!(buffer instanceof ArrayBuffer)); +#endif +#if ALLOW_MEMORY_GROWTH + // 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; + } +#endif // ALLOW_MEMORY_GROWTH + + 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) { +#if ASSERTIONS + assert(position === 0, 'canOwn must imply no weird position inside the file'); +#endif + 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 === {{{ cDefs.SEEK_CUR }}}) { + position += stream.position; + } else if (whence === {{{ cDefs.SEEK_END }}}) { + if (FS.isFile(stream.node.mode)) { + position += stream.node.usedBytes; + } + } + if (position < 0) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + return position; + }, + mmap(stream, length, position, prot, flags) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError({{{ cDefs.ENODEV }}}); + } + var ptr; + var allocated; + var contents = stream.node.contents; + // Only make a new copy when MAP_PRIVATE is specified. + if (!(flags & {{{ cDefs.MAP_PRIVATE }}}) && 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({{{ cDefs.ENOMEM }}}); + } + 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; + } + } + } +}); + diff --git a/src/library_memoryprofiler.js b/src/lib/libmemoryprofiler.js similarity index 100% rename from src/library_memoryprofiler.js rename to src/lib/libmemoryprofiler.js diff --git a/src/lib/libnodefs.js b/src/lib/libnodefs.js new file mode 100644 index 0000000000000..f6de7ac661643 --- /dev/null +++ b/src/lib/libnodefs.js @@ -0,0 +1,320 @@ +/** + * @license + * Copyright 2013 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ +#if WASMFS + $NODEFS__deps: ['$stringToUTF8OnStack', 'wasmfs_create_node_backend'], + $NODEFS: { + createBackend(opts) { + return _wasmfs_create_node_backend(stringToUTF8OnStack(opts.root)); + } + } +#else + $NODEFS__deps: ['$FS', '$PATH', '$ERRNO_CODES', '$mmapAlloc'], + $NODEFS__postset: 'if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit(); }', + $NODEFS: { + isWindows: false, + staticInit() { + NODEFS.isWindows = !!process.platform.match(/^win/); + var flags = process.binding("constants")["fs"]; + NODEFS.flagsForNodeMap = { + "{{{ cDefs.O_APPEND }}}": flags["O_APPEND"], + "{{{ cDefs.O_CREAT }}}": flags["O_CREAT"], + "{{{ cDefs.O_EXCL }}}": flags["O_EXCL"], + "{{{ cDefs.O_NOCTTY }}}": flags["O_NOCTTY"], + "{{{ cDefs.O_RDONLY }}}": flags["O_RDONLY"], + "{{{ cDefs.O_RDWR }}}": flags["O_RDWR"], + "{{{ cDefs.O_DSYNC }}}": flags["O_SYNC"], + "{{{ cDefs.O_TRUNC }}}": flags["O_TRUNC"], + "{{{ cDefs.O_WRONLY }}}": flags["O_WRONLY"], + "{{{ cDefs.O_NOFOLLOW }}}": flags["O_NOFOLLOW"], + }; +#if ASSERTIONS + // The 0 define must match on both sides, as otherwise we would not + // know to add it. + assert(NODEFS.flagsForNodeMap["0"] === 0); +#endif + }, + convertNodeCode(e) { + var code = e.code; +#if ASSERTIONS + assert(code in ERRNO_CODES, `unexpected node error code: ${code} (${e})`); +#endif + 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({{{ cDefs.EINVAL }}}); + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + mount(mount) { +#if ASSERTIONS + assert(ENVIRONMENT_IS_NODE); +#endif + 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({{{ cDefs.EINVAL }}}); + } + 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 & {{{ cDefs.S_IRUGO }}}) >> 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); + }, + // This maps the integer permission modes from http://linux.die.net/man/3/open + // to node.js-specific file open permission strings at http://nodejs.org/api/fs.html#fs_fs_open_path_flags_mode_callback + flagsForNode(flags) { + flags &= ~{{{ cDefs.O_PATH }}}; // Ignore this flag from musl, otherwise node.js fails to open the file. + flags &= ~{{{ cDefs.O_NONBLOCK }}}; // Ignore this flag from musl, otherwise node.js fails to open the file. + flags &= ~{{{ cDefs.O_LARGEFILE }}}; // Ignore this flag from musl, otherwise node.js fails to open the file. + flags &= ~{{{ cDefs.O_CLOEXEC }}}; // Some applications may pass it; it makes no sense for a single process. + flags &= ~{{{ cDefs.O_DIRECTORY }}}; // 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({{{ cDefs.EINVAL }}}); + } + 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 & {{{ cDefs.S_IRUGO }}}) >> 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 + }; + }, + // Common code for both node and stream setattr + // For node getatrr: + // - arg is a native path + // - chmod, utimes, truncate are fs.chmodSync, fs.utimesSync, fs.truncateSync + // For stream getatrr: + // - arg is a native file descriptor + // - chmod, utimes, truncate are fs.fchmodSync, fs.futimesSync, fs.ftruncateSync + 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 &= {{{ cDefs.S_IRUSR | cDefs.S_IWUSR }}}; + } + 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({{{ cDefs.ENOSYS }}}); + } + 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 === {{{ cDefs.SEEK_CUR }}}) { + position += stream.position; + } else if (whence === {{{ cDefs.SEEK_END }}}) { + if (FS.isFile(stream.node.mode)) { + NODEFS.tryFSOperation(() => { + var stat = fs.fstatSync(stream.nfd); + position += stat.size; + }); + } + } + + if (position < 0) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + + return position; + }, + mmap(stream, length, position, prot, flags) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError({{{ cDefs.ENODEV }}}); + } + + 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; + } + } + } +#endif +}); diff --git a/src/lib/libnodepath.js b/src/lib/libnodepath.js new file mode 100644 index 0000000000000..209b9e9340620 --- /dev/null +++ b/src/lib/libnodepath.js @@ -0,0 +1,36 @@ +/** + * @license + * Copyright 2022 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +// This implementation ensures that Windows-style paths are being +// used when running on a Windows operating system - see: +// https://nodejs.org/api/path.html#path_windows_vs_posix +// It's only used/needed when linking with `-sNODERAWFS`, as that +// will replace all normal filesystem access with direct Node.js +// operations. Hence, using `nodePath` should be safe here. + +addToLibrary({ + $nodePath: "require('path')", + $PATH__deps: ['$nodePath'], + $PATH: `{ + isAbs: nodePath.isAbsolute, + normalize: nodePath.normalize, + dirname: nodePath.dirname, + basename: nodePath.basename, + join: nodePath.join, + join2: nodePath.join, + }`, + // The FS-using parts are split out into a separate object, so simple path + // usage does not require the FS. + $PATH_FS__deps: ['$FS', '$nodePath'], + $PATH_FS__docs: '/** @type{{resolve: function(...*)}} */', + $PATH_FS: { + resolve: (...paths) => { + paths.unshift(FS.cwd()); + return nodePath.posix.resolve(...paths); + }, + relative: (from, to) => nodePath.posix.relative(from || FS.cwd(), to || FS.cwd()), + } +}); diff --git a/src/lib/libnoderawfs.js b/src/lib/libnoderawfs.js new file mode 100644 index 0000000000000..c7e86e95b1566 --- /dev/null +++ b/src/lib/libnoderawfs.js @@ -0,0 +1,258 @@ +/** + * @license + * Copyright 2018 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + $NODERAWFS__deps: ['$ERRNO_CODES', '$FS', '$NODEFS', '$mmapAlloc', '$FS_modeStringToFlags'], + $NODERAWFS__postset: ` + if (!ENVIRONMENT_IS_NODE) { + throw new Error("NODERAWFS is currently only supported on Node.js environment.") + } + var _wrapNodeError = function(func) { + return function(...args) { + try { + return func(...args) + } catch (e) { + if (e.code) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + throw e; + } + } + }; + // Use this to reference our in-memory filesystem + /** @suppress {partialAlias} */ + var VFS = {...FS}; + // Wrap the whole in-memory filesystem API with + // our Node.js based functions + for (var _key in NODERAWFS) { + FS[_key] = _wrapNodeError(NODERAWFS[_key]); + }`, + $NODERAWFS: { + lookup(parent, name) { +#if ASSERTIONS + assert(parent) + assert(parent.path) +#endif + return FS.lookupPath(`${parent.path}/${name}`).node; + }, + lookupPath(path, opts = {}) { + if (opts.parent) { + path = PATH.dirname(path); + } + var st = fs.lstatSync(path); + var mode = NODEFS.getMode(path); + return { path, node: { id: st.ino, mode, node_ops: NODERAWFS, path }}; + }, + createStandardStreams() { + // FIXME: tty is set to true to appease isatty(), the underlying ioctl syscalls still needs to be implemented, see issue #22264. + FS.createStream({ nfd: 0, position: 0, path: '/dev/stdin', flags: 0, tty: true, seekable: false }, 0); + var paths = [,'/dev/stdout', '/dev/stderr']; + for (var i = 1; i < 3; i++) { + FS.createStream({ nfd: i, position: 0, path: paths[i], flags: {{{ cDefs.O_TRUNC | cDefs.O_CREAT | cDefs.O_WRONLY }}}, tty: true, seekable: false }, i); + } + }, + // generic function for all node creation + cwd() { return process.cwd(); }, + chdir(...args) { process.chdir(...args); }, + mknod(path, mode) { + if (FS.isDir(path)) { + fs.mkdirSync(path, mode); + } else { + fs.writeFileSync(path, '', { mode: mode }); + } + }, + mkdir(...args) { fs.mkdirSync(...args); }, + symlink(...args) { fs.symlinkSync(...args); }, + rename(...args) { fs.renameSync(...args); }, + rmdir(...args) { fs.rmdirSync(...args); }, + readdir(...args) { return ['.', '..'].concat(fs.readdirSync(...args)); }, + unlink(...args) { fs.unlinkSync(...args); }, + readlink(...args) { return fs.readlinkSync(...args); }, + stat(path, dontFollow) { + var stat = dontFollow ? fs.lstatSync(path) : fs.statSync(path); + if (NODEFS.isWindows) { + // Windows does not report the 'x' permission bit, so propagate read + // bits to execute bits. + stat.mode |= (stat.mode & {{{ cDefs.S_IRUGO }}}) >> 2; + } + return stat; + }, + fstat(fd) { + var stream = FS.getStreamChecked(fd); + return fs.fstatSync(stream.nfd); + }, + statfs(path) { + // Node's fs.statfsSync API doesn't provide these attributes so include + // some defaults. + var defaults = { + fsid: 42, + flags: 2, + namelen: 255, + } + return Object.assign(defaults, fs.statfsSync(path)); + }, + statfsStream(stream) { + return FS.statfs(stream.path); + }, + chmod(path, mode, dontFollow) { + mode &= {{{ cDefs.S_IALLUGO }}}; + 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 &= {{{ cDefs.S_IRUSR | cDefs.S_IWUSR }}}; + } + if (dontFollow && fs.lstatSync(path).isSymbolicLink()) { + // Node (and indeed linux) does not support chmod on symlinks + // https://nodejs.org/api/fs.html#fslchmodsyncpath-mode + throw new FS.ErrnoError({{{ cDefs.EOPNOTSUPP }}}); + } + fs.chmodSync(path, mode); + }, + fchmod(fd, mode) { + var stream = FS.getStreamChecked(fd); + fs.fchmodSync(stream.nfd, mode); + }, + chown(...args) { fs.chownSync(...args); }, + fchown(fd, owner, group) { + var stream = FS.getStreamChecked(fd); + fs.fchownSync(stream.nfd, owner, group); + }, + truncate(path, len) { + // See https://github.com/nodejs/node/issues/35632 + if (len < 0) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + return fs.truncateSync(path, len); + }, + ftruncate(fd, len) { + // See https://github.com/nodejs/node/issues/35632 + if (len < 0) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + var stream = FS.getStreamChecked(fd); + fs.ftruncateSync(stream.nfd, len); + }, + utime(path, atime, mtime) { + // null here for atime or mtime means UTIME_OMIT was passed. Since node + // doesn't support this concept we need to first find the existing + // timestamps in order to preserve them. + if ((atime === null) || (mtime === null)) { + var st = fs.statSync(path); + atime ||= st.atimeMs; + mtime ||= st.mtimeMs; + } + fs.utimesSync(path, atime/1000, mtime/1000); + }, + open(path, flags, mode) { + if (typeof flags == "string") { + flags = FS_modeStringToFlags(flags) + } + var pathTruncated = path.split('/').map((s) => s.slice(0, 255)).join('/'); + var nfd = fs.openSync(pathTruncated, NODEFS.flagsForNode(flags), mode); + var st = fs.fstatSync(nfd); + if (flags & {{{ cDefs.O_DIRECTORY }}} && !st.isDirectory()) { + fs.closeSync(nfd); + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } + var newMode = NODEFS.getMode(pathTruncated); + var node = { id: st.ino, mode: newMode, node_ops: NODERAWFS, path } + return FS.createStream({ nfd, position: 0, path, flags, node, seekable: true }); + }, + createStream(stream, fd) { + // Call the original FS.createStream + var rtn = VFS.createStream(stream, fd); + if (typeof rtn.shared.refcnt == 'undefined') { + rtn.shared.refcnt = 1; + } else { + rtn.shared.refcnt++; + } + return rtn; + }, + close(stream) { + VFS.closeStream(stream.fd); + // Don't close stdin/stdout/stderr since they are used by node itself. + if (!stream.stream_ops && --stream.shared.refcnt <= 0 && stream.nfd > 2) { + // This stream is created by our Node.js filesystem, close the + // native file descriptor when its reference count drops to 0. + fs.closeSync(stream.nfd); + } + }, + llseek(stream, offset, whence) { + if (stream.stream_ops) { + // this stream is created by in-memory filesystem + return VFS.llseek(stream, offset, whence); + } + var position = offset; + if (whence === {{{ cDefs.SEEK_CUR }}}) { + position += stream.position; + } else if (whence === {{{ cDefs.SEEK_END }}}) { + position += fs.fstatSync(stream.nfd).size; + } else if (whence !== {{{ cDefs.SEEK_SET }}}) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + + if (position < 0) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + stream.position = position; + return position; + }, + read(stream, buffer, offset, length, position) { + if (stream.stream_ops) { + // this stream is created by in-memory filesystem + return VFS.read(stream, buffer, offset, length, position); + } + var seeking = typeof position != 'undefined'; + if (!seeking && stream.seekable) position = stream.position; + var bytesRead = fs.readSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), 0, length, position); + // update position marker when non-seeking + if (!seeking) stream.position += bytesRead; + return bytesRead; + }, + write(stream, buffer, offset, length, position) { + if (stream.stream_ops) { + // this stream is created by in-memory filesystem + return VFS.write(stream, buffer, offset, length, position); + } + if (stream.flags & +"{{{ cDefs.O_APPEND }}}") { + // seek to the end before writing in append mode + FS.llseek(stream, 0, +"{{{ cDefs.SEEK_END }}}"); + } + var seeking = typeof position != 'undefined'; + if (!seeking && stream.seekable) position = stream.position; + var bytesWritten = fs.writeSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), 0, length, position); + // update position marker when non-seeking + if (!seeking) stream.position += bytesWritten; + return bytesWritten; + }, + mmap(stream, length, position, prot, flags) { + if (!length) { + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + } + if (stream.stream_ops) { + // this stream is created by in-memory filesystem + return VFS.mmap(stream, length, position, prot, flags); + } + + var ptr = mmapAlloc(length); + FS.read(stream, HEAP8, ptr, length, position); + return { ptr, allocated: true }; + }, + msync(stream, buffer, offset, length, mmapFlags) { + if (stream.stream_ops) { + // this stream is created by in-memory filesystem + return VFS.msync(stream, buffer, offset, length, mmapFlags); + } + + FS.write(stream, buffer, 0, length, offset); + // should we check if bytesWritten and length are the same? + return 0; + }, + ioctl() { + throw new FS.ErrnoError({{{ cDefs.ENOTTY }}}); + } + } +}); diff --git a/src/lib/libopenal.js b/src/lib/libopenal.js new file mode 100644 index 0000000000000..eeb28dc414c17 --- /dev/null +++ b/src/lib/libopenal.js @@ -0,0 +1,4781 @@ +/** + * @license + * Copyright 2013 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +//'use strict'; + +var LibraryOpenAL = { + // ************************************************************************ + // ** INTERNALS + // ************************************************************************ + + $AL__deps: ['$MainLoop'], + $AL: { + // ------------------------------------------------------ + // -- Constants + // ------------------------------------------------------ + + QUEUE_INTERVAL: 25, + QUEUE_LOOKAHEAD: 100.0 / 1000.0, + + DEVICE_NAME: 'Emscripten OpenAL', + CAPTURE_DEVICE_NAME: 'Emscripten OpenAL capture', + + ALC_EXTENSIONS: { + // TODO: 'ALC_EXT_EFX': true, + '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 + }, + + // ------------------------------------------------------ + // -- ALC Fields + // ------------------------------------------------------ + + _alcErr: 0, + get alcErr() { + return this._alcErr; + }, + set alcErr(val) { + // Errors should not be overwritten by later errors until they are cleared by a query. + if (this._alcErr === {{{ cDefs.ALC_NO_ERROR }}} || val === {{{ cDefs.ALC_NO_ERROR }}}) { + this._alcErr = val; + } + }, + + deviceRefCounts: {}, + alcStringCache: {}, + paused: false, + + // ------------------------------------------------------ + // -- AL Fields + // ------------------------------------------------------ + + stringCache: {}, + contexts: {}, + currentCtx: null, + buffers: { + // The zero buffer is legal to use, so create a placeholder for it + '0': { + id: 0, + refCount: 0, + audioBuf: null, + frequency: 0, + bytesPerSample: 2, + channels: 1, + length: 0 + } + }, + paramArray: [], // Used to prevent allocating a new array for each param call + + _nextId: 1, + newId: () => AL.freeIds.length > 0 ? AL.freeIds.pop() : AL._nextId++, + freeIds: [], + + // ------------------------------------------------------ + // -- Mixing Logic + // ------------------------------------------------------ + + 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 === {{{ cDefs.EM_TIMING_RAF }}} && document['visibilityState'] != 'visible') { + return; + } + + for (var i in ctx.sources) { + AL.scheduleSourceAudio(ctx.sources[i]); + } + }, + + // This function is the core scheduler that queues web-audio buffers for output. + // src.bufQueue represents the abstract OpenAL buffer queue, which is taversed to schedule + // corresponding web-audio buffers. These buffers are stored in src.audioQueue, which + // represents the queue of buffers scheduled for physical playback. These two queues are + // distinct because of the differing semantics of OpenAL and web audio. Some changes + // to OpenAL parameters, such as pitch, may require the web audio queue to be flushed and rescheduled. + scheduleSourceAudio: (src, lookahead) => { + // See comment on scheduleContextAudio above. + if (MainLoop.timingMode === {{{ cDefs.EM_TIMING_RAF }}} && document['visibilityState'] != 'visible') { + return; + } + if (src.state !== {{{ cDefs.AL_PLAYING }}}) { + 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 === {{{ cDefs.AL_STATIC }}} && 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); +#if OPENAL_DEBUG + if (offset > 0.0) { + warnOnce('The current browser does not support AudioBufferSourceNode.start(when, offset); method, so cannot play back audio with an offset '+startOffset+' secs! Audio glitches will occur!'); + } +#endif + } +#if OPENAL_DEBUG + else { + warnOnce('Unable to start AudioBufferSourceNode playback! Not supported by the browser?'); + } + + dbg(`scheduleSourceAudio() queuing buffer ${buf.id} for source ${src.id} at ${startTime} (offset by ${startOffset})`); +#endif + audioSrc._startTime = startTime; + src.audioQueue.push(audioSrc); + + startTime += duration; + } + + startOffset = 0.0; + bufCursor++; + } + }, + + // Advance the state of a source forward to the current time + updateSourceTime: (src) => { + var currentTime = src.context.audioCtx.currentTime; + if (src.state !== {{{ cDefs.AL_PLAYING }}}) { + 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, {{{ cDefs.AL_STOPPED }}}); + } else if (src.type === {{{ cDefs.AL_STATIC }}} && 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 !== {{{ cDefs.AL_STATIC }}} && 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, {{{ cDefs.AL_STOPPED }}}); + 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 === {{{ cDefs.AL_PLAYING }}}) { + if (src.state === {{{ cDefs.AL_PLAYING }}} || src.state == {{{ cDefs.AL_STOPPED }}}) { + src.bufsProcessed = 0; + src.bufOffset = 0.0; +#if OPENAL_DEBUG + dbg(`setSourceState() resetting and playing source ${src.id}`); +#endif + } else { +#if OPENAL_DEBUG + dbg(`setSourceState() playing source ${src.id} at ${src.bufOffset}`); +#endif + } + + AL.stopSourceAudio(src); + + src.state = {{{ cDefs.AL_PLAYING }}}; + src.bufStartTime = Number.NEGATIVE_INFINITY; + AL.scheduleSourceAudio(src); + } else if (state === {{{ cDefs.AL_PAUSED }}}) { + if (src.state === {{{ cDefs.AL_PLAYING }}}) { + // Store off the current offset to restore with on resume. + AL.updateSourceTime(src); + AL.stopSourceAudio(src); + + src.state = {{{ cDefs.AL_PAUSED }}}; +#if OPENAL_DEBUG + dbg(`setSourceState() pausing source ${src.id} at ${src.bufOffset}`); +#endif + } + } else if (state === {{{ cDefs.AL_STOPPED }}}) { + if (src.state !== {{{ cDefs.AL_INITIAL }}}) { + src.state = {{{ cDefs.AL_STOPPED }}}; + src.bufsProcessed = src.bufQueue.length; + src.bufStartTime = Number.NEGATIVE_INFINITY; + src.bufOffset = 0.0; + AL.stopSourceAudio(src); +#if OPENAL_DEBUG + dbg(`setSourceState() stopping source ${src.id}`); +#endif + } + } else if (state === {{{ cDefs.AL_INITIAL }}}) { + if (src.state !== {{{ cDefs.AL_INITIAL }}}) { + src.state = {{{ cDefs.AL_INITIAL }}}; + src.bufsProcessed = 0; + src.bufStartTime = Number.NEGATIVE_INFINITY; + src.bufOffset = 0.0; + AL.stopSourceAudio(src); +#if OPENAL_DEBUG + dbg(`setSourceState() initializing source ${src.id}`); +#endif + } + } + }, + + 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 === {{{ cDefs.AL_TRUE }}} || (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 {{{ cDefs.AL_NONE }}}: + 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 { +#if OPENAL_DEBUG + warnOnce('Listener position attributes are not present, falling back to setPosition()'); +#endif + 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 { +#if OPENAL_DEBUG + warnOnce('Listener orientation attributes are not present, falling back to setOrientation()'); +#endif + 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 { +#if OPENAL_DEBUG + warnOnce('Panner position attributes are not present, falling back to setPosition()'); +#endif + 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 { +#if OPENAL_DEBUG + warnOnce('Panner orientation attributes are not present, falling back to setOrientation()'); +#endif + 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 === {{{ cDefs.AL_PLAYING }}}) { + // 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 === {{{ cDefs.AL_STATIC }}} && 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 == {{{ cDefs.AL_PLAYING }}}; + if (playing) { + AL.setSourceState(src, {{{ cDefs.AL_INITIAL }}}); + } + + 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, {{{ cDefs.AL_PLAYING }}}); + } + }, + + // ------------------------------------------------------ + // -- Accessor Helpers + // ------------------------------------------------------ + + getGlobalParam: (funcname, param) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg(`${funcname}() called without a valid context`); +#endif + return null; + } + + switch (param) { + case {{{ cDefs.AL_DOPPLER_FACTOR }}}: + return AL.currentCtx.dopplerFactor; + case {{{ cDefs.AL_SPEED_OF_SOUND }}}: + return AL.currentCtx.speedOfSound; + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + return AL.currentCtx.distanceModel; + default: +#if OPENAL_DEBUG + dbg(`${funcname}() param ${ptrToString(param} is unknown or not implemented`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return null; + } + }, + + setGlobalParam: (funcname, param, value) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg(`${funcname}() called without a valid context`); +#endif + return; + } + + switch (param) { + case {{{ cDefs.AL_DOPPLER_FACTOR }}}: + if (!Number.isFinite(value) || value < 0.0) { // Strictly negative values are disallowed +#if OPENAL_DEBUG + dbg(`${funcname}() value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + AL.currentCtx.dopplerFactor = value; + AL.updateListenerSpace(AL.currentCtx); + break; + case {{{ cDefs.AL_SPEED_OF_SOUND }}}: + if (!Number.isFinite(value) || value <= 0.0) { // Negative or zero values are disallowed +#if OPENAL_DEBUG + dbg(`${funcname}() value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + AL.currentCtx.speedOfSound = value; + AL.updateListenerSpace(AL.currentCtx); + break; + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + switch (value) { + case {{{ cDefs.AL_NONE }}}: + 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: +#if OPENAL_DEBUG + dbg(`${funcname}() value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + break; + default: +#if OPENAL_DEBUG + dbg(`${funcname}() param ${ptrToString(param)} is unknown or not implemented`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + getListenerParam: (funcname, param) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg(`${funcname}() called without a valid context`); +#endif + return null; + } + + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + return AL.currentCtx.listener.position; + case {{{ cDefs.AL_VELOCITY }}}: + return AL.currentCtx.listener.velocity; + case {{{ cDefs.AL_ORIENTATION }}}: + return AL.currentCtx.listener.direction.concat(AL.currentCtx.listener.up); + case {{{ cDefs.AL_GAIN }}}: + return AL.currentCtx.gain.gain.value; + default: +#if OPENAL_DEBUG + dbg(`${funcname}() param ${ptrToString(param)} is unknown or not implemented`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return null; + } + }, + + setListenerParam: (funcname, param, value) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg(`${funcname}() called without a valid context`); +#endif + return; + } + if (value === null) { +#if OPENAL_DEBUG + dbg(`${funcname}(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + + var listener = AL.currentCtx.listener; + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_POSITION value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + listener.position[0] = value[0]; + listener.position[1] = value[1]; + listener.position[2] = value[2]; + AL.updateListenerSpace(AL.currentCtx); + break; + case {{{ cDefs.AL_VELOCITY }}}: + if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_VELOCITY value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + listener.velocity[0] = value[0]; + listener.velocity[1] = value[1]; + listener.velocity[2] = value[2]; + AL.updateListenerSpace(AL.currentCtx); + break; + case {{{ cDefs.AL_GAIN }}}: + if (!Number.isFinite(value) || value < 0.0) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_GAIN value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + AL.currentCtx.gain.gain.value = value; + break; + case {{{ cDefs.AL_ORIENTATION }}}: + 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]) + ) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_ORIENTATION value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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: +#if OPENAL_DEBUG + dbg(`${funcname}() param ${ptrToString(param)} is unknown or not implemented`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + getBufferParam: (funcname, bufferId, param) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg(`${funcname}() called without a valid context`); +#endif + return; + } + var buf = AL.buffers[bufferId]; + if (!buf || bufferId === 0) { +#if OPENAL_DEBUG + dbg(`${funcname}() called with an invalid buffer`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + 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: +#if OPENAL_DEBUG + dbg(`${funcname}() param ${ptrToString(param)} is unknown or not implemented`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return null; + } + }, + + setBufferParam: (funcname, bufferId, param, value) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg(`${funcname}() called without a valid context`); +#endif + return; + } + var buf = AL.buffers[bufferId]; + if (!buf || bufferId === 0) { +#if OPENAL_DEBUG + dbg(`${funcname}() called with an invalid buffer`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + if (value === null) { +#if OPENAL_DEBUG + dbg(`${funcname}(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + + switch (param) { + case 0x2004 /* AL_SIZE */: + if (value !== 0) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_SIZE value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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]) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_LOOP_POINTS_SOFT value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + if (buf.refCount > 0) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_LOOP_POINTS_SOFT set on bound buffer`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}}; + return; + } + + if (buf.audioBuf) { + buf.audioBuf._loopStart = value[0] / buf.frequency; + buf.audioBuf._loopEnd = value[1] / buf.frequency; + } + break; + default: +#if OPENAL_DEBUG + dbg(`${funcname}() param ${ptrToString(param)}' is unknown or not implemented`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + getSourceParam: (funcname, sourceId, param) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg(`${funcname}() called without a valid context`); +#endif + return null; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { +#if OPENAL_DEBUG + dbg(`${funcname}() called with an invalid source`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + 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 {{{ cDefs.AL_POSITION }}}: + return src.position; + case {{{ cDefs.AL_DIRECTION }}}: + return src.direction; + case {{{ cDefs.AL_VELOCITY }}}: + return src.velocity; + case 0x1007 /* AL_LOOPING */: + return src.looping; + case 0x1009 /* AL_BUFFER */: + if (src.type === {{{ cDefs.AL_STATIC }}}) { + return src.bufQueue[0].id; + } + return 0; + case {{{ cDefs.AL_GAIN }}}: + 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 {{{ cDefs.AL_DISTANCE_MODEL }}}: + return src.distanceModel; + default: +#if OPENAL_DEBUG + dbg(`${funcname}() param ${ptrToString(param)}' is unknown or not implemented`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return null; + } + }, + + setSourceParam: (funcname, sourceId, param, value) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg(`${funcname}() called without a valid context`); +#endif + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { +#if OPENAL_DEBUG + dbg('alSourcef() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + if (value === null) { +#if OPENAL_DEBUG + dbg(`${funcname}(): param ${ptrToString(param)}' has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + + switch (param) { + case 0x202 /* AL_SOURCE_RELATIVE */: + if (value === {{{ cDefs.AL_TRUE }}}) { + src.relative = true; + AL.updateSourceSpace(src); + } else if (value === {{{ cDefs.AL_FALSE }}}) { + src.relative = false; + AL.updateSourceSpace(src); + } else { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_SOURCE_RELATIVE value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + break; + case 0x1001 /* AL_CONE_INNER_ANGLE */: + if (!Number.isFinite(value)) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_CONE_INNER_ANGLE value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + src.coneInnerAngle = value; + if (src.panner) { + src.panner.coneInnerAngle = value % 360.0; + } + break; + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + if (!Number.isFinite(value)) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_CONE_OUTER_ANGLE value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_PITCH value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + if (src.pitch === value) { + break; + } + + src.pitch = value; + AL.updateSourceRate(src); + break; + case {{{ cDefs.AL_POSITION }}}: + if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_POSITION value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + src.position[0] = value[0]; + src.position[1] = value[1]; + src.position[2] = value[2]; + AL.updateSourceSpace(src); + break; + case {{{ cDefs.AL_DIRECTION }}}: + if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_DIRECTION value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + src.direction[0] = value[0]; + src.direction[1] = value[1]; + src.direction[2] = value[2]; + AL.updateSourceSpace(src); + break; + case {{{ cDefs.AL_VELOCITY }}}: + if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_VELOCITY value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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 === {{{ cDefs.AL_TRUE }}}) { + src.looping = true; + AL.updateSourceTime(src); + if (src.type === {{{ cDefs.AL_STATIC }}} && src.audioQueue.length > 0) { + var audioSrc = src.audioQueue[0]; + audioSrc.loop = true; + audioSrc._duration = Number.POSITIVE_INFINITY; + } + } else if (value === {{{ cDefs.AL_FALSE }}}) { + src.looping = false; + var currentTime = AL.updateSourceTime(src); + if (src.type === {{{ cDefs.AL_STATIC }}} && 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 { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_LOOPING value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + break; + case 0x1009 /* AL_BUFFER */: + if (src.state === {{{ cDefs.AL_PLAYING }}} || src.state === {{{ cDefs.AL_PAUSED }}}) { +#if OPENAL_DEBUG + dbg(`${funcname}(AL_BUFFER) called while source is playing or paused`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}}; + 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) { +#if OPENAL_DEBUG + dbg('alSourcei(AL_BUFFER) called with an invalid buffer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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 = {{{ cDefs.AL_STATIC }}}; + } + + AL.initSourcePanner(src); + AL.scheduleSourceAudio(src); + break; + case {{{ cDefs.AL_GAIN }}}: + if (!Number.isFinite(value) || value < 0.0) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_GAIN value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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)) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_MIN_GAIN value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } +#if OPENAL_DEBUG + warnOnce('AL_MIN_GAIN is not currently supported'); +#endif + src.minGain = value; + break; + case 0x100E /* AL_MAX_GAIN */: + if (!Number.isFinite(value) || value < Math.max(0.0, src.minGain) || value > 1.0) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_MAX_GAIN value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } +#if OPENAL_DEBUG + warnOnce('AL_MAX_GAIN is not currently supported'); +#endif + src.maxGain = value; + break; + case 0x1020 /* AL_REFERENCE_DISTANCE */: + if (!Number.isFinite(value) || value < 0.0) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_REFERENCE_DISTANCE value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + src.refDistance = value; + if (src.panner) { + src.panner.refDistance = value; + } + break; + case 0x1021 /* AL_ROLLOFF_FACTOR */: + if (!Number.isFinite(value) || value < 0.0) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_ROLLOFF_FACTOR value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_CORE_OUTER_GAIN value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + src.coneOuterGain = value; + if (src.panner) { + src.panner.coneOuterGain = value; + } + break; + case 0x1023 /* AL_MAX_DISTANCE */: + if (!Number.isFinite(value) || value < 0.0) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_MAX_DISTANCE value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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)) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_SEC_OFFSET value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_SAMPLE_OFFSET value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_BYTE_OFFSET value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + AL.sourceSeek(src, value); + break; + case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: + if (value !== {{{ cDefs.AL_FALSE }}} && value !== {{{ cDefs.AL_TRUE }}} && value !== 2 /* AL_AUTO_SOFT */) { +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_SOURCE_SPATIALIZE_SOFT value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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 */: +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_*_LENGTH_SOFT is read only`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}}; + break; + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + switch (value) { + case {{{ cDefs.AL_NONE }}}: + 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: +#if OPENAL_DEBUG + dbg(`${funcname}() param AL_DISTANCE_MODEL value ${value} is out of range`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + break; + default: +#if OPENAL_DEBUG + dbg(`${funcname}() param ${ptrToString(param)} is unknown or not implemented`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + // ------------------------------------------------------- + // -- Capture + // ------------------------------------------------------- + + // A map of 'capture device contexts'. + captures: {}, + + sharedCaptureAudioCtx: null, + + // Helper which: + // - Asserts that deviceId is both non-NULL AND a known device ID; + // - Returns a reference to it, or null if not found. + // - Sets alcErr accordingly. + // Treat NULL and separately because careless + // people might assume that most alcCapture functions + // accept NULL as a 'use the default' device. + requireValidCaptureDevice: (deviceId, funcname) => { + if (deviceId === 0) { +#if OPENAL_DEBUG + dbg(`${funcname}() on a NULL device is an error`); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return null; + } + var c = AL.captures[deviceId]; + if (!c) { +#if OPENAL_DEBUG + dbg(`${funcname}() on an invalid device`); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return null; + } + var err = c.mediaStreamError; + if (err) { +#if OPENAL_DEBUG + switch (err.name) { + case 'PermissionDeniedError': + dbg(`${funcname}() but the user denied access to the device`); + break; + case 'NotFoundError': + dbg(`${funcname}() but no capture device was found`); + break; + default: + dbg(`${funcname}() but a MediaStreamError was encountered: ${err}`); + break; + } +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return null; + } + return c; + } + + }, + + // *************************************************************************** + // ** ALC API + // *************************************************************************** + + // ------------------------------------------------------- + // -- ALC Capture + // ------------------------------------------------------- + + // bufferSize is actually 'number of sample frames', so was renamed + // bufferFrameCapacity here for clarity. + alcCaptureOpenDevice__deps: ['$autoResumeAudioContext'], + alcCaptureOpenDevice__proxy: 'sync', + 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) { +#if OPENAL_DEBUG + dbg(`alcCaptureOpenDevice() with invalid device name '${resolvedDeviceName}'`); +#endif + // 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 +#if OPENAL_DEBUG + dbg('alcCaptureOpenDevice() with negative bufferSize'); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}}; + 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) { +#if OPENAL_DEBUG + dbg('alcCaptureOpenDevice() cannot capture audio, because your browser lacks a `getUserMedia()` implementation'); +#endif + // 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) { +#if OPENAL_DEBUG + dbg(`alcCaptureOpenDevice() could not create the shared capture AudioContext: ${e}`); +#endif + // 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: +#if OPENAL_DEBUG + dbg(`alcCaptureOpenDevice() with unsupported format ${format}`); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}}; + 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) { +#if OPENAL_DEBUG + dbg(`alcCaptureOpenDevice() failed to allocate internal buffers (is bufferSize low enough?): ${e}`); +#endif + 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; +#if OPENAL_DEBUG + dbg(`navigator.getUserMedia() errored with: ${mediaStreamError}`); +#endif + }; + 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; + +#if OPENAL_DEBUG + if (inputChannelCount > 2 || outputChannelCount > 2) { + dbg('The number of input or output channels is too high, capture might not work as expected!'); + } +#endif + + // 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; + }, + + alcCaptureCloseDevice__proxy: 'sync', + 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; + }, + + alcCaptureStart__proxy: 'sync', + alcCaptureStart: (deviceId) => { + var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureStart'); + if (!c) return; + + if (c.isCapturing) { +#if OPENAL_DEBUG + dbg('Redundant call to alcCaptureStart()'); +#endif + // 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; + }, + + alcCaptureStop__proxy: 'sync', + alcCaptureStop: (deviceId) => { + var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureStop'); + if (!c) return; + +#if OPENAL_DEBUG + if (!c.isCapturing) { + dbg('Redundant call to alcCaptureStop()'); + } +#endif + c.isCapturing = false; + }, + + // The OpenAL spec hints that implementations are allowed to + // 'defer resampling and other conversions' up until this point. + // + // The last parameter is actually 'number of sample frames', so was + // renamed accordingly here + alcCaptureSamples__proxy: 'sync', + 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)) + { +#if OPENAL_DEBUG + dbg('alcCaptureSamples() with invalid bufferSize'); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}}; + return; + } + + function setF32Sample(i, sample) { + {{{ makeSetValue('pFrames', '4*i', 'sample', 'float') }}}; + } + function setI16Sample(i, sample) { + {{{ makeSetValue('pFrames', '2*i', 'sample', 'i16') }}}; + } + function setU8Sample(i, sample) { + {{{ makeSetValue('pFrames', 'i', 'sample', 'i8') }}}; + } + + var setSample; + + switch (c.requestedSampleType) { + case 'f32': setSample = setF32Sample; break; + case 'i16': setSample = setI16Sample; break; + case 'u8' : setSample = setU8Sample ; break; + default: +#if OPENAL_DEBUG + dbg(`Internal error: Unknown sample type '${c.requestedSampleType}'`); +#endif + 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; + }, + + + // ------------------------------------------------------- + // -- ALC Resources + // ------------------------------------------------------- + + alcOpenDevice__proxy: 'sync', + 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; + }, + + alcCloseDevice__proxy: 'sync', + alcCloseDevice: (deviceId) => { + if (!(deviceId in AL.deviceRefCounts) || AL.deviceRefCounts[deviceId] > 0) { + return {{{ cDefs.ALC_FALSE }}}; + } + + delete AL.deviceRefCounts[deviceId]; + AL.freeIds.push(deviceId); + return {{{ cDefs.ALC_TRUE }}}; + }, + + alcCreateContext__deps: ['$autoResumeAudioContext'], + alcCreateContext__proxy: 'sync', + alcCreateContext: (deviceId, pAttrList) => { + if (!(deviceId in AL.deviceRefCounts)) { +#if OPENAL_DEBUG + dbg('alcCreateContext() called with an invalid device'); +#endif + 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 {{{ cDefs.ALC_FALSE }}}: + hrtf = false; + break; + case {{{ cDefs.ALC_TRUE }}}: + hrtf = true; + break; + case 2 /* ALC_DONT_CARE_SOFT */: + break; + default: +#if OPENAL_DEBUG + dbg(`Unsupported ALC_HRTF_SOFT mode ${val}`); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}}; + return 0; + } + break; + case 0x1996 /* ALC_HRTF_ID_SOFT */: + if (val !== 0) { +#if OPENAL_DEBUG + dbg(`Invalid ALC_HRTF_ID_SOFT index ${val}`); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}}; + return 0; + } + break; + default: +#if OPENAL_DEBUG + dbg(`Unsupported context attribute ${ptrToString(attr)}`); +#endif + 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') { +#if OPENAL_DEBUG + dbg('Invalid or unsupported options'); +#endif + 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 === {{{ cDefs.AL_NO_ERROR }}} || val === {{{ cDefs.AL_NO_ERROR }}}) { + 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; + }, + + alcDestroyContext__proxy: 'sync', + alcDestroyContext: (contextId) => { + var ctx = AL.contexts[contextId]; + if (AL.currentCtx === ctx) { +#if OPENAL_DEBUG + dbg('alcDestroyContext() called with an invalid context'); +#endif + 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); + }, + + // ------------------------------------------------------- + // -- ALC State + // ------------------------------------------------------- + + alcGetError__proxy: 'sync', + alcGetError: (deviceId) => { + var err = AL.alcErr; + AL.alcErr = {{{ cDefs.ALC_NO_ERROR }}}; + return err; + }, + + alcGetCurrentContext__proxy: 'sync', + alcGetCurrentContext: () => { + if (AL.currentCtx !== null) { + return AL.currentCtx.id; + } + return 0; + }, + + alcMakeContextCurrent__proxy: 'sync', + alcMakeContextCurrent: (contextId) => { + if (contextId === 0) { + AL.currentCtx = null; + } else { + AL.currentCtx = AL.contexts[contextId]; + } + return {{{ cDefs.ALC_TRUE }}}; + }, + + alcGetContextsDevice__proxy: 'sync', + alcGetContextsDevice: (contextId) => { + if (contextId in AL.contexts) { + return AL.contexts[contextId].deviceId; + } + return 0; + }, + + // The spec is vague about what these are actually supposed to do, and NOP is a reasonable implementation + alcProcessContext: (contextId) => {}, + alcSuspendContext: (contextId) => {}, + + alcIsExtensionPresent__proxy: 'sync', + alcIsExtensionPresent: (deviceId, pExtName) => { + var name = UTF8ToString(pExtName); + + return AL.ALC_EXTENSIONS[name] ? 1 : 0; + }, + + alcGetEnumValue__proxy: 'sync', + 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)) { +#if OPENAL_DEBUG + dbg('alcGetEnumValue() called with an invalid device'); +#endif + // ALC_INVALID_DEVICE is not listed as a possible error state for + // this function, sadly. + return 0; + } else if (!pEnumName) { + AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}}; + 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: +#if OPENAL_DEBUG + dbg(`No value for `${pEnumName}` is known by alcGetEnumValue()`); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}}; + return {{{ cDefs.AL_NONE }}}; + } + }, + + alcGetString__proxy: 'sync', + alcGetString__deps: ['$stringToNewUTF8'], + alcGetString: (deviceId, param) => { + if (AL.alcStringCache[param]) { + return AL.alcStringCache[param]; + } + + var ret; + switch (param) { + case {{{ cDefs.ALC_NO_ERROR }}}: + ret = 'No Error'; + break; + case {{{ cDefs.ALC_INVALID_DEVICE }}}: + ret = 'Invalid Device'; + break; + case 0xA002 /* ALC_INVALID_CONTEXT */: + ret = 'Invalid Context'; + break; + case {{{ cDefs.ALC_INVALID_ENUM }}}: + ret = 'Invalid Enum'; + break; + case {{{ cDefs.ALC_INVALID_VALUE }}}: + 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 = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return 0; + } + + ret = Object.keys(AL.ALC_EXTENSIONS).join(' ') + break; + default: + AL.alcErr = {{{ cDefs.ALC_INVALID_ENUM }}}; + return 0; + } + + ret = stringToNewUTF8(ret); + AL.alcStringCache[param] = ret; + return ret; + }, + + alcGetIntegerv__proxy: 'sync', + alcGetIntegerv: (deviceId, param, size, pValues) => { + if (size === 0 || !pValues) { + // Ignore the query, per the spec + return; + } + + switch (param) { + case 0x1000 /* ALC_MAJOR_VERSION */: + {{{ makeSetValue('pValues', '0', '1', 'i32') }}}; + break; + case 0x1001 /* ALC_MINOR_VERSION */: + {{{ makeSetValue('pValues', '0', '1', 'i32') }}}; + break; + case 0x1002 /* ALC_ATTRIBUTES_SIZE */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return; + } + if (!AL.currentCtx) { + AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; + return; + } + + {{{ makeSetValue('pValues', '0', 'AL.currentCtx.attrs.length', 'i32') }}}; + break; + case 0x1003 /* ALC_ALL_ATTRIBUTES */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return; + } + if (!AL.currentCtx) { + AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; + return; + } + + for (var i = 0; i < AL.currentCtx.attrs.length; i++) { + {{{ makeSetValue('pValues', 'i*4', 'AL.currentCtx.attrs[i]', 'i32') }}}; + } + break; + case 0x1007 /* ALC_FREQUENCY */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return; + } + if (!AL.currentCtx) { + AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; + return; + } + + {{{ makeSetValue('pValues', '0', 'AL.currentCtx.audioCtx.sampleRate', 'i32') }}}; + break; + case 0x1010 /* ALC_MONO_SOURCES */: + case 0x1011 /* ALC_STEREO_SOURCES */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return; + } + if (!AL.currentCtx) { + AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; + return; + } + + {{{ makeSetValue('pValues', '0', '0x7FFFFFFF', 'i32') }}}; + break; + case 0x1992 /* ALC_HRTF_SOFT */: + case 0x1993 /* ALC_HRTF_STATUS_SOFT */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + 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 */; + } + } + {{{ makeSetValue('pValues', '0', 'hrtfStatus', 'i32') }}}; + break; + case 0x1994 /* ALC_NUM_HRTF_SPECIFIERS_SOFT */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return; + } + {{{ makeSetValue('pValues', '0', '1', 'i32') }}}; + break; + case 0x20003 /* ALC_MAX_AUXILIARY_SENDS */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return; + } + if (!AL.currentCtx) { + AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; + return; + } + + {{{ makeSetValue('pValues', '0', '1', 'i32') }}}; + 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)); + {{{ makeSetValue('pValues', '0', 'nsamples', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alcGetIntegerv() with param ${ptrToString(param)} not implemented yet`); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_ENUM }}}; + return; + } + }, + + emscripten_alcDevicePauseSOFT__proxy: 'sync', + emscripten_alcDevicePauseSOFT__sig: 'vi', + emscripten_alcDevicePauseSOFT: (deviceId) => { + if (!(deviceId in AL.deviceRefCounts)) { +#if OPENAL_DEBUG + dbg('alcDevicePauseSOFT() called with an invalid device'); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + 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_alcDeviceResumeSOFT__proxy: 'sync', + emscripten_alcDeviceResumeSOFT__sig: 'vi', + emscripten_alcDeviceResumeSOFT: (deviceId) => { + if (!(deviceId in AL.deviceRefCounts)) { +#if OPENAL_DEBUG + dbg('alcDeviceResumeSOFT() called with an invalid device'); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + 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_alcGetStringiSOFT__proxy: 'sync', + emscripten_alcGetStringiSOFT__sig: 'iiii', + emscripten_alcGetStringiSOFT__deps: ['alcGetString', '$stringToNewUTF8'], + emscripten_alcGetStringiSOFT: (deviceId, param, index) => { + if (!(deviceId in AL.deviceRefCounts)) { +#if OPENAL_DEBUG + dbg('alcGetStringiSOFT() called with an invalid device'); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + 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 { +#if OPENAL_DEBUG + dbg(`alcGetStringiSOFT() with param ALC_HRTF_SPECIFIER_SOFT index ${index} is out of range`); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}}; + return 0; + } + break; + default: + if (index !== 0) { +#if OPENAL_DEBUG + dbg(`alcGetStringiSOFT() with param ${ptrToString(param)} not implemented yet`); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_ENUM }}}; + return 0; + } + return _alcGetString(deviceId, param); + } + + ret = stringToNewUTF8(ret); + AL.alcStringCache[param] = ret; + return ret; + }, + + emscripten_alcResetDeviceSOFT__proxy: 'sync', + emscripten_alcResetDeviceSOFT__sig: 'iii', + emscripten_alcResetDeviceSOFT: (deviceId, pAttrList) => { + if (!(deviceId in AL.deviceRefCounts)) { +#if OPENAL_DEBUG + dbg('alcResetDeviceSOFT() called with an invalid device'); +#endif + AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}}; + return {{{ cDefs.ALC_FALSE }}}; + } + + 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 === {{{ cDefs.ALC_TRUE }}}) { + hrtf = true; + } else if (val === {{{ cDefs.ALC_FALSE }}}) { + 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 {{{ cDefs.ALC_TRUE }}}; + }, + + // *************************************************************************** + // ** AL API + // *************************************************************************** + + // ------------------------------------------------------- + // -- AL Resources + // ------------------------------------------------------- + + alGenBuffers__proxy: 'sync', + alGenBuffers: (count, pBufferIds) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alGenBuffers() called without a valid context'); +#endif + 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; + {{{ makeSetValue('pBufferIds', 'i*4', 'buf.id', 'i32') }}}; + } + }, + + alDeleteBuffers__proxy: 'sync', + alDeleteBuffers: (count, pBufferIds) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alDeleteBuffers() called without a valid context'); +#endif + return; + } + + for (var i = 0; i < count; ++i) { + var bufId = {{{ makeGetValue('pBufferIds', 'i*4', 'i32') }}}; + /// 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]) { +#if OPENAL_DEBUG + dbg('alDeleteBuffers() called with an invalid buffer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + + // Make sure the buffer is no longer in use. + if (AL.buffers[bufId].refCount) { +#if OPENAL_DEBUG + dbg('alDeleteBuffers() called with a used buffer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}}; + return; + } + } + + for (var i = 0; i < count; ++i) { + var bufId = {{{ makeGetValue('pBufferIds', 'i*4', 'i32') }}}; + if (bufId === 0) { + continue; + } + + AL.deviceRefCounts[AL.buffers[bufId].deviceId]--; + delete AL.buffers[bufId]; + AL.freeIds.push(bufId); + } + }, + + alGenSources__proxy: 'sync', + alGenSources: (count, pSourceIds) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alGenSources() called without a valid context'); +#endif + 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: {{{ cDefs.AL_INITIAL }}}, + 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; + {{{ makeSetValue('pSourceIds', 'i*4', 'src.id', 'i32') }}}; + } + }, + + alDeleteSources__deps: ['alSourcei'], + alDeleteSources__proxy: 'sync', + alDeleteSources: (count, pSourceIds) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alDeleteSources() called without a valid context'); +#endif + return; + } + + for (var i = 0; i < count; ++i) { + var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}; + if (!AL.currentCtx.sources[srcId]) { +#if OPENAL_DEBUG + dbg('alDeleteSources() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + } + + for (var i = 0; i < count; ++i) { + var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}; + AL.setSourceState(AL.currentCtx.sources[srcId], {{{ cDefs.AL_STOPPED }}}); + _alSourcei(srcId, 0x1009 /* AL_BUFFER */, 0); + delete AL.currentCtx.sources[srcId]; + AL.freeIds.push(srcId); + } + }, + + // ------------------------------------------------------- + // --- AL Context State + // ------------------------------------------------------- + + alGetError__proxy: 'sync', + alGetError: () => { + if (!AL.currentCtx) { + return {{{ cDefs.AL_INVALID_OPERATION }}}; + } + // Reset error on get. + var err = AL.currentCtx.err; + AL.currentCtx.err = {{{ cDefs.AL_NO_ERROR }}}; + return err; + }, + + alIsExtensionPresent__proxy: 'sync', + alIsExtensionPresent: (pExtName) => { + var name = UTF8ToString(pExtName); + + return AL.AL_EXTENSIONS[name] ? 1 : 0; + }, + + alGetEnumValue__proxy: 'sync', + alGetEnumValue: (pEnumName) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alGetEnumValue() called without a valid context'); +#endif + return 0; + } + + if (!pEnumName) { +#if OPENAL_DEBUG + dbg('alGetEnumValue() called with null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return {{{ cDefs.AL_NONE }}}; + } + 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: +#if OPENAL_DEBUG + dbg(`No value for `${name}` is known by alGetEnumValue()`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return 0; + } + }, + + alGetString__proxy: 'sync', + alGetString__deps: ['$stringToNewUTF8'], + alGetString: (param) => { + if (AL.stringCache[param]) { + return AL.stringCache[param]; + } + + var ret; + switch (param) { + case {{{ cDefs.AL_NO_ERROR }}}: + ret = 'No Error'; + break; + case {{{ cDefs.AL_INVALID_NAME }}}: + ret = 'Invalid Name'; + break; + case {{{ cDefs.AL_INVALID_ENUM }}}: + ret = 'Invalid Enum'; + break; + case {{{ cDefs.AL_INVALID_VALUE }}}: + ret = 'Invalid Value'; + break; + case {{{ cDefs.AL_INVALID_OPERATION }}}: + 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 = {{{ cDefs.AL_INVALID_ENUM }}}; + } else { + #if OPENAL_DEBUG + dbg('alGetString() called without a valid context'); + #endif + } + return 0; + } + + ret = stringToNewUTF8(ret); + AL.stringCache[param] = ret; + return ret; + }, + + alEnable__proxy: 'sync', + alEnable: (param) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alEnable() called without a valid context'); +#endif + return; + } + switch (param) { + case 0x200 /* AL_SOURCE_DISTANCE_MODEL */: + AL.currentCtx.sourceDistanceModel = true; + AL.updateContextGlobal(AL.currentCtx); + break; + default: +#if OPENAL_DEBUG + dbg(`alEnable() with param ${ptrToString(param)} not implemented yet`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alDisable__proxy: 'sync', + alDisable: (param) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alDisable() called without a valid context'); +#endif + return; + } + switch (param) { + case 0x200 /* AL_SOURCE_DISTANCE_MODEL */: + AL.currentCtx.sourceDistanceModel = false; + AL.updateContextGlobal(AL.currentCtx); + break; + default: +#if OPENAL_DEBUG + dbg(`alDisable() with param ${ptrToString(param)} not implemented yet`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alIsEnabled__proxy: 'sync', + alIsEnabled: (param) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alIsEnabled() called without a valid context'); +#endif + return 0; + } + switch (param) { + case 0x200 /* AL_SOURCE_DISTANCE_MODEL */: + return AL.currentCtx.sourceDistanceModel ? {{{ cDefs.AL_FALSE }}} : {{{ cDefs.AL_TRUE }}}; + default: +#if OPENAL_DEBUG + dbg(`alIsEnabled() with param ${ptrToString(param)} not implemented yet`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return 0; + } + }, + + alGetDouble__proxy: 'sync', + alGetDouble: (param) => { + var val = AL.getGlobalParam('alGetDouble', param); + if (val === null) { + return 0.0; + } + + switch (param) { + case {{{ cDefs.AL_DOPPLER_FACTOR }}}: + case {{{ cDefs.AL_SPEED_OF_SOUND }}}: + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + return val; + default: +#if OPENAL_DEBUG + dbg(`alGetDouble(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return 0.0; + } + }, + + alGetDoublev__proxy: 'sync', + 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 {{{ cDefs.AL_DOPPLER_FACTOR }}}: + case {{{ cDefs.AL_SPEED_OF_SOUND }}}: + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + {{{ makeSetValue('pValues', '0', 'val', 'double') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetDoublev(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetFloat__proxy: 'sync', + alGetFloat: (param) => { + var val = AL.getGlobalParam('alGetFloat', param); + if (val === null) { + return 0.0; + } + + switch (param) { + case {{{ cDefs.AL_DOPPLER_FACTOR }}}: + case {{{ cDefs.AL_SPEED_OF_SOUND }}}: + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + return val; + default: +#if OPENAL_DEBUG + dbg(`alGetFloat(): param ${ptrToString(param)} has wrong signature`); +#endif + return 0.0; + } + }, + + alGetFloatv__proxy: 'sync', + 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 {{{ cDefs.AL_DOPPLER_FACTOR }}}: + case {{{ cDefs.AL_SPEED_OF_SOUND }}}: + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + {{{ makeSetValue('pValues', '0', 'val', 'float') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetFloatv(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetInteger__proxy: 'sync', + alGetInteger: (param) => { + var val = AL.getGlobalParam('alGetInteger', param); + if (val === null) { + return 0; + } + + switch (param) { + case {{{ cDefs.AL_DOPPLER_FACTOR }}}: + case {{{ cDefs.AL_SPEED_OF_SOUND }}}: + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + return val; + default: +#if OPENAL_DEBUG + dbg(`alGetInteger(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return 0; + } + }, + + alGetIntegerv__proxy: 'sync', + 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 {{{ cDefs.AL_DOPPLER_FACTOR }}}: + case {{{ cDefs.AL_SPEED_OF_SOUND }}}: + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + {{{ makeSetValue('pValues', '0', 'val', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetIntegerv(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetBoolean__proxy: 'sync', + alGetBoolean: (param) => { + var val = AL.getGlobalParam('alGetBoolean', param); + if (val === null) { + return {{{ cDefs.AL_FALSE }}}; + } + + switch (param) { + case {{{ cDefs.AL_DOPPLER_FACTOR }}}: + case {{{ cDefs.AL_SPEED_OF_SOUND }}}: + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + return val !== 0 ? {{{ cDefs.AL_TRUE }}} : {{{ cDefs.AL_FALSE }}}; + default: +#if OPENAL_DEBUG + dbg(`alGetBoolean(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return {{{ cDefs.AL_FALSE }}}; + } + }, + + alGetBooleanv__proxy: 'sync', + 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 {{{ cDefs.AL_DOPPLER_FACTOR }}}: + case {{{ cDefs.AL_SPEED_OF_SOUND }}}: + case {{{ cDefs.AL_DISTANCE_MODEL }}}: + {{{ makeSetValue('pValues', '0', 'val', 'i8') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetBooleanv(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alDistanceModel__proxy: 'sync', + alDistanceModel: (model) => { + AL.setGlobalParam('alDistanceModel', {{{ cDefs.AL_DISTANCE_MODEL }}}, model); + }, + + alSpeedOfSound__proxy: 'sync', + alSpeedOfSound: (value) => { + AL.setGlobalParam('alSpeedOfSound', {{{ cDefs.AL_SPEED_OF_SOUND }}}, value); + }, + + alDopplerFactor__proxy: 'sync', + alDopplerFactor: (value) => { + AL.setGlobalParam('alDopplerFactor', {{{ cDefs.AL_DOPPLER_FACTOR }}}, value); + }, + + // http://openal.996291.n3.nabble.com/alSpeedOfSound-or-alDopperVelocity-tp1960.html + // alDopplerVelocity() sets a multiplier for the speed of sound. + // It's deprecated since it's equivalent to directly calling + // alSpeedOfSound() with an appropriately premultiplied value. + alDopplerVelocity__proxy: 'sync', + alDopplerVelocity: (value) => { + warnOnce('alDopplerVelocity() is deprecated, and only kept for compatibility with OpenAL 1.0. Use alSpeedOfSound() instead.'); + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alDopplerVelocity() called without a valid context'); +#endif + return; + } + if (value <= 0) { // Negative or zero values are disallowed + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + }, + + // ------------------------------------------------------- + // -- AL Listener State + // ------------------------------------------------------- + + alGetListenerf__proxy: 'sync', + alGetListenerf: (param, pValue) => { + var val = AL.getListenerParam('alGetListenerf', param); + if (val === null) { + return; + } + if (!pValue) { +#if OPENAL_DEBUG + dbg('alGetListenerf() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case {{{ cDefs.AL_GAIN }}}: + {{{ makeSetValue('pValue', '0', 'val', 'float') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetListenerf(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetListener3f__proxy: 'sync', + alGetListener3f: (param, pValue0, pValue1, pValue2) => { + var val = AL.getListenerParam('alGetListener3f', param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { +#if OPENAL_DEBUG + dbg('alGetListener3f() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + {{{ makeSetValue('pValue0', '0', 'val[0]', 'float') }}}; + {{{ makeSetValue('pValue1', '0', 'val[1]', 'float') }}}; + {{{ makeSetValue('pValue2', '0', 'val[2]', 'float') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetListener3f(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetListenerfv__proxy: 'sync', + alGetListenerfv: (param, pValues) => { + var val = AL.getListenerParam('alGetListenerfv', param); + if (val === null) { + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alGetListenerfv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + {{{ makeSetValue('pValues', '0', 'val[0]', 'float') }}}; + {{{ makeSetValue('pValues', '4', 'val[1]', 'float') }}}; + {{{ makeSetValue('pValues', '8', 'val[2]', 'float') }}}; + break; + case {{{ cDefs.AL_ORIENTATION }}}: + {{{ makeSetValue('pValues', '0', 'val[0]', 'float') }}}; + {{{ makeSetValue('pValues', '4', 'val[1]', 'float') }}}; + {{{ makeSetValue('pValues', '8', 'val[2]', 'float') }}}; + {{{ makeSetValue('pValues', '12', 'val[3]', 'float') }}}; + {{{ makeSetValue('pValues', '16', 'val[4]', 'float') }}}; + {{{ makeSetValue('pValues', '20', 'val[5]', 'float') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetListenerfv(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetListeneri__proxy: 'sync', + alGetListeneri: (param, pValue) => { + var val = AL.getListenerParam('alGetListeneri', param); + if (val === null) { + return; + } + if (!pValue) { +#if OPENAL_DEBUG + dbg('alGetListeneri() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + +#if OPENAL_DEBUG + dbg(`alGetListeneri(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + }, + + alGetListener3i__proxy: 'sync', + alGetListener3i: (param, pValue0, pValue1, pValue2) => { + var val = AL.getListenerParam('alGetListener3i', param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { +#if OPENAL_DEBUG + dbg('alGetListener3i() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + {{{ makeSetValue('pValue0', '0', 'val[0]', 'i32') }}}; + {{{ makeSetValue('pValue1', '0', 'val[1]', 'i32') }}}; + {{{ makeSetValue('pValue2', '0', 'val[2]', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetListener3i(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetListeneriv__proxy: 'sync', + alGetListeneriv: (param, pValues) => { + var val = AL.getListenerParam('alGetListeneriv', param); + if (val === null) { + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alGetListeneriv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + {{{ makeSetValue('pValues', '0', 'val[0]', 'i32') }}}; + {{{ makeSetValue('pValues', '4', 'val[1]', 'i32') }}}; + {{{ makeSetValue('pValues', '8', 'val[2]', 'i32') }}}; + break; + case {{{ cDefs.AL_ORIENTATION }}}: + {{{ makeSetValue('pValues', '0', 'val[0]', 'i32') }}}; + {{{ makeSetValue('pValues', '4', 'val[1]', 'i32') }}}; + {{{ makeSetValue('pValues', '8', 'val[2]', 'i32') }}}; + {{{ makeSetValue('pValues', '12', 'val[3]', 'i32') }}}; + {{{ makeSetValue('pValues', '16', 'val[4]', 'i32') }}}; + {{{ makeSetValue('pValues', '20', 'val[5]', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetListeneriv(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alListenerf__proxy: 'sync', + alListenerf: (param, value) => { + switch (param) { + case {{{ cDefs.AL_GAIN }}}: + AL.setListenerParam('alListenerf', param, value); + break; + default: + AL.setListenerParam('alListenerf', param, null); + break; + } + }, + + alListener3f__proxy: 'sync', + alListener3f: (param, value0, value1, value2) => { + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + 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; + } + }, + + alListenerfv__proxy: 'sync', + alListenerfv: (param, pValues) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alListenerfv() called without a valid context'); +#endif + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alListenerfv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'float') }}}; + AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'float') }}}; + AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'float') }}}; + AL.setListenerParam('alListenerfv', param, AL.paramArray); + break; + case {{{ cDefs.AL_ORIENTATION }}}: + AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'float') }}}; + AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'float') }}}; + AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'float') }}}; + AL.paramArray[3] = {{{ makeGetValue('pValues', '12', 'float') }}}; + AL.paramArray[4] = {{{ makeGetValue('pValues', '16', 'float') }}}; + AL.paramArray[5] = {{{ makeGetValue('pValues', '20', 'float') }}}; + AL.setListenerParam('alListenerfv', param, AL.paramArray); + break; + default: + AL.setListenerParam('alListenerfv', param, null); + break; + } + }, + + alListeneri__proxy: 'sync', + alListeneri: (param, value) => { + AL.setListenerParam('alListeneri', param, null); + }, + + alListener3i__proxy: 'sync', + alListener3i: (param, value0, value1, value2) => { + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + 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; + } + }, + + alListeneriv__proxy: 'sync', + alListeneriv: (param, pValues) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alListeneriv() called without a valid context'); +#endif + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alListeneriv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'i32') }}}; + AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'i32') }}}; + AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'i32') }}}; + AL.setListenerParam('alListeneriv', param, AL.paramArray); + break; + case {{{ cDefs.AL_ORIENTATION }}}: + AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'i32') }}}; + AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'i32') }}}; + AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'i32') }}}; + AL.paramArray[3] = {{{ makeGetValue('pValues', '12', 'i32') }}}; + AL.paramArray[4] = {{{ makeGetValue('pValues', '16', 'i32') }}}; + AL.paramArray[5] = {{{ makeGetValue('pValues', '20', 'i32') }}}; + AL.setListenerParam('alListeneriv', param, AL.paramArray); + break; + default: + AL.setListenerParam('alListeneriv', param, null); + break; + } + }, + + // ------------------------------------------------------- + // -- AL Buffer State + // ------------------------------------------------------- + + alIsBuffer__proxy: 'sync', + alIsBuffer: (bufferId) => { + if (!AL.currentCtx) { + return false; + } + if (bufferId > AL.buffers.length) { + return false; + } + + if (!AL.buffers[bufferId]) { + return false; + } + return true; + }, + + alBufferData__proxy: 'sync', + alBufferData: (bufferId, format, pData, size, freq) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alBufferData() called without a valid context'); +#endif + return; + } + var buf = AL.buffers[bufferId]; + if (!buf) { +#if OPENAL_DEBUG + dbg('alBufferData() called with an invalid buffer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + if (freq <= 0) { +#if OPENAL_DEBUG + dbg('alBufferData() called with an invalid frequency'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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: +#if OPENAL_DEBUG + dbg(`alBufferData() called with invalid format ${format}`; +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + buf.frequency = freq; + buf.audioBuf = audioBuf; + } catch (e) { +#if OPENAL_DEBUG + dbg(`alBufferData() upload failed with an exception ${e}`; +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + }, + + alGetBufferf__proxy: 'sync', + alGetBufferf: (bufferId, param, pValue) => { + var val = AL.getBufferParam('alGetBufferf', bufferId, param); + if (val === null) { + return; + } + if (!pValue) { +#if OPENAL_DEBUG + dbg('alGetBufferf() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + +#if OPENAL_DEBUG + dbg(`alGetBufferf(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + }, + + alGetBuffer3f__proxy: 'sync', + alGetBuffer3f: (bufferId, param, pValue0, pValue1, pValue2) => { + var val = AL.getBufferParam('alGetBuffer3f', bufferId, param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { +#if OPENAL_DEBUG + dbg('alGetBuffer3f() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + +#if OPENAL_DEBUG + dbg(`alGetBuffer3f(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + }, + + alGetBufferfv__proxy: 'sync', + alGetBufferfv: (bufferId, param, pValues) => { + var val = AL.getBufferParam('alGetBufferfv', bufferId, param); + if (val === null) { + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alGetBufferfv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + +#if OPENAL_DEBUG + dbg(`alGetBufferfv(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + }, + + alGetBufferi__proxy: 'sync', + alGetBufferi: (bufferId, param, pValue) => { + var val = AL.getBufferParam('alGetBufferi', bufferId, param); + if (val === null) { + return; + } + if (!pValue) { +#if OPENAL_DEBUG + dbg('alGetBufferi() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case 0x2001 /* AL_FREQUENCY */: + case 0x2002 /* AL_BITS */: + case 0x2003 /* AL_CHANNELS */: + case 0x2004 /* AL_SIZE */: + {{{ makeSetValue('pValue', '0', 'val', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetBufferi(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetBuffer3i__proxy: 'sync', + alGetBuffer3i: (bufferId, param, pValue0, pValue1, pValue2) => { + var val = AL.getBufferParam('alGetBuffer3i', bufferId, param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { +#if OPENAL_DEBUG + dbg('alGetBuffer3i() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + +#if OPENAL_DEBUG + dbg(`alGetBuffer3i(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + }, + + alGetBufferiv__proxy: 'sync', + alGetBufferiv: (bufferId, param, pValues) => { + var val = AL.getBufferParam('alGetBufferiv', bufferId, param); + if (val === null) { + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alGetBufferiv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case 0x2001 /* AL_FREQUENCY */: + case 0x2002 /* AL_BITS */: + case 0x2003 /* AL_CHANNELS */: + case 0x2004 /* AL_SIZE */: + {{{ makeSetValue('pValues', '0', 'val', 'i32') }}}; + break; + case 0x2015 /* AL_LOOP_POINTS_SOFT */: + {{{ makeSetValue('pValues', '0', 'val[0]', 'i32') }}}; + {{{ makeSetValue('pValues', '4', 'val[1]', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetBufferiv(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + // All of the remaining alBuffer* setters and getters are only of interest + // to extensions which need them. Core OpenAL alone defines no valid + // property for these. + + alBufferf__proxy: 'sync', + alBufferf: (bufferId, param, value) => { + AL.setBufferParam('alBufferf', bufferId, param, null); + }, + + alBuffer3f__proxy: 'sync', + alBuffer3f: (bufferId, param, value0, value1, value2) => { + AL.setBufferParam('alBuffer3f', bufferId, param, null); + }, + + alBufferfv__proxy: 'sync', + alBufferfv: (bufferId, param, pValues) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alBufferfv() called without a valid context'); +#endif + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alBufferfv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + AL.setBufferParam('alBufferfv', bufferId, param, null); + }, + + alBufferi__proxy: 'sync', + alBufferi: (bufferId, param, value) => { + AL.setBufferParam('alBufferi', bufferId, param, null); + }, + + alBuffer3i__proxy: 'sync', + alBuffer3i: (bufferId, param, value0, value1, value2) => { + AL.setBufferParam('alBuffer3i', bufferId, param, null); + }, + + alBufferiv__proxy: 'sync', + alBufferiv: (bufferId, param, pValues) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alBufferiv() called without a valid context'); +#endif + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alBufferiv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case 0x2015 /* AL_LOOP_POINTS_SOFT */: + AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'i32') }}}; + AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'i32') }}}; + AL.setBufferParam('alBufferiv', bufferId, param, AL.paramArray); + break; + default: + AL.setBufferParam('alBufferiv', bufferId, param, null); + break; + } + }, + + // ------------------------------------------------------- + // -- AL Source State + // ------------------------------------------------------- + + alIsSource__proxy: 'sync', + alIsSource: (sourceId) => { + if (!AL.currentCtx) { + return false; + } + + if (!AL.currentCtx.sources[sourceId]) { + return false; + } + return true; + }, + + alSourceQueueBuffers__proxy: 'sync', + alSourceQueueBuffers: (sourceId, count, pBufferIds) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourceQueueBuffers() called without a valid context'); +#endif + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { +#if OPENAL_DEBUG + dbg('alSourceQueueBuffers() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + if (src.type === {{{ cDefs.AL_STATIC }}}) { +#if OPENAL_DEBUG + dbg('alSourceQueueBuffers() called while a static buffer is bound'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}}; + 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 = {{{ makeGetValue('pBufferIds', 'i*4', 'i32') }}}; + var buf = AL.buffers[bufId]; + if (!buf) { +#if OPENAL_DEBUG + dbg('alSourceQueueBuffers() called with an invalid buffer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + 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) + ) { +#if OPENAL_DEBUG + dbg('alSourceQueueBuffers() called with a buffer of different format'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}}; + } + } + + // 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 = {{{ makeGetValue('pBufferIds', 'i*4', 'i32') }}}; + 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); + }, + + alSourceUnqueueBuffers__proxy: 'sync', + alSourceUnqueueBuffers: (sourceId, count, pBufferIds) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourceUnqueueBuffers() called without a valid context'); +#endif + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { +#if OPENAL_DEBUG + dbg('alSourceUnqueueBuffers() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + if (count > (src.bufQueue.length === 1 && src.bufQueue[0].id === 0 ? 0 : src.bufsProcessed)) { + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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. + {{{ makeSetValue('pBufferIds', 'i*4', 'buf.id', 'i32') }}}; + 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); + }, + + alSourcePlay__proxy: 'sync', + alSourcePlay: (sourceId) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourcePlay() called without a valid context'); +#endif + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { +#if OPENAL_DEBUG + dbg('alSourcePlay() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + AL.setSourceState(src, {{{ cDefs.AL_PLAYING }}}); + }, + + alSourcePlayv__proxy: 'sync', + alSourcePlayv: (count, pSourceIds) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourcePlayv() called without a valid context'); +#endif + return; + } + if (!pSourceIds) { +#if OPENAL_DEBUG + dbg('alSourcePlayv() called with null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + } + for (var i = 0; i < count; ++i) { + if (!AL.currentCtx.sources[{{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}]) { +#if OPENAL_DEBUG + dbg('alSourcePlayv() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + } + + for (var i = 0; i < count; ++i) { + var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}; + AL.setSourceState(AL.currentCtx.sources[srcId], {{{ cDefs.AL_PLAYING }}}); + } + }, + + alSourceStop__proxy: 'sync', + alSourceStop: (sourceId) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourceStop() called without a valid context'); +#endif + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { +#if OPENAL_DEBUG + dbg('alSourceStop() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + AL.setSourceState(src, {{{ cDefs.AL_STOPPED }}}); + }, + + alSourceStopv__proxy: 'sync', + alSourceStopv: (count, pSourceIds) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourceStopv() called without a valid context'); +#endif + return; + } + if (!pSourceIds) { +#if OPENAL_DEBUG + dbg('alSourceStopv() called with null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + } + for (var i = 0; i < count; ++i) { + if (!AL.currentCtx.sources[{{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}]) { +#if OPENAL_DEBUG + dbg('alSourceStopv() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + } + + for (var i = 0; i < count; ++i) { + var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}; + AL.setSourceState(AL.currentCtx.sources[srcId], {{{ cDefs.AL_STOPPED }}}); + } + }, + + alSourceRewind__proxy: 'sync', + alSourceRewind: (sourceId) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourceRewind() called without a valid context'); +#endif + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { +#if OPENAL_DEBUG + dbg('alSourceRewind() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + // Stop the source first to clear the source queue + AL.setSourceState(src, {{{ cDefs.AL_STOPPED }}}); + // Now set the state of AL_INITIAL according to the specification + AL.setSourceState(src, {{{ cDefs.AL_INITIAL }}}); + }, + + alSourceRewindv__proxy: 'sync', + alSourceRewindv: (count, pSourceIds) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourceRewindv() called without a valid context'); +#endif + return; + } + if (!pSourceIds) { +#if OPENAL_DEBUG + dbg('alSourceRewindv() called with null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + } + for (var i = 0; i < count; ++i) { + if (!AL.currentCtx.sources[{{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}]) { +#if OPENAL_DEBUG + dbg('alSourceRewindv() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + } + + for (var i = 0; i < count; ++i) { + var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}; + AL.setSourceState(AL.currentCtx.sources[srcId], {{{ cDefs.AL_INITIAL }}}); + } + }, + + alSourcePause__proxy: 'sync', + alSourcePause: (sourceId) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourcePause() called without a valid context'); +#endif + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { +#if OPENAL_DEBUG + dbg('alSourcePause() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + AL.setSourceState(src, {{{ cDefs.AL_PAUSED }}}); + }, + + alSourcePausev__proxy: 'sync', + alSourcePausev: (count, pSourceIds) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourcePausev() called without a valid context'); +#endif + return; + } + if (!pSourceIds) { +#if OPENAL_DEBUG + dbg('alSourcePausev() called with null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + } + for (var i = 0; i < count; ++i) { + if (!AL.currentCtx.sources[{{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}]) { +#if OPENAL_DEBUG + dbg('alSourcePausev() called with an invalid source'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}}; + return; + } + } + + for (var i = 0; i < count; ++i) { + var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}; + AL.setSourceState(AL.currentCtx.sources[srcId], {{{ cDefs.AL_PAUSED }}}); + } + }, + + alGetSourcef__proxy: 'sync', + alGetSourcef: (sourceId, param, pValue) => { + var val = AL.getSourceParam('alGetSourcef', sourceId, param); + if (val === null) { + return; + } + if (!pValue) { +#if OPENAL_DEBUG + dbg('alGetSourcef() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1003 /* AL_PITCH */: + case {{{ cDefs.AL_GAIN }}}: + 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 */: + {{{ makeSetValue('pValue', '0', 'val', 'float') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetSourcef(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetSource3f__proxy: 'sync', + alGetSource3f: (sourceId, param, pValue0, pValue1, pValue2) => { + var val = AL.getSourceParam('alGetSource3f', sourceId, param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { +#if OPENAL_DEBUG + dbg('alGetSource3f() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_DIRECTION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + {{{ makeSetValue('pValue0', '0', 'val[0]', 'float') }}}; + {{{ makeSetValue('pValue1', '0', 'val[1]', 'float') }}}; + {{{ makeSetValue('pValue2', '0', 'val[2]', 'float') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetSource3f(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetSourcefv__proxy: 'sync', + alGetSourcefv: (sourceId, param, pValues) => { + var val = AL.getSourceParam('alGetSourcefv', sourceId, param); + if (val === null) { + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alGetSourcefv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1003 /* AL_PITCH */: + case {{{ cDefs.AL_GAIN }}}: + 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 */: + {{{ makeSetValue('pValues', '0', 'val[0]', 'float') }}}; + break; + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_DIRECTION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + {{{ makeSetValue('pValues', '0', 'val[0]', 'float') }}}; + {{{ makeSetValue('pValues', '4', 'val[1]', 'float') }}}; + {{{ makeSetValue('pValues', '8', 'val[2]', 'float') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetSourcefv(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetSourcei__proxy: 'sync', + alGetSourcei: (sourceId, param, pValue) => { + var val = AL.getSourceParam('alGetSourcei', sourceId, param); + if (val === null) { + return; + } + if (!pValue) { +#if OPENAL_DEBUG + dbg('alGetSourcei() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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 {{{ cDefs.AL_DISTANCE_MODEL }}}: + {{{ makeSetValue('pValue', '0', 'val', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetSourcei(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetSource3i__proxy: 'sync', + alGetSource3i: (sourceId, param, pValue0, pValue1, pValue2) => { + var val = AL.getSourceParam('alGetSource3i', sourceId, param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { +#if OPENAL_DEBUG + dbg('alGetSource3i() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_DIRECTION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + {{{ makeSetValue('pValue0', '0', 'val[0]', 'i32') }}}; + {{{ makeSetValue('pValue1', '0', 'val[1]', 'i32') }}}; + {{{ makeSetValue('pValue2', '0', 'val[2]', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetSource3i(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alGetSourceiv__proxy: 'sync', + alGetSourceiv: (sourceId, param, pValues) => { + var val = AL.getSourceParam('alGetSourceiv', sourceId, param); + if (val === null) { + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alGetSourceiv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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 {{{ cDefs.AL_DISTANCE_MODEL }}}: + {{{ makeSetValue('pValues', '0', 'val', 'i32') }}}; + break; + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_DIRECTION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + {{{ makeSetValue('pValues', '0', 'val[0]', 'i32') }}}; + {{{ makeSetValue('pValues', '4', 'val[1]', 'i32') }}}; + {{{ makeSetValue('pValues', '8', 'val[2]', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + dbg(`alGetSourceiv(): param ${ptrToString(param)} has wrong signature`); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}}; + return; + } + }, + + alSourcef__proxy: 'sync', + alSourcef: (sourceId, param, value) => { + switch (param) { + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1003 /* AL_PITCH */: + case {{{ cDefs.AL_GAIN }}}: + 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; + } + }, + + alSource3f__proxy: 'sync', + alSource3f: (sourceId, param, value0, value1, value2) => { + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_DIRECTION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + 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; + } + }, + + alSourcefv__proxy: 'sync', + alSourcefv: (sourceId, param, pValues) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourcefv() called without a valid context'); +#endif + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alSourcefv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + return; + } + + switch (param) { + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1003 /* AL_PITCH */: + case {{{ cDefs.AL_GAIN }}}: + 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 = {{{ makeGetValue('pValues', '0', 'float') }}}; + AL.setSourceParam('alSourcefv', sourceId, param, val); + break; + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_DIRECTION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'float') }}}; + AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'float') }}}; + AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'float') }}}; + AL.setSourceParam('alSourcefv', sourceId, param, AL.paramArray); + break; + default: + AL.setSourceParam('alSourcefv', sourceId, param, null); + break; + } + }, + + alSourcei__proxy: 'sync', + 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 {{{ cDefs.AL_DISTANCE_MODEL }}}: + AL.setSourceParam('alSourcei', sourceId, param, value); + break; + default: + AL.setSourceParam('alSourcei', sourceId, param, null); + break; + } + }, + + alSource3i__proxy: 'sync', + alSource3i: (sourceId, param, value0, value1, value2) => { + switch (param) { + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_DIRECTION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + 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; + } + }, + + alSourceiv__proxy: 'sync', + alSourceiv: (sourceId, param, pValues) => { + if (!AL.currentCtx) { +#if OPENAL_DEBUG + dbg('alSourceiv() called without a valid context'); +#endif + return; + } + if (!pValues) { +#if OPENAL_DEBUG + dbg('alSourceiv() called with a null pointer'); +#endif + AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}}; + 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 {{{ cDefs.AL_DISTANCE_MODEL }}}: + var val = {{{ makeGetValue('pValues', '0', 'i32') }}}; + AL.setSourceParam('alSourceiv', sourceId, param, val); + break; + case {{{ cDefs.AL_POSITION }}}: + case {{{ cDefs.AL_DIRECTION }}}: + case {{{ cDefs.AL_VELOCITY }}}: + AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'i32') }}}; + AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'i32') }}}; + AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'i32') }}}; + AL.setSourceParam('alSourceiv', sourceId, param, AL.paramArray); + break; + default: + AL.setSourceParam('alSourceiv', sourceId, param, null); + break; + } + } +}; + +autoAddDeps(LibraryOpenAL, '$AL'); +addToLibrary(LibraryOpenAL); diff --git a/src/library_opfs.js b/src/lib/libopfs.js similarity index 100% rename from src/library_opfs.js rename to src/lib/libopfs.js diff --git a/src/lib/libpath.js b/src/lib/libpath.js new file mode 100644 index 0000000000000..1905aae0bf1af --- /dev/null +++ b/src/lib/libpath.js @@ -0,0 +1,136 @@ +/** + * @license + * Copyright 2013 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + $PATH: { + isAbs: (path) => path.charAt(0) === '/', + // split a filename into [root, dir, basename, ext], unix version + // 'root' is just a slash, or nothing. + 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; + }, + // This differs from node's path.basename in that it returns '/' for '/' + // rather than the empty string. + basename: (path) => path && path.match(/([^\/]+|\/)\/*$/)[1], + join: (...paths) => PATH.normalize(paths.join('/')), + join2: (l, r) => PATH.normalize(l + '/' + r), + }, + // The FS-using parts are split out into a separate object, so simple path + // usage does not require the FS. + $PATH_FS__deps: [ + '$PATH', + '$FS', +#if WASMFS + // In WasmFS, FS.cwd() is implemented via a call into wasm, so we need to + // add a dependency on that. + '_wasmfs_get_cwd', +#endif + ], + $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('/'); + } + } +}); diff --git a/src/lib/libpipefs.js b/src/lib/libpipefs.js new file mode 100644 index 0000000000000..d9a3fed62180e --- /dev/null +++ b/src/lib/libpipefs.js @@ -0,0 +1,255 @@ +/** + * @license + * Copyright 2017 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + $PIPEFS__postset: () => addAtInit('PIPEFS.root = FS.mount(PIPEFS, {}, null);'), + $PIPEFS__deps: ['$FS'], + $PIPEFS: { + BUCKET_BUFFER_SIZE: 1024 * 8, // 8KiB Buffer + 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, '/', {{{ cDefs.S_IFDIR }}} | 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, {{{ cDefs.S_IFIFO }}}, 0); + var wNode = FS.createNode(PIPEFS.root, wName, {{{ cDefs.S_IFIFO }}}, 0); + + rNode.pipe = pipe; + wNode.pipe = pipe; + + var readableStream = FS.createStream({ + path: rName, + node: rNode, + flags: {{{ cDefs.O_RDONLY }}}, + seekable: false, + stream_ops: PIPEFS.stream_ops + }); + rNode.stream = readableStream; + + var writableStream = FS.createStream({ + path: wName, + node: wNode, + flags: {{{ cDefs.O_WRONLY }}}, + 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 & {{{ cDefs.O_ACCMODE }}}) === {{{ cDefs.O_WRONLY }}}) { + return ({{{ cDefs.POLLWRNORM }}} | {{{ cDefs.POLLOUT }}}); + } + for (var bucket of pipe.buckets) { + if (bucket.offset - bucket.roffset > 0) { + return ({{{ cDefs.POLLRDNORM }}} | {{{ cDefs.POLLIN }}}); + } + } + + return 0; + }, + dup(stream) { + stream.node.pipe.refcnt++; + }, + ioctl(stream, request, varargs) { + return {{{ cDefs.EINVAL }}}; + }, + fsync(stream) { + return {{{ cDefs.EINVAL }}}; + }, + 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; + } + +#if ASSERTIONS && !(MEMORY64 && MAXIMUM_MEMORY > FOUR_GB) +#if PTHREADS + assert(buffer instanceof ArrayBuffer || buffer instanceof SharedArrayBuffer || ArrayBuffer.isView(buffer)); +#else + assert(buffer instanceof ArrayBuffer || ArrayBuffer.isView(buffer)); +#endif +#endif + var data = buffer.subarray(offset, offset + length); + + if (length <= 0) { + return 0; + } + if (currentLength == 0) { + // Behave as if the read end is always non-blocking + throw new FS.ErrnoError({{{ cDefs.EAGAIN }}}); + } + 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; + +#if ASSERTIONS && !(MEMORY64 && MAXIMUM_MEMORY > FOUR_GB) +#if PTHREADS + assert(buffer instanceof ArrayBuffer || buffer instanceof SharedArrayBuffer || ArrayBuffer.isView(buffer)); +#else + assert(buffer instanceof ArrayBuffer || ArrayBuffer.isView(buffer)); +#endif +#endif + 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]; + } + +#if ASSERTIONS + assert(currBucket.offset <= PIPEFS.BUCKET_BUFFER_SIZE); +#endif + + 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++) + ']'; + }, + }, +}); diff --git a/src/lib/libpromise.js b/src/lib/libpromise.js new file mode 100644 index 0000000000000..db4838e09cc22 --- /dev/null +++ b/src/lib/libpromise.js @@ -0,0 +1,277 @@ +/** + * @license + * Copyright 2023 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + $promiseMap__deps: ['$HandleAllocator'], + $promiseMap: "new HandleAllocator();", + + $getPromise__deps: ['$promiseMap'], + $getPromise: (id) => promiseMap.get(id).promise, + + $makePromise__deps: ['$promiseMap'], + $makePromise: () => { + var promiseInfo = {}; + promiseInfo.promise = new Promise((resolve, reject) => { + promiseInfo.reject = reject; + promiseInfo.resolve = resolve; + }); + promiseInfo.id = promiseMap.allocate(promiseInfo); +#if RUNTIME_DEBUG + dbg(`makePromise: ${promiseInfo.id}`); +#endif + return promiseInfo; + }, + + $idsToPromises__deps: ['$getPromise'], + $idsToPromises: (idBuf, size) => { + var promises = []; + for (var i = 0; i < size; i++) { + var id = {{{ makeGetValue('idBuf', `i*${POINTER_SIZE}`, 'i32') }}}; + promises[i] = getPromise(id); + } + return promises; + }, + + emscripten_promise_create__deps: ['$makePromise'], + emscripten_promise_create: () => makePromise().id, + + emscripten_promise_destroy__deps: ['$promiseMap'], + emscripten_promise_destroy: (id) => { +#if RUNTIME_DEBUG + dbg(`emscripten_promise_destroy: ${id}`); +#endif + promiseMap.free(id); + }, + + emscripten_promise_resolve__deps: ['$promiseMap', + '$getPromise', + 'emscripten_promise_destroy'], + emscripten_promise_resolve: (id, result, value) => { +#if RUNTIME_DEBUG + dbg(`emscripten_promise_resolve: ${id}`); +#endif + var info = promiseMap.get(id); + switch (result) { + case {{{ cDefs.EM_PROMISE_FULFILL }}}: + info.resolve(value); + return; + case {{{ cDefs.EM_PROMISE_MATCH }}}: + info.resolve(getPromise(value)); + return; + case {{{ cDefs.EM_PROMISE_MATCH_RELEASE }}}: + info.resolve(getPromise(value)); + _emscripten_promise_destroy(value); + return; + case {{{ cDefs.EM_PROMISE_REJECT }}}: + info.reject(value); + return; + } +#if ASSERTIONS + abort("unexpected promise callback result " + result); +#endif + }, + + $makePromiseCallback__deps: ['$getPromise', + '$POINTER_SIZE', + 'emscripten_promise_destroy', + '$stackAlloc', + '$stackRestore', + '$stackSave'], + $makePromiseCallback: (callback, userData) => { + return (value) => { +#if RUNTIME_DEBUG + dbg(`emscripten promise callback: ${value}`); +#endif + {{{ runtimeKeepalivePop() }}}; + var stack = stackSave(); + // Allocate space for the result value and initialize it to NULL. + var resultPtr = stackAlloc(POINTER_SIZE); + {{{ makeSetValue('resultPtr', 0, '0', '*') }}}; + try { + var result = + {{{ makeDynCall('ippp', 'callback') }}}(resultPtr, userData, value); + var resultVal = {{{ makeGetValue('resultPtr', 0, '*') }}}; + } 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 MEMORY64 + if (typeof e != 'bigint') { + throw 0n; + } +#else + if (typeof e != 'number') { + throw 0; + } +#endif + throw e; + } finally { + // Thrown errors will reject the promise, but at least we will restore + // the stack first. + stackRestore(stack); + } + switch (result) { + case {{{ cDefs.EM_PROMISE_FULFILL }}}: + return resultVal; + case {{{ cDefs.EM_PROMISE_MATCH }}}: + return getPromise(resultVal); + case {{{ cDefs.EM_PROMISE_MATCH_RELEASE }}}: + var ret = getPromise(resultVal); + _emscripten_promise_destroy(resultVal); + return ret; + case {{{ cDefs.EM_PROMISE_REJECT }}}: + throw resultVal; + } +#if ASSERTIONS + abort("unexpected promise callback result " + result); +#endif + }; + }, + + emscripten_promise_then__deps: ['$promiseMap', + '$getPromise', + '$makePromiseCallback'], + emscripten_promise_then: (id, onFulfilled, onRejected, userData) => { +#if RUNTIME_DEBUG + dbg(`emscripten_promise_then: ${id}`); +#endif + {{{ runtimeKeepalivePush() }}}; + var promise = getPromise(id); + var newId = promiseMap.allocate({ + promise: promise.then(makePromiseCallback(onFulfilled, userData), + makePromiseCallback(onRejected, userData)) + }); +#if RUNTIME_DEBUG + dbg(`emscripten_promise_then: -> ${newId}`); +#endif + return newId; + }, + + emscripten_promise_all__deps: ['$promiseMap', '$idsToPromises'], + emscripten_promise_all: (idBuf, resultBuf, size) => { + var promises = idsToPromises(idBuf, size); +#if RUNTIME_DEBUG + dbg(`emscripten_promise_all: ${promises}`); +#endif + var id = promiseMap.allocate({ + promise: Promise.all(promises).then((results) => { + if (resultBuf) { + for (var i = 0; i < size; i++) { + var result = results[i]; + {{{ makeSetValue('resultBuf', `i*${POINTER_SIZE}`, 'result', '*') }}}; + } + } + return resultBuf; + }) + }); +#if RUNTIME_DEBUG + dbg(`create: ${id}`); +#endif + return id; + }, + + $setPromiseResult__internal: true, + $setPromiseResult: (ptr, fulfill, value) => { +#if ASSERTIONS + assert(typeof value == 'undefined' || typeof value === 'number', `native promises can only handle numeric results (${value} ${typeof value})`); +#endif + var result = fulfill ? {{{ cDefs.EM_PROMISE_FULFILL }}} : {{{ cDefs.EM_PROMISE_REJECT }}} + {{{ makeSetValue('ptr', C_STRUCTS.em_settled_result_t.result, 'result', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.em_settled_result_t.value, 'value', '*') }}}; + }, + + emscripten_promise_all_settled__deps: ['$promiseMap', '$idsToPromises', '$setPromiseResult'], + emscripten_promise_all_settled: (idBuf, resultBuf, size) => { + var promises = idsToPromises(idBuf, size); +#if RUNTIME_DEBUG + dbg(`emscripten_promise_all_settled: ${promises}`); +#endif + var id = promiseMap.allocate({ + promise: Promise.allSettled(promises).then((results) => { + if (resultBuf) { + var offset = resultBuf; + for (var i = 0; i < size; i++, offset += {{{ C_STRUCTS.em_settled_result_t.__size__ }}}) { + if (results[i].status === 'fulfilled') { + setPromiseResult(offset, true, results[i].value); + } else { + setPromiseResult(offset, false, results[i].reason); + } + } + } + return resultBuf; + }) + }); +#if RUNTIME_DEBUG + dbg(`create: ${id}`); +#endif + return id; + }, + + emscripten_promise_any__deps: [ + '$promiseMap', '$idsToPromises', +#if !SUPPORTS_PROMISE_ANY && !INCLUDE_FULL_LIBRARY + () => error("emscripten_promise_any used, but Promise.any is not supported by the current runtime configuration (run with EMCC_DEBUG=1 in the env for more details)"), +#endif + ], + emscripten_promise_any: (idBuf, errorBuf, size) => { + var promises = idsToPromises(idBuf, size); +#if RUNTIME_DEBUG + dbg(`emscripten_promise_any: ${promises}`); +#endif +#if ASSERTIONS + assert(typeof Promise.any != 'undefined', "Promise.any does not exist"); +#endif + var id = promiseMap.allocate({ + promise: Promise.any(promises).catch((err) => { + if (errorBuf) { + for (var i = 0; i < size; i++) { + {{{ makeSetValue('errorBuf', `i*${POINTER_SIZE}`, 'err.errors[i]', '*') }}}; + } + } + throw errorBuf; + }) + }); +#if RUNTIME_DEBUG + dbg(`create: ${id}`); +#endif + return id; + }, + + emscripten_promise_race__deps: ['$promiseMap', '$idsToPromises'], + emscripten_promise_race: (idBuf, size) => { + var promises = idsToPromises(idBuf, size); +#if RUNTIME_DEBUG + dbg(`emscripten_promise_race: ${promises}`); +#endif + var id = promiseMap.allocate({ + promise: Promise.race(promises) + }); +#if RUNTIME_DEBUG + dbg(`create: ${id}`); +#endif + return id; + }, + + emscripten_promise_await__async: true, +#if ASYNCIFY + emscripten_promise_await__deps: ['$getPromise', '$setPromiseResult'], +#endif + emscripten_promise_await: (returnValuePtr, id) => { +#if ASYNCIFY +#if RUNTIME_DEBUG + dbg(`emscripten_promise_await: ${id}`); +#endif + return Asyncify.handleAsync(() => getPromise(id).then( + value => setPromiseResult(returnValuePtr, true, value), + error => setPromiseResult(returnValuePtr, false, error) + )); +#else + abort('emscripten_promise_await is only available with ASYNCIFY'); +#endif + }, +}); diff --git a/src/lib/libproxyfs.js b/src/lib/libproxyfs.js new file mode 100644 index 0000000000000..6a2a9d35efe49 --- /dev/null +++ b/src/lib/libproxyfs.js @@ -0,0 +1,224 @@ +/** + * @license + * Copyright 2016 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +addToLibrary({ + $PROXYFS__deps: ['$FS', '$PATH', '$ERRNO_CODES'], + $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 === {{{ cDefs.SEEK_CUR }}}) { + position += stream.position; + } else if (whence === {{{ cDefs.SEEK_END }}}) { + 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; + } + } + } +}); + +if (WASMFS) { + error("using -lproxyfs is not currently supported in WasmFS."); +} diff --git a/src/lib/libpthread.js b/src/lib/libpthread.js new file mode 100644 index 0000000000000..b4885c15f0a15 --- /dev/null +++ b/src/lib/libpthread.js @@ -0,0 +1,1287 @@ +/** + * @license + * Copyright 2015 The Emscripten Authors + * SPDX-License-Identifier: MIT + * + * Because only modern JS engines support SAB we can use modern JS language + * features within this file (ES2020). + */ + +#if !PTHREADS +#error "Internal error! PTHREADS should be enabled when including library_pthread.js." +#endif +#if !SHARED_MEMORY +#error "Internal error! SHARED_MEMORY should be enabled when including library_pthread.js." +#endif +#if PTHREADS == 2 +#error "PTHREADS=2 is no longer supported" +#endif +#if BUILD_AS_WORKER +#error "pthreads + BUILD_AS_WORKER require separate modes that don't work together, see https://github.com/emscripten-core/emscripten/issues/8854" +#endif +#if EVAL_CTORS +#error "EVAL_CTORS is not compatible with pthreads yet (passive segments)" +#endif +#if EXPORT_ES6 && (MIN_FIREFOX_VERSION < 114 || MIN_CHROME_VERSION < 80 || MIN_SAFARI_VERSION < 150000) +#error "internal error, feature_matrix should not allow this" +#endif + +{{{ +#if MEMORY64 +const MAX_PTR = Number((2n ** 64n) - 1n); +#else +const MAX_PTR = (2 ** 32) - 1 +#endif + +#if WASM_ESM_INTEGRATION +const pthreadWorkerScript = TARGET_BASENAME + '.pthread.mjs'; +#else +const pthreadWorkerScript = TARGET_JS_NAME; +#endif + +// Use a macro to avoid duplicating pthread worker options. +// We cannot use a normal JS variable since the vite bundler requires that worker +// options be inline. +// See https://github.com/emscripten-core/emscripten/issues/22394 +const pthreadWorkerOptions = `{ +#if EXPORT_ES6 + 'type': 'module', +#endif +#if ENVIRONMENT_MAY_BE_NODE + // This is the way that we signal to the node worker that it is hosting + // a pthread. + 'workerData': 'em-pthread', +#if WASMFS + // In WasmFS, close() is not proxied to the main thread. Suppress + // warnings when a thread closes a file descriptor it didn't open. + // See: https://github.com/emscripten-core/emscripten/issues/24731 + 'trackUnmanagedFds': false, +#endif +#endif +#if ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER + // This is the way that we signal to the Web Worker that it is hosting + // a pthread. +#if ASSERTIONS + 'name': 'em-pthread-' + PThread.nextWorkerID, +#else + 'name': 'em-pthread', +#endif +#endif +}`; +}}} + +var LibraryPThread = { + $PThread__postset: 'PThread.init();', + $PThread__deps: ['_emscripten_thread_init', + '$terminateWorker', + '$cleanupThread', + '$addOnPreRun', +#if MAIN_MODULE + '$markAsFinished', +#endif +#if !MINIMAL_RUNTIME && PTHREAD_POOL_SIZE && !PTHREAD_POOL_DELAY_LOAD + '$addRunDependency', + '$removeRunDependency', +#endif + '$spawnThread', + '_emscripten_thread_free_data', + 'exit', +#if PTHREADS_DEBUG || ASSERTIONS + '$ptrToString', +#endif + ], + $PThread: { + // Contains all Workers that are idle/unused and not currently hosting an + // executing pthread. Unused Workers can either be pooled up before page + // startup, but also when a pthread quits, its hosting Worker is not + // terminated, but is returned to this pool as an optimization so that + // starting the next thread is faster. + unusedWorkers: [], + // Contains all Workers that are currently hosting an active pthread. + runningWorkers: [], + tlsInitFunctions: [], + // Maps pthread_t pointers to the workers on which they are running. For + // the reverse mapping, each worker has a `pthread_ptr` when its running a + // pthread. + pthreads: {}, +#if ASSERTIONS + nextWorkerID: 1, +#endif + init() { + if ({{{ ENVIRONMENT_IS_MAIN_THREAD() }}}) { + PThread.initMainThread(); + } + }, + initMainThread() { +#if PTHREAD_POOL_SIZE + var pthreadPoolSize = {{{ PTHREAD_POOL_SIZE }}}; + // Start loading up the Worker pool, if requested. + while (pthreadPoolSize--) { + PThread.allocateUnusedWorker(); + } +#endif +#if !MINIMAL_RUNTIME && PTHREAD_POOL_SIZE + // MINIMAL_RUNTIME takes care of calling loadWasmModuleToAllWorkers + // in postamble_minimal.js + addOnPreRun(async () => { + var pthreadPoolReady = PThread.loadWasmModuleToAllWorkers(); +#if !PTHREAD_POOL_DELAY_LOAD + addRunDependency('loading-workers'); + await pthreadPoolReady; + removeRunDependency('loading-workers'); +#endif // PTHREAD_POOL_DELAY_LOAD + }); +#endif // !MINIMAL_RUNTIME && PTHREAD_POOL_SIZE +#if MAIN_MODULE + PThread.outstandingPromises = {}; + // Finished threads are threads that have finished running but we not yet + // joined. + PThread.finishedThreads = new Set(); +#endif + }, + +#if PTHREADS_PROFILING + getThreadName(pthreadPtr) { + var profilerBlock = {{{ makeGetValue('pthreadPtr', C_STRUCTS.pthread.profilerBlock, '*') }}}; + if (!profilerBlock) return ""; + return UTF8ToString(profilerBlock + {{{ C_STRUCTS.thread_profiler_block.name }}}); + }, + + threadStatusToString(threadStatus) { + switch (threadStatus) { + case 0: return "not yet started"; + case 1: return "running"; + case 2: return "sleeping"; + case 3: return "waiting for a futex"; + case 4: return "waiting for a mutex"; + case 5: return "waiting for a proxied operation"; + case 6: return "finished execution"; + default: return "unknown (corrupt?!)"; + } + }, + + threadStatusAsString(pthreadPtr) { + var profilerBlock = {{{ makeGetValue('pthreadPtr', C_STRUCTS.pthread.profilerBlock, '*') }}}; + var status = (profilerBlock == 0) ? 0 : Atomics.load(HEAPU32, {{{ getHeapOffset('profilerBlock + ' + C_STRUCTS.thread_profiler_block.threadStatus, 'i32') }}}); + return PThread.threadStatusToString(status); + }, +#endif + + terminateAllThreads: () => { +#if ASSERTIONS + assert(!ENVIRONMENT_IS_PTHREAD, 'Internal Error! terminateAllThreads() can only ever be called from main application thread!'); +#endif +#if PTHREADS_DEBUG + dbg('terminateAllThreads'); +#endif + // Attempt to kill all workers. Sadly (at least on the web) there is no + // way to terminate a worker synchronously, or to be notified when a + // worker in actually terminated. This means there is some risk that + // pthreads will continue to be executing after `worker.terminate` has + // returned. For this reason, we don't call `returnWorkerToPool` here or + // free the underlying pthread data structures. + for (var worker of PThread.runningWorkers) { + terminateWorker(worker); + } + for (var worker of PThread.unusedWorkers) { + terminateWorker(worker); + } + PThread.unusedWorkers = []; + PThread.runningWorkers = []; + PThread.pthreads = {}; + }, + returnWorkerToPool: (worker) => { + // We don't want to run main thread queued calls here, since we are doing + // some operations that leave the worker queue in an invalid state until + // we are completely done (it would be bad if free() ends up calling a + // queued pthread_create which looks at the global data structures we are + // modifying). To achieve that, defer the free() til the very end, when + // we are all done. + var pthread_ptr = worker.pthread_ptr; + delete PThread.pthreads[pthread_ptr]; + // Note: worker is intentionally not terminated so the pool can + // dynamically grow. + PThread.unusedWorkers.push(worker); + PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker), 1); + // Not a running Worker anymore + // Detach the worker from the pthread object, and return it to the + // worker pool as an unused worker. + worker.pthread_ptr = 0; + +#if ENVIRONMENT_MAY_BE_NODE && PROXY_TO_PTHREAD + if (ENVIRONMENT_IS_NODE) { + // Once the proxied main thread has finished, mark it as weakly + // referenced so that its existence does not prevent Node.js from + // exiting. This has no effect if the worker is already weakly + // referenced. + worker.unref(); + } +#endif + + // Finally, free the underlying (and now-unused) pthread structure in + // linear memory. + __emscripten_thread_free_data(pthread_ptr); + }, +#if OFFSCREENCANVAS_SUPPORT + receiveOffscreenCanvases(data) { + if (typeof GL != 'undefined') { + Object.assign(GL.offscreenCanvases, data.offscreenCanvases); + if (!Module['canvas'] && data.moduleCanvasId && GL.offscreenCanvases[data.moduleCanvasId]) { + Module['canvas'] = GL.offscreenCanvases[data.moduleCanvasId].offscreenCanvas; + Module['canvas'].id = data.moduleCanvasId; + } + } + }, +#endif + // Called by worker.js each time a thread is started. + threadInitTLS() { +#if PTHREADS_DEBUG + dbg('threadInitTLS'); +#endif + // Call thread init functions (these are the _emscripten_tls_init for each + // module loaded. + PThread.tlsInitFunctions.forEach((f) => f()); + }, + // Loads the WebAssembly module into the given Worker. + // onFinishedLoading: A callback function that will be called once all of + // the workers have been initialized and are + // ready to host pthreads. + loadWasmModuleToWorker: (worker) => new Promise((onFinishedLoading) => { + worker.onmessage = (e) => { + var d = e['data']; + var cmd = d.cmd; +#if PTHREADS_DEBUG + dbg(`main thread: received message '${cmd}' from worker. ${d}`); +#endif + + // If this message is intended to a recipient that is not the main + // thread, forward it to the target thread. + if (d.targetThread && d.targetThread != _pthread_self()) { + var targetWorker = PThread.pthreads[d.targetThread]; + if (targetWorker) { + targetWorker.postMessage(d, d.transferList); + } else { + err(`Internal error! Worker sent a message "${cmd}" to target pthread ${d.targetThread}, but that thread no longer exists!`); + } + return; + } + + if (cmd === 'checkMailbox') { + checkMailbox(); + } else if (cmd === 'spawnThread') { + spawnThread(d); + } else if (cmd === 'cleanupThread') { + // cleanupThread needs to be run via callUserCallback since it calls + // back into user code to free thread data. Without this it's possible + // the unwind or ExitStatus exception could escape here. + callUserCallback(() => cleanupThread(d.thread)); +#if MAIN_MODULE + } else if (cmd === 'markAsFinished') { + markAsFinished(d.thread); +#endif + } else if (cmd === 'loaded') { + worker.loaded = true; +#if ENVIRONMENT_MAY_BE_NODE && PTHREAD_POOL_SIZE + // Check that this worker doesn't have an associated pthread. + if (ENVIRONMENT_IS_NODE && !worker.pthread_ptr) { + // Once worker is loaded & idle, mark it as weakly referenced, + // so that mere existence of a Worker in the pool does not prevent + // Node.js from exiting the app. + worker.unref(); + } +#endif + onFinishedLoading(worker); + } else if (d.target === 'setimmediate') { + // Worker wants to postMessage() to itself to implement setImmediate() + // emulation. + worker.postMessage(d); +#if ENVIRONMENT_MAY_BE_NODE + } else if (cmd === 'uncaughtException') { + // Message handler for Node.js specific out-of-order behavior: + // https://github.com/nodejs/node/issues/59617 + // A pthread sent an uncaught exception event. Re-raise it on the main thread. + worker.onerror(d.error); +#endif + } else if (cmd === 'callHandler') { + Module[d.handler](...d.args); + } else if (cmd) { + // The received message looks like something that should be handled by this message + // handler, (since there is a e.data.cmd field present), but is not one of the + // recognized commands: + err(`worker sent an unknown command ${cmd}`); + } + }; + + worker.onerror = (e) => { + var message = 'worker sent an error!'; +#if ASSERTIONS + if (worker.pthread_ptr) { + message = `Pthread ${ptrToString(worker.pthread_ptr)} sent an error!`; + } +#endif + err(`${message} ${e.filename}:${e.lineno}: ${e.message}`); + throw e; + }; + +#if ENVIRONMENT_MAY_BE_NODE + if (ENVIRONMENT_IS_NODE) { + worker.on('message', (data) => worker.onmessage({ data: data })); + worker.on('error', (e) => worker.onerror(e)); + +#if PTHREADS_DEBUG + worker.on('exit', (code) => { + if (worker.pthread_ptr) dbg(`Worker hosting pthread ${ptrToString(worker.pthread_ptr)} has terminated with code ${code}.`); + else dbg(`Worker has terminated with code ${code}.`); + }); +#endif + } +#endif + +#if ASSERTIONS + assert(wasmMemory instanceof WebAssembly.Memory, 'WebAssembly memory should have been loaded by now!'); +#if !WASM_ESM_INTEGRATION + assert(wasmModule instanceof WebAssembly.Module, 'WebAssembly Module should have been loaded by now!'); +#endif +#endif + + // When running on a pthread, none of the incoming parameters on the module + // object are present. Proxy known handlers back to the main thread if specified. + var handlers = []; + var knownHandlers = [ +#if expectToReceiveOnModule('onExit') + 'onExit', +#endif +#if expectToReceiveOnModule('onAbort') + 'onAbort', +#endif +#if expectToReceiveOnModule('print') + 'print', +#endif +#if expectToReceiveOnModule('printErr') + 'printErr', +#endif + ]; + for (var handler of knownHandlers) { + if (Module.propertyIsEnumerable(handler)) { + handlers.push(handler); + } + } + + // Ask the new worker to load up the Emscripten-compiled page. This is a heavy operation. + worker.postMessage({ + cmd: 'load', + handlers: handlers, +#if WASM2JS + // the polyfill WebAssembly.Memory instance has function properties, + // which will fail in postMessage, so just send a custom object with the + // property we need, the buffer + wasmMemory: { 'buffer': wasmMemory.buffer }, +#else // WASM2JS + wasmMemory, +#endif // WASM2JS +#if !WASM_ESM_INTEGRATION + wasmModule, +#endif +#if LOAD_SOURCE_MAP + wasmSourceMap, +#endif +#if MAIN_MODULE + dynamicLibraries, + // Share all modules that have been loaded so far. New workers + // won't start running threads until these are all loaded. + sharedModules, +#endif +#if ASSERTIONS + 'workerID': worker.workerID, +#endif + }); + }), + +#if PTHREAD_POOL_SIZE + async loadWasmModuleToAllWorkers() { + // Instantiation is synchronous in pthreads. + if ( + ENVIRONMENT_IS_PTHREAD +#if WASM_WORKERS + || ENVIRONMENT_IS_WASM_WORKER +#endif + ) { + return; + } + + let pthreadPoolReady = Promise.all(PThread.unusedWorkers.map(PThread.loadWasmModuleToWorker)); +#if PTHREAD_POOL_DELAY_LOAD + // PTHREAD_POOL_DELAY_LOAD means we want to proceed synchronously without + // waiting for the pthread pool during the startup phase. + // If the user wants to wait on it elsewhere, they can do so via the + // Module['pthreadPoolReady'] promise. + Module['pthreadPoolReady'] = pthreadPoolReady; + return; +#endif + return pthreadPoolReady; + }, +#endif // PTHREAD_POOL_SIZE + + // Creates a new web Worker and places it in the unused worker pool to wait for its use. + allocateUnusedWorker() { + var worker; +#if EXPORT_ES6 + // If we're using module output, use bundler-friendly pattern. +#if PTHREADS_DEBUG + dbg(`Allocating a new web worker from ${import.meta.url}`); +#endif +#if TRUSTED_TYPES + // Use Trusted Types compatible wrappers. + if (globalThis.trustedTypes?.createPolicy) { + var p = trustedTypes.createPolicy('emscripten#workerPolicy1', { createScriptURL: (ignored) => new URL('{{{ pthreadWorkerScript }}}', import.meta.url) }); + worker = new Worker(p.createScriptURL('ignored'), {{{ pthreadWorkerOptions }}}); + } else +#endif +#if expectToReceiveOnModule('mainScriptUrlOrBlob') + if (Module['mainScriptUrlOrBlob']) { + var pthreadMainJs = Module['mainScriptUrlOrBlob']; + if (typeof pthreadMainJs != 'string') { + pthreadMainJs = URL.createObjectURL(pthreadMainJs); + } + worker = new Worker(pthreadMainJs, {{{ pthreadWorkerOptions }}}); + } else +#endif + // We need to generate the URL with import.meta.url as the base URL of the JS file + // instead of just using new URL(import.meta.url) because bundler's only recognize + // the first case in their bundling step. The latter ends up producing an invalid + // URL to import from the server (e.g., for webpack the file:// path). + // See https://github.com/webpack/webpack/issues/12638 + worker = new Worker(new URL('{{{ pthreadWorkerScript }}}', import.meta.url), {{{ pthreadWorkerOptions }}}); +#else // EXPORT_ES6 + var pthreadMainJs = _scriptName; +#if expectToReceiveOnModule('mainScriptUrlOrBlob') + // We can't use makeModuleReceiveWithVar here since we want to also + // call URL.createObjectURL on the mainScriptUrlOrBlob. + if (Module['mainScriptUrlOrBlob']) { + pthreadMainJs = Module['mainScriptUrlOrBlob']; + if (typeof pthreadMainJs != 'string') { + pthreadMainJs = URL.createObjectURL(pthreadMainJs); + } + } +#endif +#if PTHREADS_DEBUG + dbg(`Allocating a new web worker from ${pthreadMainJs}`); +#endif +#if TRUSTED_TYPES + // Use Trusted Types compatible wrappers. + if (globalThis.trustedTypes?.createPolicy) { + var p = trustedTypes.createPolicy('emscripten#workerPolicy2', { createScriptURL: (ignored) => pthreadMainJs }); + worker = new Worker(p.createScriptURL('ignored'), {{{ pthreadWorkerOptions }}}); + } else +#endif + worker = new Worker(pthreadMainJs, {{{ pthreadWorkerOptions }}}); +#endif // EXPORT_ES6 +#if ASSERTIONS + worker.workerID = PThread.nextWorkerID++; +#endif + PThread.unusedWorkers.push(worker); + }, + + getNewWorker() { + if (PThread.unusedWorkers.length == 0) { +// PTHREAD_POOL_SIZE_STRICT should show a warning and, if set to level `2`, return from the function. +#if (PTHREAD_POOL_SIZE_STRICT && ASSERTIONS) || PTHREAD_POOL_SIZE_STRICT == 2 +// However, if we're in Node.js, then we can create new workers on the fly and PTHREAD_POOL_SIZE_STRICT +// should be ignored altogether. +#if ENVIRONMENT_MAY_BE_NODE + if (!ENVIRONMENT_IS_NODE) { +#endif +#if ASSERTIONS + err('Tried to spawn a new thread, but the thread pool is exhausted.\n' + + 'This might result in a deadlock unless some threads eventually exit or the code explicitly breaks out to the event loop.\n' + + 'If you want to increase the pool size, use setting `-sPTHREAD_POOL_SIZE=...`.' +#if PTHREAD_POOL_SIZE_STRICT == 1 + + '\nIf you want to throw an explicit error instead of the risk of deadlocking in those cases, use setting `-sPTHREAD_POOL_SIZE_STRICT=2`.' +#endif + ); +#endif // ASSERTIONS +#if PTHREAD_POOL_SIZE_STRICT == 2 + return; +#endif +#if ENVIRONMENT_MAY_BE_NODE + } +#endif +#endif // PTHREAD_POOL_SIZE_STRICT +#if PTHREAD_POOL_SIZE_STRICT < 2 || ENVIRONMENT_MAY_BE_NODE + PThread.allocateUnusedWorker(); + PThread.loadWasmModuleToWorker(PThread.unusedWorkers[0]); +#endif + } + return PThread.unusedWorkers.pop(); + } + }, + + $terminateWorker: (worker) => { +#if PTHREADS_DEBUG + dbg(`terminateWorker: ${worker.workerID}`); +#endif + worker.terminate(); + // terminate() can be asynchronous, so in theory the worker can continue + // to run for some amount of time after termination. However from our POV + // the worker now dead and we don't want to hear from it again, so we stub + // out its message handler here. This avoids having to check in each of + // the onmessage handlers if the message was coming from valid worker. + worker.onmessage = (e) => { +#if ASSERTIONS + var cmd = e['data'].cmd; + err(`received "${cmd}" command from terminated worker: ${worker.workerID}`); +#endif + }; + }, + + _emscripten_thread_cleanup: (thread) => { + // Called when a thread needs to be cleaned up so it can be reused. + // A thread is considered reusable when it either returns from its + // entry point, calls pthread_exit, or acts upon a cancellation. + // Detached threads are responsible for calling this themselves, + // otherwise pthread_join is responsible for calling this. +#if PTHREADS_DEBUG + dbg(`_emscripten_thread_cleanup: ${ptrToString(thread)}`) +#endif + if (!ENVIRONMENT_IS_PTHREAD) cleanupThread(thread); + else postMessage({ cmd: 'cleanupThread', thread }); + }, + + _emscripten_thread_set_strongref: (thread) => { + // Called when a thread needs to be strongly referenced. + // Currently only used for: + // - keeping the "main" thread alive in PROXY_TO_PTHREAD mode; + // - crashed threads that needs to propagate the uncaught exception + // back to the main thread. +#if ENVIRONMENT_MAY_BE_NODE + if (ENVIRONMENT_IS_NODE) { + PThread.pthreads[thread].ref(); + } +#endif + }, + + $cleanupThread: (pthread_ptr) => { +#if PTHREADS_DEBUG + dbg(`cleanupThread: ${ptrToString(pthread_ptr)}`) +#endif +#if ASSERTIONS + assert(!ENVIRONMENT_IS_PTHREAD, 'Internal Error! cleanupThread() can only ever be called from main application thread!'); + assert(pthread_ptr, 'Internal Error! Null pthread_ptr in cleanupThread!'); +#endif + var worker = PThread.pthreads[pthread_ptr]; +#if MAIN_MODULE + PThread.finishedThreads.delete(pthread_ptr); + if (pthread_ptr in PThread.outstandingPromises) { + PThread.outstandingPromises[pthread_ptr].resolve(); + } +#endif +#if ASSERTIONS + assert(worker); +#endif + PThread.returnWorkerToPool(worker); + }, + +#if MAIN_MODULE + $registerTLSInit: (tlsInitFunc, moduleExports, metadata) => { +#if DYLINK_DEBUG + dbg("registerTLSInit: " + tlsInitFunc); +#endif + // In relocatable builds, we use the result of calling tlsInitFunc + // (`_emscripten_tls_init`) to relocate the TLS exports of the module + // according to this new __tls_base. + function tlsInitWrapper() { + var __tls_base = tlsInitFunc(); +#if DYLINK_DEBUG + dbg(`tlsInit -> ${__tls_base}`); +#endif + if (!__tls_base) { +#if ASSERTIONS + // __tls_base should never be zero if there are tls exports + assert(__tls_base || metadata.tlsExports.size == 0); +#endif + return; + } + var tlsExports = {}; + metadata.tlsExports.forEach((s) => tlsExports[s] = moduleExports[s]); + relocateExports(tlsExports, __tls_base, /*replace=*/true); + } + + // Register this function so that its gets called for each thread on + // startup. + PThread.tlsInitFunctions.push(tlsInitWrapper); + + // If the main thread is already running we also need to call this function + // now. If the main thread is not yet running this will happen when it + // is initialized and processes `PThread.tlsInitFunctions`. + if (runtimeInitialized) { + tlsInitWrapper(); + } + }, +#else + $registerTLSInit: (tlsInitFunc) => PThread.tlsInitFunctions.push(tlsInitFunc), +#endif + + $spawnThread: (threadParams) => { +#if ASSERTIONS + assert(!ENVIRONMENT_IS_PTHREAD, 'Internal Error! spawnThread() can only ever be called from main application thread!'); + assert(threadParams.pthread_ptr, 'Internal error, no pthread ptr!'); +#endif + + var worker = PThread.getNewWorker(); + if (!worker) { + // No available workers in the PThread pool. + return {{{ cDefs.EAGAIN }}}; + } +#if ASSERTIONS + assert(!worker.pthread_ptr, 'Internal error!'); +#endif + + PThread.runningWorkers.push(worker); + + // Add to pthreads map + PThread.pthreads[threadParams.pthread_ptr] = worker; + + worker.pthread_ptr = threadParams.pthread_ptr; + var msg = { + cmd: 'run', + start_routine: threadParams.startRoutine, + arg: threadParams.arg, + pthread_ptr: threadParams.pthread_ptr, + }; +#if OFFSCREENCANVAS_SUPPORT + // Note that we do not need to quote these names because they are only used + // in this file, and not from the external worker.js. + msg.moduleCanvasId = threadParams.moduleCanvasId; + msg.offscreenCanvases = threadParams.offscreenCanvases; +#endif +#if ENVIRONMENT_MAY_BE_NODE + if (ENVIRONMENT_IS_NODE) { + // Mark worker as weakly referenced once we start executing a pthread, + // so that its existence does not prevent Node.js from exiting. This + // has no effect if the worker is already weakly referenced (e.g. if + // this worker was previously idle/unused). + worker.unref(); + } +#endif + // Ask the worker to start executing its pthread entry point function. + worker.postMessage(msg, threadParams.transferList); + return 0; + }, + + _emscripten_init_main_thread_js: (tb) => { + // Pass the thread address to the native code where they stored in wasm + // globals which act as a form of TLS. Global constructors trying + // to access this value will read the wrong value, but that is UB anyway. + __emscripten_thread_init( + tb, + /*is_main=*/!ENVIRONMENT_IS_WORKER, + /*is_runtime=*/1, + /*can_block=*/!ENVIRONMENT_IS_WEB, + /*default_stacksize=*/{{{ DEFAULT_PTHREAD_STACK_SIZE }}}, +#if PTHREADS_PROFILING + /*start_profiling=*/true, +#else + /*start_profiling=*/false, +#endif + ); + PThread.threadInitTLS(); + }, + + $pthreadCreateProxied__internal: true, + $pthreadCreateProxied__proxy: 'sync', + $pthreadCreateProxied__deps: ['__pthread_create_js'], + $pthreadCreateProxied: (pthread_ptr, attr, startRoutine, arg) => ___pthread_create_js(pthread_ptr, attr, startRoutine, arg), + +#if OFFSCREENCANVAS_SUPPORT + // ASan wraps the emscripten_builtin_pthread_create call in + // __lsan::ScopedInterceptorDisabler. Unfortunately, that only disables it on + // the thread that made the call. __pthread_create_js gets proxied to the + // main thread, where LSan is not disabled. This makes it necessary for us to + // disable LSan here (using __noleakcheck), so that it does not detect + // pthread's internal allocations as leaks. If/when we remove all the + // allocations from __pthread_create_js we could also remove this. + __pthread_create_js__noleakcheck: true, +#endif + __pthread_create_js__deps: ['$spawnThread', '$pthreadCreateProxied', + 'emscripten_has_threading_support', +#if OFFSCREENCANVAS_SUPPORT + 'malloc', +#endif + ], + __pthread_create_js: (pthread_ptr, attr, startRoutine, arg) => { + if (!_emscripten_has_threading_support()) { +#if ASSERTIONS + dbg('pthread_create: environment does not support SharedArrayBuffer, pthreads are not available'); +#endif + return {{{ cDefs.EAGAIN }}}; + } +#if PTHREADS_DEBUG + dbg("createThread: " + ptrToString(pthread_ptr)); +#endif + + // List of JS objects that will transfer ownership to the Worker hosting the thread + var transferList = []; + var error = 0; + +#if OFFSCREENCANVAS_SUPPORT + // Deduce which WebGL canvases (HTMLCanvasElements or OffscreenCanvases) should be passed over to the + // Worker that hosts the spawned pthread. + // Comma-delimited list of CSS selectors that must identify canvases by IDs: "#canvas1, #canvas2, ..." + var transferredCanvasNames = attr ? {{{ makeGetValue('attr', C_STRUCTS.pthread_attr_t._a_transferredcanvases, '*') }}} : 0; +#if OFFSCREENCANVASES_TO_PTHREAD + // Proxied canvases string pointer -1/MAX_PTR is used as a special token to + // fetch whatever canvases were passed to build in + // -sOFFSCREENCANVASES_TO_PTHREAD= command line. + if (transferredCanvasNames == {{{ MAX_PTR }}}) { + transferredCanvasNames = '{{{ OFFSCREENCANVASES_TO_PTHREAD }}}'; + } else +#endif + { + transferredCanvasNames = UTF8ToString(transferredCanvasNames).trim(); + } + transferredCanvasNames = transferredCanvasNames ? transferredCanvasNames.split(',') : []; +#if GL_DEBUG + dbg(`pthread_create: transferredCanvasNames="${transferredCanvasNames}"`); +#endif + + var offscreenCanvases = {}; // Dictionary of OffscreenCanvas objects we'll transfer to the created thread to own + var moduleCanvasId = Module['canvas']?.id || ''; + // Note that transferredCanvasNames might be null (so we cannot do a for-of loop). + for (var name of transferredCanvasNames) { + name = name.trim(); + var offscreenCanvasInfo; + try { + if (name == '#canvas') { + if (!Module['canvas']) { + err(`pthread_create: could not find canvas with ID "${name}" to transfer to thread!`); + error = {{{ cDefs.EINVAL }}}; + break; + } + name = Module['canvas'].id; + } +#if ASSERTIONS + assert(typeof GL == 'object', 'OFFSCREENCANVAS_SUPPORT assumes GL is in use (you can force-include it with \'-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$GL\')'); +#endif + if (GL.offscreenCanvases[name]) { + offscreenCanvasInfo = GL.offscreenCanvases[name]; + GL.offscreenCanvases[name] = null; // This thread no longer owns this canvas. + if (Module['canvas'] instanceof OffscreenCanvas && name === Module['canvas'].id) Module['canvas'] = null; + } else if (!ENVIRONMENT_IS_PTHREAD) { + var canvas = (Module['canvas'] && Module['canvas'].id === name) ? Module['canvas'] : document.querySelector(name); + if (!canvas) { + err(`pthread_create: could not find canvas with ID "${name}" to transfer to thread!`); + error = {{{ cDefs.EINVAL }}}; + break; + } + if (canvas.controlTransferredOffscreen) { + err(`pthread_create: cannot transfer canvas with ID "${name}" to thread, since the current thread does not have control over it!`); + error = {{{ cDefs.EPERM }}}; // Operation not permitted, some other thread is accessing the canvas. + break; + } + if (canvas.transferControlToOffscreen) { +#if GL_DEBUG + dbg(`pthread_create: canvas.transferControlToOffscreen(), transferring canvas by name "${name}" (DOM id="${canvas.id}") from main thread to pthread`); +#endif + // Create a shared information block in heap so that we can control + // the canvas size from any thread. + if (!canvas.canvasSharedPtr) { + canvas.canvasSharedPtr = _malloc({{{ 8 + POINTER_SIZE }}}); + {{{ makeSetValue('canvas.canvasSharedPtr', 0, 'canvas.width', 'i32') }}}; + {{{ makeSetValue('canvas.canvasSharedPtr', 4, 'canvas.height', 'i32') }}}; + {{{ makeSetValue('canvas.canvasSharedPtr', 8, 0, '*') }}}; // pthread ptr to the thread that owns this canvas, filled in below. + } + offscreenCanvasInfo = { + offscreenCanvas: canvas.transferControlToOffscreen(), + canvasSharedPtr: canvas.canvasSharedPtr, + id: canvas.id + } + // After calling canvas.transferControlToOffscreen(), it is no + // longer possible to access certain operations on the canvas, such + // as resizing it or obtaining GL contexts via it. + // Use this field to remember that we have permanently converted + // this Canvas to be controlled via an OffscreenCanvas (there is no + // way to undo this in the spec) + canvas.controlTransferredOffscreen = true; + } else { + err(`pthread_create: cannot transfer control of canvas "${name}" to pthread, because current browser does not support OffscreenCanvas!`); + // If building with OFFSCREEN_FRAMEBUFFER=1 mode, we don't need to + // be able to transfer control to offscreen, but WebGL can be + // proxied from worker to main thread. +#if !OFFSCREEN_FRAMEBUFFER + err('pthread_create: Build with -sOFFSCREEN_FRAMEBUFFER to enable fallback proxying of GL commands from pthread to main thread.'); + return {{{ cDefs.ENOSYS }}}; // Function not implemented, browser doesn't have support for this. +#endif + } + } + if (offscreenCanvasInfo) { + transferList.push(offscreenCanvasInfo.offscreenCanvas); + offscreenCanvases[offscreenCanvasInfo.id] = offscreenCanvasInfo; + } + } catch(e) { + err(`pthread_create: failed to transfer control of canvas "${name}" to OffscreenCanvas! Error: ${e}`); + return {{{ cDefs.EINVAL }}}; // Hitting this might indicate an implementation bug or some other internal error + } + } +#endif // OFFSCREENCANVAS_SUPPORT + + // Synchronously proxy the thread creation to main thread if possible. If we + // need to transfer ownership of objects, then proxy asynchronously via + // postMessage. + if (ENVIRONMENT_IS_PTHREAD && (transferList.length === 0 || error)) { + return pthreadCreateProxied(pthread_ptr, attr, startRoutine, arg); + } + + // If on the main thread, and accessing Canvas/OffscreenCanvas failed, abort + // with the detected error. + if (error) return error; + +#if OFFSCREENCANVAS_SUPPORT + // Register for each of the transferred canvases that the new thread now + // owns the OffscreenCanvas. + for (var canvas of Object.values(offscreenCanvases)) { + // pthread ptr to the thread that owns this canvas. + {{{ makeSetValue('canvas.canvasSharedPtr', 8, 'pthread_ptr', '*') }}}; + } +#endif + + var threadParams = { + startRoutine, + pthread_ptr, + arg, +#if OFFSCREENCANVAS_SUPPORT + moduleCanvasId, + offscreenCanvases, +#endif + transferList, + }; + + if (ENVIRONMENT_IS_PTHREAD) { + // The prepopulated pool of web workers that can host pthreads is stored + // in the main JS thread. Therefore if a pthread is attempting to spawn a + // new thread, the thread creation must be deferred to the main JS thread. + threadParams.cmd = 'spawnThread'; + postMessage(threadParams, transferList); + // When we defer thread creation this way, we have no way to detect thread + // creation synchronously today, so we have to assume success and return 0. + return 0; + } + + // We are the main thread, so we have the pthread warmup pool in this + // thread and can fire off JS thread creation directly ourselves. + return spawnThread(threadParams); + }, + +#if (ASSERTIONS || !ALLOW_BLOCKING_ON_MAIN_THREAD) && !MINIMAL_RUNTIME + emscripten_check_blocking_allowed__deps: ['$warnOnce'], +#endif + emscripten_check_blocking_allowed: () => { +#if (ASSERTIONS || !ALLOW_BLOCKING_ON_MAIN_THREAD) && !MINIMAL_RUNTIME +#if ENVIRONMENT_MAY_BE_NODE + if (ENVIRONMENT_IS_NODE) return; +#endif + + if (ENVIRONMENT_IS_WORKER) return; // Blocking in a worker/pthread is fine. + + warnOnce('Blocking on the main thread is very dangerous, see https://emscripten.org/docs/porting/pthreads.html#blocking-on-the-main-browser-thread'); +#if !ALLOW_BLOCKING_ON_MAIN_THREAD + abort('Blocking on the main thread is not allowed by default. See https://emscripten.org/docs/porting/pthreads.html#blocking-on-the-main-browser-thread'); +#endif + +#endif + }, + + // This function is call by a pthread to signal that exit() was called and + // that the entire process should exit. + // This function is always called from a pthread, but is executed on the + // main thread due the __proxy attribute. + $exitOnMainThread__deps: ['exit'], + $exitOnMainThread__proxy: 'async', + $exitOnMainThread: (returnCode) => { +#if PTHREADS_DEBUG + dbg('exitOnMainThread'); +#endif +#if PROXY_TO_PTHREAD + {{{ runtimeKeepalivePop() }}}; +#endif + _exit(returnCode); + }, + +#if MEMORY64 + // Calls proxyToMainThread but returns a bigint rather than a number + $proxyToMainThreadPtr__deps: ['$proxyToMainThread'], + $proxyToMainThreadPtr: (...args) => BigInt(proxyToMainThread(...args)), +#endif + + $proxyToMainThread__deps: ['$stackSave', '$stackRestore', '$stackAlloc', '_emscripten_run_js_on_main_thread'], + $proxyToMainThread__docs: '/** @type{function(number, (number|boolean), ...number)} */', + $proxyToMainThread: (funcIndex, emAsmAddr, sync, ...callArgs) => { + // EM_ASM proxying is done by passing a pointer to the address of the EM_ASM + // content as `emAsmAddr`. JS library proxying is done by passing an index + // into `proxiedJSCallArgs` as `funcIndex`. If `emAsmAddr` is non-zero then + // `funcIndex` will be ignored. + // Additional arguments are passed after the first three are the actual + // function arguments. + // The serialization buffer contains the number of call params, and then + // all the args here. + // We also pass 'sync' to C separately, since C needs to look at it. + // Allocate a buffer, which will be copied by the C code. + // + // First passed parameter specifies the number of arguments to the function. + // When BigInt support is enabled, we must handle types in a more complex + // way, detecting at runtime if a value is a BigInt or not (as we have no + // type info here). To do that, add a "prefix" before each value that + // indicates if it is a BigInt, which effectively doubles the number of + // values we serialize for proxying. TODO: pack this? + var serializedNumCallArgs = callArgs.length {{{ WASM_BIGINT ? "* 2" : "" }}}; + var sp = stackSave(); + var args = stackAlloc(serializedNumCallArgs * 8); + var b = {{{ getHeapOffset('args', 'i64') }}}; + for (var i = 0; i < callArgs.length; i++) { + var arg = callArgs[i]; +#if WASM_BIGINT + if (typeof arg == 'bigint') { + // The prefix is non-zero to indicate a bigint. + HEAP64[b + 2*i] = 1n; + HEAP64[b + 2*i + 1] = arg; + } else { + // The prefix is zero to indicate a JS Number. + HEAP64[b + 2*i] = 0n; + HEAPF64[b + 2*i + 1] = arg; + } +#else + HEAPF64[b + i] = arg; +#endif + } + var rtn = __emscripten_run_js_on_main_thread(funcIndex, emAsmAddr, serializedNumCallArgs, args, sync); + stackRestore(sp); + return rtn; + }, + + // Reuse global JS array to avoid creating JS garbage for each proxied call + $proxiedJSCallArgs: [], + + _emscripten_receive_on_main_thread_js__deps: [ + '$proxyToMainThread', + '$proxiedJSCallArgs'], + _emscripten_receive_on_main_thread_js: (funcIndex, emAsmAddr, callingThread, numCallArgs, args) => { + // Sometimes we need to backproxy events to the calling thread (e.g. + // HTML5 DOM events handlers such as + // emscripten_set_mousemove_callback()), so keep track in a globally + // accessible variable about the thread that initiated the proxying. +#if WASM_BIGINT + numCallArgs /= 2; +#endif + proxiedJSCallArgs.length = numCallArgs; + var b = {{{ getHeapOffset('args', 'i64') }}}; + for (var i = 0; i < numCallArgs; i++) { +#if WASM_BIGINT + if (HEAP64[b + 2*i]) { + // It's a BigInt. + proxiedJSCallArgs[i] = HEAP64[b + 2*i + 1]; + } else { + // It's a Number. + proxiedJSCallArgs[i] = HEAPF64[b + 2*i + 1]; + } +#else + proxiedJSCallArgs[i] = HEAPF64[b + i]; +#endif + } + // Proxied JS library funcs use funcIndex and EM_ASM functions use emAsmAddr +#if HAVE_EM_ASM + var func = emAsmAddr ? ASM_CONSTS[emAsmAddr] : proxiedFunctionTable[funcIndex]; +#else +#if ASSERTIONS + assert(!emAsmAddr); +#endif + var func = proxiedFunctionTable[funcIndex]; +#endif +#if ASSERTIONS + assert(!(funcIndex && emAsmAddr)); + assert(func.length == numCallArgs, 'Call args mismatch in _emscripten_receive_on_main_thread_js'); +#endif + PThread.currentProxiedOperationCallerThread = callingThread; + var rtn = func(...proxiedJSCallArgs); + PThread.currentProxiedOperationCallerThread = 0; +#if MEMORY64 + // In memory64 mode some proxied functions return bigint/pointer but + // our return type is i53/double. + if (typeof rtn == "bigint") { + rtn = bigintToI53Checked(rtn); + } +#endif +#if ASSERTIONS + // Proxied functions can return any type except bigint. All other types + // cooerce to f64/double (the return type of this function in C) but not + // bigint. + assert(typeof rtn != "bigint"); +#endif + return rtn; + }, + + $establishStackSpace__internal: true, + $establishStackSpace__deps: ['$stackRestore', 'emscripten_stack_set_limits'], + $establishStackSpace: function (pthread_ptr) { + var stackHigh = {{{ makeGetValue('pthread_ptr', C_STRUCTS.pthread.stack, '*') }}}; + var stackSize = {{{ makeGetValue('pthread_ptr', C_STRUCTS.pthread.stack_size, '*') }}}; + var stackLow = stackHigh - stackSize; +#if PTHREADS_DEBUG + dbg(`establishStackSpace: ${ptrToString(stackHigh)} -> ${ptrToString(stackLow)}`); +#endif +#if ASSERTIONS + assert(stackHigh != 0); + assert(stackLow != 0); + assert(stackHigh > stackLow, 'stackHigh must be higher then stackLow'); +#endif + // Set stack limits used by `emscripten/stack.h` function. These limits are + // cached in wasm-side globals to make checks as fast as possible. + _emscripten_stack_set_limits(stackHigh, stackLow); + +#if STACK_OVERFLOW_CHECK >= 2 + setStackLimits(); +#endif STACK_OVERFLOW_CHECK + + // Call inside wasm module to set up the stack frame for this pthread in wasm module scope + stackRestore(stackHigh); + +#if STACK_OVERFLOW_CHECK + // Write the stack cookie last, after we have set up the proper bounds and + // current position of the stack. + writeStackCookie(); +#endif + }, + + $invokeEntryPoint__deps: [ + '_emscripten_thread_exit', +#if !MINIMAL_RUNTIME + '$keepRuntimeAlive', + '$runtimeKeepaliveCounter', +#endif + ], +#if ASYNCIFY + $invokeEntryPoint__async: true, +#endif + $invokeEntryPoint: {{{ asyncIf(ASYNCIFY == 2) }}}(ptr, arg) => { +#if PTHREADS_DEBUG + dbg(`invokeEntryPoint: ${ptrToString(ptr)}`); +#endif +#if !MINIMAL_RUNTIME + // An old thread on this worker may have been canceled without returning the + // `runtimeKeepaliveCounter` to zero. Reset it now so the new thread won't + // be affected. + runtimeKeepaliveCounter = 0; + +#if isSymbolNeeded('$noExitRuntime') + // Same for noExitRuntime. The default for pthreads should always be false + // otherwise pthreads would never complete and attempts to pthread_join to + // them would block forever. + // pthreads can still choose to set `noExitRuntime` explicitly, or + // call emscripten_unwind_to_js_event_loop to extend their lifetime beyond + // their main function. See comment in src/runtime_pthread.js for more. + noExitRuntime = 0; +#endif +#endif + +#if MAIN_MODULE + // Before we call the thread entry point, make sure any shared libraries + // have been loaded on this there. Otherwise our table might be not be + // in sync and might not contain the function pointer `ptr` at all. + __emscripten_dlsync_self(); +#endif + // pthread entry points are always of signature 'void *ThreadMain(void *arg)' + // Native codebases sometimes spawn threads with other thread entry point + // signatures, such as void ThreadMain(void *arg), void *ThreadMain(), or + // void ThreadMain(). That is not acceptable per C/C++ specification, but + // x86 compiler ABI extensions enable that to work. If you find the + // following line to crash, either change the signature to "proper" void + // *ThreadMain(void *arg) form, or try linking with the Emscripten linker + // flag -sEMULATE_FUNCTION_POINTER_CASTS to add in emulation for this x86 + // ABI extension. + + var result = {{{ makeDynCall('pp', 'ptr', ASYNCIFY == 2) }}}(arg); + +#if STACK_OVERFLOW_CHECK + checkStackCookie(); +#endif + function finish(result) { +#if !MINIMAL_RUNTIME + // In MINIMAL_RUNTIME the noExitRuntime concept does not apply to + // pthreads. To exit a pthread with live runtime, use the function + // emscripten_unwind_to_js_event_loop() in the pthread body. + if (keepRuntimeAlive()) { + EXITSTATUS = result; + return; + } +#endif + __emscripten_thread_exit(result); + } +#if ASYNCIFY == 2 + result = await result; +#endif + finish(result); + }, + +#if MAIN_MODULE + _emscripten_thread_exit_joinable: (thread) => { + // Called when a thread exits and is joinable. We mark these threads + // as finished, which means that are in state where are no longer actually + // running, but remain around waiting to be joined. In this state they + // cannot run any more proxied work. + if (!ENVIRONMENT_IS_PTHREAD) markAsFinished(thread); + else postMessage({ cmd: 'markAsFinished', thread }); + }, + + $markAsFinished: (pthread_ptr) => { +#if PTHREADS_DEBUG + dbg(`markAsFinished: ${ptrToString(pthread_ptr)}`); +#endif + PThread.finishedThreads.add(pthread_ptr); + if (pthread_ptr in PThread.outstandingPromises) { + PThread.outstandingPromises[pthread_ptr].resolve(); + } + }, + + // Asynchronous version dlsync_threads. Always run on the main thread. + // This work happens asynchronously. The `callback` is called once this work + // is completed, passing the ctx. + // TODO(sbc): Should we make a new form of __proxy attribute for JS library + // function that run asynchronously like but blocks the caller until they are + // done. Perhaps "sync_with_ctx"? + _emscripten_dlsync_threads_async__deps: ['_emscripten_proxy_dlsync_async', '$makePromise'], + _emscripten_dlsync_threads_async: (caller, callback, ctx) => { +#if PTHREADS_DEBUG + dbg("_emscripten_dlsync_threads_async caller=" + ptrToString(caller)); +#endif +#if ASSERTIONS + assert(!ENVIRONMENT_IS_PTHREAD, 'Internal Error! _emscripten_dlsync_threads_async() can only ever be called from main thread'); + assert(Object.keys(PThread.outstandingPromises).length === 0); +#endif + + const promises = []; + + // This first promise resolves once the main thread has loaded all modules. + var info = makePromise(); + promises.push(info.promise); + __emscripten_dlsync_self_async(info.id); + + + // We then create a sequence of promises, one per thread, that resolve once + // each thread has performed its sync using _emscripten_proxy_dlsync. + // Any new threads that are created after this call will automatically be + // in sync because we call `__emscripten_dlsync_self` in + // invokeEntryPoint before the threads entry point is called. + for (const ptr of Object.keys(PThread.pthreads)) { + const pthread_ptr = Number(ptr); + if (pthread_ptr !== caller && !PThread.finishedThreads.has(pthread_ptr)) { + info = makePromise(); + __emscripten_proxy_dlsync_async(pthread_ptr, info.id); + PThread.outstandingPromises[pthread_ptr] = info; + promises.push(info.promise); + } + } + +#if PTHREADS_DEBUG + dbg(`_emscripten_dlsync_threads_async: waiting on ${promises.length} promises`); +#endif + // Once all promises are resolved then we know all threads are in sync and + // we can call the callback. + Promise.all(promises).then(() => { + PThread.outstandingPromises = {}; +#if PTHREADS_DEBUG + dbg('_emscripten_dlsync_threads_async done: calling callback'); +#endif + {{{ makeDynCall('vp', 'callback') }}}(ctx); + }); + }, + + // Synchronous version dlsync_threads. This is only needed for the case then + // the main thread call dlopen and in that case we have not choice but to + // synchronously block the main thread until all other threads are in sync. + // When `dlopen` is called from a worker, the worker itself is blocked but + // the operation its waiting on (on the main thread) can be async. + _emscripten_dlsync_threads__deps: ['_emscripten_proxy_dlsync'], + _emscripten_dlsync_threads: () => { +#if ASSERTIONS + assert(!ENVIRONMENT_IS_PTHREAD, 'Internal Error! _emscripten_dlsync_threads() can only ever be called from main thread'); +#endif + for (const ptr of Object.keys(PThread.pthreads)) { + const pthread_ptr = Number(ptr); + if (!PThread.finishedThreads.has(pthread_ptr)) { + __emscripten_proxy_dlsync(pthread_ptr); + } + } + }, +#elif RELOCATABLE + // Provide a dummy version of _emscripten_thread_exit_joinable when + // RELOCATABLE is used without MAIN_MODULE. This is because the call + // site in pthread_create.c is not able to distinguish between these + // two cases. + _emscripten_thread_exit_joinable: (thread) => {}, +#endif // MAIN_MODULE + + $checkMailbox__deps: ['$callUserCallback', + 'pthread_self', + '_emscripten_check_mailbox', + '_emscripten_thread_mailbox_await'], + $checkMailbox: () => callUserCallback(() => { + // Only check the mailbox if we have a live pthread runtime. We implement + // pthread_self to return 0 if there is no live runtime. + // + // TODO(https://github.com/emscripten-core/emscripten/issues/25076): + // Is this check still needed? `callUserCallback` is supposed to + // ensure the runtime is alive, and if `_pthread_self` is NULL then the + // runtime certainly is *not* alive, so this should be a redundant check. + var pthread_ptr = _pthread_self(); + if (pthread_ptr) { + // If we are using Atomics.waitAsync as our notification mechanism, wait + // for a notification before processing the mailbox to avoid missing any + // work that could otherwise arrive after we've finished processing the + // mailbox and before we're ready for the next notification. + __emscripten_thread_mailbox_await(pthread_ptr); + __emscripten_check_mailbox(); + } + }), + + _emscripten_thread_mailbox_await__deps: ['$checkMailbox'], + _emscripten_thread_mailbox_await: (pthread_ptr) => { + if (Atomics.waitAsync) { + // Wait on the pthread's initial self-pointer field because it is easy and + // safe to access from sending threads that need to notify the waiting + // thread. + // TODO: How to make this work with wasm64? + var wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('pthread_ptr', 'i32') }}}, pthread_ptr); +#if ASSERTIONS + assert(wait.async); +#endif + wait.value.then(checkMailbox); + var waitingAsync = pthread_ptr + {{{ C_STRUCTS.pthread.waiting_async }}}; + Atomics.store(HEAP32, {{{ getHeapOffset('waitingAsync', 'i32') }}}, 1); + } + // If `Atomics.waitAsync` is not implemented, then we will always fall back + // to postMessage and there is no need to do anything here. + }, + + // PostMessage is used to notify threads instead of Atomics.notify whenever + // the environment does not implement Atomics.waitAsync or when messaging a + // new thread that has not had a chance to initialize itself and execute + // Atomics.waitAsync to prepare for the notification. + _emscripten_notify_mailbox_postmessage__deps: ['$checkMailbox'], + _emscripten_notify_mailbox_postmessage: (targetThread, currThreadId) => { + if (targetThread == currThreadId) { + setTimeout(checkMailbox); + } else if (ENVIRONMENT_IS_PTHREAD) { + postMessage({targetThread, cmd: 'checkMailbox'}); + } else { + var worker = PThread.pthreads[targetThread]; + if (!worker) { +#if ASSERTIONS + err(`Cannot send message to thread with ID ${targetThread}, unknown thread ID!`); +#endif + return; + } + worker.postMessage({cmd: 'checkMailbox'}); + } + } +}; + +autoAddDeps(LibraryPThread, '$PThread'); +addToLibrary(LibraryPThread); diff --git a/src/lib/libpthread_stub.js b/src/lib/libpthread_stub.js new file mode 100644 index 0000000000000..44043cd9843b5 --- /dev/null +++ b/src/lib/libpthread_stub.js @@ -0,0 +1,28 @@ +/** + * @license + * Copyright 2015 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +#if PTHREADS +#error "Internal error! PTHREADS should not be enabled when including library_pthread_stub.js." +#endif +#if STANDALONE_WASM && SHARED_MEMORY +#error "STANDALONE_WASM does not support shared memories yet" +#endif + +var LibraryPThreadStub = { + // =================================================================================== + // Stub implementation for pthread.h when not compiling with pthreads support enabled. + // =================================================================================== + + emscripten_is_main_browser_thread: () => +#if MINIMAL_RUNTIME + typeof WorkerGlobalScope == 'undefined' +#else + !ENVIRONMENT_IS_WORKER +#endif + , +}; + +addToLibrary(LibraryPThreadStub); diff --git a/src/lib/libsdl.js b/src/lib/libsdl.js new file mode 100644 index 0000000000000..c810a2c5e0302 --- /dev/null +++ b/src/lib/libsdl.js @@ -0,0 +1,3644 @@ +/** + * @license + * Copyright 2010 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +//"use strict"; + +// See browser tests for examples (test/runner.py, search for sdl_). Run with +// test/runner browser + +// Notes: +// SDL_VIDEORESIZE: This is sent when the canvas is resized. Note that the user +// cannot manually do so, so this is only sent when the +// program manually resizes it (emscripten_set_canvas_element_size +// or otherwise). + +var LibrarySDL = { + $SDL__deps: [ + '$PATH', '$Browser', 'SDL_GetTicks', 'SDL_LockSurface', + '$MainLoop', + // For makeCEvent(). + '$stringToUTF8', + // Many SDL functions depend on malloc/free + 'malloc', 'free', + 'memcpy', + ], + $SDL: { + defaults: { + width: 320, + height: 200, + // If true, SDL_LockSurface will copy the contents of each surface back to + // the Emscripten HEAP so that C code can access it. If false, the surface + // contents are captured only back to JS code. + copyOnLock: true, + // If true, SDL_LockSurface will discard the contents of each surface when + // SDL_LockSurface() is called. This greatly improves performance of + // SDL_LockSurface(). If discardOnLock is true, copyOnLock is ignored. + discardOnLock: false, + // If true, emulate compatibility with desktop SDL by ignoring alpha on + // the screen frontbuffer canvas. Setting this to false will improve + // performance considerably and enables alpha-blending on the frontbuffer, + // so be sure to properly write 0xFF alpha for opaque pixels if you set + // this to false! + opaqueFrontBuffer: true + }, + + version: null, + + surfaces: {}, + // A pool of freed canvas elements. Reusing them avoids GC pauses. + canvasPool: [], + events: [], + fonts: [null], + + // The currently preloaded audio elements ready to be played + audios: [null], + rwops: [null], + // The currently playing audio element. There's only one music track. + music: { + audio: null, + volume: 1.0 + }, + mixerFrequency: 22050, + mixerFormat: {{{ cDefs.AUDIO_S16LSB }}}, + mixerNumChannels: 2, + mixerChunkSize: 1024, + channelMinimumNumber: 0, + + // Set to true if we call SDL_SetVideoMode with SDL_OPENGL, and if so, we do + // not create 2D canvases&contexts for blitting + // Note that images loaded before SDL_SetVideoMode will not get this + // optimization + GL: false, + + // all possible GL attributes, with their default value + glAttributes: { + 0: 3, /* SDL_GL_RED_SIZE */ + 1: 3, /* SDL_GL_GREEN_SIZE */ + 2: 2, /* SDL_GL_BLUE_SIZE */ + 3: 0, /* SDL_GL_ALPHA_SIZE */ + 4: 0, /* SDL_GL_BUFFER_SIZE */ + 5: 1, /* SDL_GL_DOUBLEBUFFER */ + 6: 16, /* SDL_GL_DEPTH_SIZE */ + 7: 0, /* SDL_GL_STENCIL_SIZE */ + 8: 0, /* SDL_GL_ACCUM_RED_SIZE */ + 9: 0, /* SDL_GL_ACCUM_GREEN_SIZE */ + 10: 0, /* SDL_GL_ACCUM_BLUE_SIZE */ + 11: 0, /* SDL_GL_ACCUM_ALPHA_SIZE */ + 12: 0, /* SDL_GL_STEREO */ + 13: 0, /* SDL_GL_MULTISAMPLEBUFFERS */ + 14: 0, /* SDL_GL_MULTISAMPLESAMPLES */ + 15: 1, /* SDL_GL_ACCELERATED_VISUAL */ + 16: 0, /* SDL_GL_RETAINED_BACKING */ + 17: 0, /* SDL_GL_CONTEXT_MAJOR_VERSION */ + 18: 0 /* SDL_GL_CONTEXT_MINOR_VERSION */ + }, + + keyboardState: null, + keyboardMap: {}, + + canRequestFullscreen: false, + isRequestingFullscreen: false, + + textInput: false, + unicode: false, + ttfContext: null, + audio: null, + + startTime: null, + initFlags: 0, // The flags passed to SDL_Init + buttonState: 0, + modState: 0, + DOMButtons: [0, 0, 0], + + DOMEventToSDLEvent: {}, + + TOUCH_DEFAULT_ID: 0, // Our default deviceID for touch events (we get nothing from the browser) + + eventHandler: null, + eventHandlerContext: null, + eventHandlerTemp: 0, + + // DOM code ==> SDL code. See + // https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent + // and SDL_keycode.h + // For keys that don't have unicode value, we map DOM codes with the + // corresponding scan codes + 1024 (using "| 1 << 10") + keyCodes: { + 16: 225 | 1<<10, // shift + 17: 224 | 1<<10, // control (right, or left) + 18: 226 | 1<<10, // alt + 20: 57 | 1<<10, // caps lock + + 33: 75 | 1<<10, // pagedup + 34: 78 | 1<<10, // pagedown + 35: 77 | 1<<10, // end + 36: 74 | 1<<10, // home + 37: 80 | 1<<10, // left arrow + 38: 82 | 1<<10, // up arrow + 39: 79 | 1<<10, // right arrow + 40: 81 | 1<<10, // down arrow + 44: 316, // print screen + 45: 73 | 1<<10, // insert + 46: 127, // SDLK_DEL == '\177' + + 91: 227 | 1<<10, // windows key or super key on linux (doesn't work on Mac) + 93: 101 | 1<<10, // application + + 96: 98 | 1<<10, // keypad 0 + 97: 89 | 1<<10, // keypad 1 + 98: 90 | 1<<10, // keypad 2 + 99: 91 | 1<<10, // keypad 3 + 100: 92 | 1<<10, // keypad 4 + 101: 93 | 1<<10, // keypad 5 + 102: 94 | 1<<10, // keypad 6 + 103: 95 | 1<<10, // keypad 7 + 104: 96 | 1<<10, // keypad 8 + 105: 97 | 1<<10, // keypad 9 + 106: 85 | 1<<10, // keypad multiply + 107: 87 | 1<<10, // keypad plus + 109: 86 | 1<<10, // keypad minus + 110: 99 | 1<<10, // keypad decimal point + 111: 84 | 1<<10, // keypad divide + 112: 58 | 1<<10, // F1 + 113: 59 | 1<<10, // F2 + 114: 60 | 1<<10, // F3 + 115: 61 | 1<<10, // F4 + 116: 62 | 1<<10, // F5 + 117: 63 | 1<<10, // F6 + 118: 64 | 1<<10, // F7 + 119: 65 | 1<<10, // F8 + 120: 66 | 1<<10, // F9 + 121: 67 | 1<<10, // F10 + 122: 68 | 1<<10, // F11 + 123: 69 | 1<<10, // F12 + 124: 104 | 1<<10, // F13 + 125: 105 | 1<<10, // F14 + 126: 106 | 1<<10, // F15 + 127: 107 | 1<<10, // F16 + 128: 108 | 1<<10, // F17 + 129: 109 | 1<<10, // F18 + 130: 110 | 1<<10, // F19 + 131: 111 | 1<<10, // F20 + 132: 112 | 1<<10, // F21 + 133: 113 | 1<<10, // F22 + 134: 114 | 1<<10, // F23 + 135: 115 | 1<<10, // F24 + + 144: 83 | 1<<10, // keypad num lock + + 160: 94, // caret + 161: 33, // exclaim + 162: 34, // double quote + 163: 35, // hash + 164: 36, // dollar + 165: 37, // percent + 166: 38, // ampersand + 167: 95, // underscore + 168: 40, // open parenthesis + 169: 41, // close parenthesis + 170: 42, // asterix + 171: 43, // plus + 172: 124, // pipe + 173: 45, // minus + 174: 123, // open curly bracket + 175: 125, // close curly bracket + 176: 126, // tilde + + 181: 127, // audio mute + 182: 129, // audio volume down + 183: 128, // audio volume up + + 188: 44, // comma + 190: 46, // period + 191: 47, // slash (/) + 192: 96, // backtick/backquote (`) + 219: 91, // open square bracket + 220: 92, // back slash + 221: 93, // close square bracket + 222: 39, // quote + 224: 227 | 1<<10, // meta (command/windows) + }, + + scanCodes: { // SDL keycode ==> SDL scancode. See SDL_scancode.h + 8: 42, // backspace + 9: 43, // tab + 13: 40, // return + 27: 41, // escape + 32: 44, // space + 35: 204, // hash + + 39: 53, // grave + + 44: 54, // comma + 46: 55, // period + 47: 56, // slash + 48: 39, // 0 + 49: 30, // 1 + 50: 31, // 2 + 51: 32, // 3 + 52: 33, // 4 + 53: 34, // 5 + 54: 35, // 6 + 55: 36, // 7 + 56: 37, // 8 + 57: 38, // 9 + 58: 203, // colon + 59: 51, // semicolon + + 61: 46, // equals + + 91: 47, // left bracket + 92: 49, // backslash + 93: 48, // right bracket + + 96: 52, // apostrophe + 97: 4, // A + 98: 5, // B + 99: 6, // C + 100: 7, // D + 101: 8, // E + 102: 9, // F + 103: 10, // G + 104: 11, // H + 105: 12, // I + 106: 13, // J + 107: 14, // K + 108: 15, // L + 109: 16, // M + 110: 17, // N + 111: 18, // O + 112: 19, // P + 113: 20, // Q + 114: 21, // R + 115: 22, // S + 116: 23, // T + 117: 24, // U + 118: 25, // V + 119: 26, // W + 120: 27, // X + 121: 28, // Y + 122: 29, // Z + + 127: 76, // delete + + 305: 224, // ctrl + + 308: 226, // alt + + 316: 70, // print screen + }, + + loadRect(rect) { + return { + x: {{{ makeGetValue('rect', C_STRUCTS.SDL_Rect.x, 'i32') }}}, + y: {{{ makeGetValue('rect', C_STRUCTS.SDL_Rect.y, 'i32') }}}, + w: {{{ makeGetValue('rect', C_STRUCTS.SDL_Rect.w, 'i32') }}}, + h: {{{ makeGetValue('rect', C_STRUCTS.SDL_Rect.h, 'i32') }}} + }; + }, + + updateRect(rect, r) { + {{{ makeSetValue('rect', C_STRUCTS.SDL_Rect.x, 'r.x', 'i32') }}}; + {{{ makeSetValue('rect', C_STRUCTS.SDL_Rect.y, 'r.y', 'i32') }}}; + {{{ makeSetValue('rect', C_STRUCTS.SDL_Rect.w, 'r.w', 'i32') }}}; + {{{ makeSetValue('rect', C_STRUCTS.SDL_Rect.h, 'r.h', 'i32') }}}; + }, + + 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) { +#if ASSERTIONS + // Canvas screens are always RGBA. + var format = {{{ makeGetValue('fmt', C_STRUCTS.SDL_PixelFormat.format, 'i32') }}}; + if (format != {{{ cDefs.SDL_PIXELFORMAT_RGBA8888 }}}) { + warnOnce('Unsupported pixel format!'); + } +#endif + }, + + // Load SDL color into a CSS-style color specification + loadColorToCSSRGB(color) { + var rgba = {{{ makeGetValue('color', 0, 'i32') }}}; + return 'rgb(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ')'; + }, + loadColorToCSSRGBA(color) { + var rgba = {{{ makeGetValue('color', 0, 'i32') }}}; + 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 & {{{ cDefs.SDL_HWSURFACE }}}; + var is_SDL_HWPALETTE = flags & {{{ cDefs.SDL_HWPALETTE }}}; + var is_SDL_OPENGL = flags & {{{ cDefs.SDL_OPENGL }}}; + + var surf = _malloc({{{ C_STRUCTS.SDL_Surface.__size__ }}}); + var pixelFormat = _malloc({{{ C_STRUCTS.SDL_PixelFormat.__size__ }}}); + // 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); + } + + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.flags, 'flags', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.format, 'pixelFormat', '*') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.w, 'width', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.h, 'height', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}}; // assuming RGBA or indexed for now, + // since that is what ImageData gives us in browsers + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', '*') }}}; + + var canvas = Browser.getCanvas(); + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.x, '0', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.y, '0', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.w, 'canvas.width', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.h, 'canvas.height', 'i32') }}}; + + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}}; + + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefs.SDL_PIXELFORMAT_RGBA8888, 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.palette, '0', 'i32') }}};// TODO + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BitsPerPixel, 'bpp * 8', 'i8') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BytesPerPixel, 'bpp', 'i8') }}}; + + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Rmask, 'rmask || 0x000000ff', 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Gmask, 'gmask || 0x0000ff00', 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Bmask, 'bmask || 0x00ff0000', 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Amask, 'amask || 0xff000000', 'i32') }}}; + + // 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[{{{ cDefs.SDL_GL_MULTISAMPLEBUFFERS }}}] != 0) && (SDL.glAttributes[{{{ cDefs.SDL_GL_MULTISAMPLESAMPLES }}}] > 1)), + depth: (SDL.glAttributes[{{{ cDefs.SDL_GL_DEPTH_SIZE }}}] > 0), + stencil: (SDL.glAttributes[{{{ cDefs.SDL_GL_STENCIL_SIZE }}}] > 0), + alpha: (SDL.glAttributes[{{{ cDefs.SDL_GL_ALPHA_SIZE }}}] > 0) + }; + +#if OFFSCREEN_FRAMEBUFFER + // TODO: Make SDL explicitly aware of whether it is being proxied or not, + // and set these to true only when proxying is being performed. + GL.enableOffscreenFramebufferAttributes(webGLContextAttributes); +#endif + 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; + }, + + // Copy data from the C++-accessible storage to the canvas backing + // for surface with HWPALETTE flag(8bpp depth) + 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[{{{ makeGetValue('buffer', 'base + x', 'u8') }}}]; + } + } + }, + + freeSurface(surf) { + var refcountPointer = surf + {{{ C_STRUCTS.SDL_Surface.refcount }}}; + var refcount = {{{ makeGetValue('refcountPointer', 0, 'i32') }}}; + if (refcount > 1) { + {{{ makeSetValue('refcountPointer', 0, 'refcount - 1', 'i32') }}}; + 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; + }, + + // the browser sends out touchstart events with the whole group of touches + // even if we received a previous touchstart for a specific touch identifier. + // You can test this by pressing one finger to the screen, then another. You'll + // receive two touchstart events, the first with a touches count of 1 the second + // with a touches count of two. + // SDL sends out a new touchstart event for only each newly started touch so to + // emulate this, we keep track of previously started touches. + 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 ? {{{ cDefs.SDL_BUTTON_WHEELUP }}} : {{{ cDefs.SDL_BUTTON_WHEELDOWN }}}) - 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: {{{ cDefs.SDL_TOUCH_MOUSEID }}}, + 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: {{{ cDefs.SDL_TOUCH_MOUSEID }}}, + 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: {{{ cDefs.SDL_TOUCH_MOUSEID }}}, + 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 RUNTIME_DEBUG + if (!code) dbg('unmapped keyCode: ', event.keyCode); +#endif + // 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} */ + {{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}}; + // TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED + SDL.modState = + ({{{ makeGetValue('SDL.keyboardState', cDefs.SDLK_LCTRL, 'i8') }}} ? {{{ cDefs.KMOD_LCTRL }}} : 0) | + ({{{ makeGetValue('SDL.keyboardState', cDefs.SDLK_LSHIFT, 'i8') }}} ? {{{ cDefs.KMOD_LSHIFT }}} : 0) | + ({{{ makeGetValue('SDL.keyboardState', cDefs.SDLK_LALT, 'i8') }}} ? {{{ cDefs.KMOD_LALT }}} : 0) | + ({{{ makeGetValue('SDL.keyboardState', cDefs.SDLK_RCTRL, 'i8') }}} ? {{{ cDefs.KMOD_RCTRL }}} : 0) | + ({{{ makeGetValue('SDL.keyboardState', cDefs.SDLK_RSHIFT, 'i8') }}} ? {{{ cDefs.KMOD_RSHIFT }}} : 0) | + ({{{ makeGetValue('SDL.keyboardState', cDefs.SDLK_RALT, 'i8') }}} ? {{{ cDefs.KMOD_RALT }}} : 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)) { + {{{ makeDynCall('ipp', 'SDL.eventHandler') }}}(SDL.eventHandlerContext, SDL.eventHandlerTemp); + } + }, + + pollEvent(ptr) { + if (SDL.initFlags & {{{ cDefs.SDL_INIT_JOYSTICK }}} && 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; + }, + + // returns false if the event was determined to be irrelevant + 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, {{{ C_STRUCTS.SDL_KeyboardEvent.__size__ }}}); + _free(event); // the copy is no longer needed + return; + } + + SDL.handleEvent(event); + + switch (event.type) { + case 'keydown': case 'keyup': { + var down = event.type === 'keydown'; +#if RUNTIME_DEBUG + dbg(`received ${event.type} event: keyCode=${event.keyCode}, key=${event.key}, code=${event.code}`); +#endif + 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; + } + + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.state, 'down ? 1 : 0', 'i8') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.repeat, '0', 'i8') }}}; // TODO + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.scancode, 'scan', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.sym, 'key', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.mod, 'SDL.modState', 'i16') }}}; + // some non-character keys (e.g. backspace and tab) won't have keypressCharCode set, fill in with the keyCode. + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.unicode, 'event.keypressCharCode || key', 'i32') }}}; + + break; + } + case 'keypress': { + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TextInputEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + // Not filling in windowID for now + stringToUTF8(String.fromCharCode(event.charCode), ptr + {{{ C_STRUCTS.SDL_TextInputEvent.text }}}, 4); + break; + } + case 'mousedown': case 'mouseup': case 'mousemove': { + if (event.type != 'mousemove') { + var down = event.type === 'mousedown'; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.timestamp, '0', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.windowID, '0', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.which, '0', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.button, 'event.button+1', 'i8') }}}; // DOM buttons are 0-2, SDL 1-3 + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.state, 'down ? 1 : 0', 'i8') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.x, 'Browser.mouseX', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.y, 'Browser.mouseY', 'i32') }}}; + } else { + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.timestamp, '0', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.windowID, '0', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.which, '0', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.state, 'SDL.buttonState', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.x, 'Browser.mouseX', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.y, 'Browser.mouseY', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.xrel, 'Browser.mouseMovementX', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.yrel, 'Browser.mouseMovementY', 'i32') }}}; + } + break; + } + case 'wheel': { + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseWheelEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseWheelEvent.x, 'event.deltaX', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseWheelEvent.y, 'event.deltaY', 'i32') }}}; + 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 + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.timestamp, '_SDL_GetTicks()', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.touchId, 'touch.deviceID', 'i64') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.fingerId, 'touch.identifier', 'i64') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.x, 'x', 'float') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.y, 'y', 'float') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.dx, 'dx', 'float') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.dy, 'dy', 'float') }}}; + if (touch.force !== undefined) { + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.pressure, 'touch.force', 'float') }}}; + } else { // No pressure data, send a digital 0/1 pressure. + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.pressure, 'event.type == "touchend" ? 0 : 1', 'float') }}}; + } + break; + } + case 'unload': { + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + break; + } + case 'resize': { + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_ResizeEvent.w, 'event.w', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_ResizeEvent.h, 'event.h', 'i32') }}}; + break; + } + case 'joystick_button_up': case 'joystick_button_down': { + var state = event.type === 'joystick_button_up' ? 0 : 1; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.which, 'event.index', 'i8') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.button, 'event.button', 'i8') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.state, 'state', 'i8') }}}; + break; + } + case 'joystick_axis_motion': { + {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.which, 'event.index', 'i8') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.axis, 'event.axis', 'i8') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.value, 'SDL.joystickAxisValueConversion(event.value)', 'i32') }}}; + break; + } + case 'focus': { + {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.windowID, '0', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.event, cDefs.SDL_WINDOWEVENT_FOCUS_GAINED, 'i8') }}}; + break; + } + case 'blur': { + {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.windowID, '0', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.event, cDefs.SDL_WINDOWEVENT_FOCUS_LOST, 'i8') }}}; + break; + } + case 'visibilitychange': { + var visibilityEventID = event.visible ? {{{ cDefs.SDL_WINDOWEVENT_SHOWN }}} : {{{ cDefs.SDL_WINDOWEVENT_HIDDEN }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.windowID, 0, 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.event, 'visibilityEventID' , 'i8') }}}; + 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; +#if ASSERTIONS + assert(tempCtx, 'TTF_Init must have been called'); +#endif + tempCtx.font = fontString; + var ret = tempCtx.measureText(text).width | 0; + return ret; + }, + + // Sound + + // Channels are a SDL abstraction for allowing multiple sound tracks to be + // played at the same time. We don't need to actually implement the mixing + // since the browser engine handles that for us. Therefore, in JS we just + // maintain a list of channels and return IDs for them to the SDL consumer. + 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