diff --git a/.github/actions/build-jtreg/action.yml b/.github/actions/build-jtreg/action.yml new file mode 100644 index 0000000000000..3abfa260c1767 --- /dev/null +++ b/.github/actions/build-jtreg/action.yml @@ -0,0 +1,68 @@ +# +# Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +name: 'Build JTReg' +description: 'Build JTReg' + +runs: + using: composite + steps: + - name: 'Get JTReg version configuration' + id: version + uses: ./.github/actions/config + with: + var: JTREG_VERSION + + - name: 'Check cache for already built JTReg' + id: get-cached + uses: actions/cache@v4 + with: + path: jtreg/installed + key: jtreg-${{ steps.version.outputs.value }} + + - name: 'Checkout the JTReg source' + uses: actions/checkout@v4 + with: + repository: openjdk/jtreg + ref: jtreg-${{ steps.version.outputs.value }} + path: jtreg/src + if: (steps.get-cached.outputs.cache-hit != 'true') + + - name: 'Build JTReg' + run: | + # Build JTReg and move files to the proper locations + bash make/build.sh --jdk "$JAVA_HOME_17_X64" + mkdir ../installed + mv build/images/jtreg/* ../installed + working-directory: jtreg/src + shell: bash + if: (steps.get-cached.outputs.cache-hit != 'true') + + - name: 'Upload JTReg artifact' + uses: actions/upload-artifact@v4 + with: + name: bundles-jtreg-${{ steps.version.outputs.value }} + path: jtreg/installed + retention-days: 1 diff --git a/.github/actions/get-gtest/action.yml b/.github/actions/get-gtest/action.yml index 1df1052285d2a..5de2b10cd3209 100644 --- a/.github/actions/get-gtest/action.yml +++ b/.github/actions/get-gtest/action.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ runs: uses: actions/checkout@v4 with: repository: google/googletest - ref: 'release-${{ steps.version.outputs.value }}' + ref: 'v${{ steps.version.outputs.value }}' path: gtest - name: 'Export path to where GTest is installed' diff --git a/.github/actions/get-jtreg/action.yml b/.github/actions/get-jtreg/action.yml index ab0927919dbcc..78a3a4c9edddd 100644 --- a/.github/actions/get-jtreg/action.yml +++ b/.github/actions/get-jtreg/action.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ # name: 'Get JTReg' -description: 'Download JTReg from cache or source location' +description: 'Get JTReg' outputs: path: description: 'Path to the installed JTReg' @@ -39,30 +39,12 @@ runs: with: var: JTREG_VERSION - - name: 'Check cache for JTReg' - id: get-cached-jtreg - uses: actions/cache@v4 + - name: 'Download JTReg artifact' + id: download-jtreg + uses: actions/download-artifact@v4 with: + name: bundles-jtreg-${{ steps.version.outputs.value }} path: jtreg/installed - key: jtreg-${{ steps.version.outputs.value }} - - - name: 'Checkout the JTReg source' - uses: actions/checkout@v4 - with: - repository: openjdk/jtreg - ref: jtreg-${{ steps.version.outputs.value }} - path: jtreg/src - if: steps.get-cached-jtreg.outputs.cache-hit != 'true' - - - name: 'Build JTReg' - run: | - # Build JTReg and move files to the proper locations - bash make/build.sh --jdk "$JAVA_HOME_11_X64" - mkdir ../installed - mv build/images/jtreg/* ../installed - working-directory: jtreg/src - shell: bash - if: steps.get-cached-jtreg.outputs.cache-hit != 'true' - name: 'Export path to where JTReg is installed' id: path-name diff --git a/.github/scripts/gen-build-failure-report.sh b/.github/scripts/gen-build-failure-report.sh index fd3215fc7fe2d..2dda69a3f331b 100644 --- a/.github/scripts/gen-build-failure-report.sh +++ b/.github/scripts/gen-build-failure-report.sh @@ -24,12 +24,19 @@ # questions. # +# Import common utils +. .github/scripts/report-utils.sh + GITHUB_STEP_SUMMARY="$1" BUILD_DIR="$(ls -d build/*)" # Send signal to the do-build action that we failed touch "$BUILD_DIR/build-failure" +# Collect hs_errs for build-time crashes, e.g. javac, jmod, jlink, CDS. +# These usually land in make/ +hs_err_files=$(ls make/hs_err*.log 2> /dev/null || true) + ( echo '### :boom: Build failure summary' echo '' @@ -46,6 +53,20 @@ touch "$BUILD_DIR/build-failure" echo '' echo '' + for hs_err in $hs_err_files; do + echo "
View HotSpot error log: "$hs_err"" + echo '' + echo '```' + echo "$hs_err:" + echo '' + cat "$hs_err" + echo '```' + echo '
' + echo '' + done + echo '' echo ':arrow_right: To see the entire test log, click the job in the list to the left. To download logs, see the `failure-logs` [artifact above](#artifacts).' ) >> $GITHUB_STEP_SUMMARY + +truncate_summary diff --git a/.github/scripts/gen-test-results.sh b/.github/scripts/gen-test-results.sh index 9e85eef4dc08d..bdf3eb3b9cbc5 100644 --- a/.github/scripts/gen-test-results.sh +++ b/.github/scripts/gen-test-results.sh @@ -24,6 +24,9 @@ # questions. # +# Import common utils +. .github/scripts/report-utils.sh + GITHUB_STEP_SUMMARY="$1" test_suite_name=$(cat build/run-test-prebuilt/test-support/test-last-ids.txt) @@ -89,18 +92,6 @@ for test in $failures $errors; do fi done >> $GITHUB_STEP_SUMMARY -# With many failures, the summary can easily exceed 1024 kB, the limit set by Github -# Trim it down if so. -summary_size=$(wc -c < $GITHUB_STEP_SUMMARY) -if [[ $summary_size -gt 1000000 ]]; then - # Trim to below 1024 kB, and cut off after the last detail group - head -c 1000000 $GITHUB_STEP_SUMMARY | tac | sed -n -e '/<\/details>/,$ p' | tac > $GITHUB_STEP_SUMMARY.tmp - mv $GITHUB_STEP_SUMMARY.tmp $GITHUB_STEP_SUMMARY - ( - echo '' - echo ':x: **WARNING: Summary is too large and has been truncated.**' - echo '' - ) >> $GITHUB_STEP_SUMMARY -fi - echo ':arrow_right: To see the entire test log, click the job in the list to the left.' >> $GITHUB_STEP_SUMMARY + +truncate_summary diff --git a/.github/scripts/report-utils.sh b/.github/scripts/report-utils.sh new file mode 100644 index 0000000000000..da5b6c04b3cbe --- /dev/null +++ b/.github/scripts/report-utils.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# +# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +function truncate_summary() { + # With large hs_errs, the summary can easily exceed 1024 kB, the limit set by Github + # Trim it down if so. + summary_size=$(wc -c < $GITHUB_STEP_SUMMARY) + if [[ $summary_size -gt 1000000 ]]; then + # Trim to below 1024 kB, and cut off after the last detail group + head -c 1000000 $GITHUB_STEP_SUMMARY | tac | sed -n -e '/<\/details>/,$ p' | tac > $GITHUB_STEP_SUMMARY.tmp + mv $GITHUB_STEP_SUMMARY.tmp $GITHUB_STEP_SUMMARY + ( + echo '' + echo ':x: **WARNING: Summary is too large and has been truncated.**' + echo '' + ) >> $GITHUB_STEP_SUMMARY + fi +} diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index 3afb910a3d888..e79c408b6567b 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -131,6 +131,7 @@ jobs: id: create-sysroot run: > sudo debootstrap + --no-merged-usr --arch=${{ matrix.debian-arch }} --verbose --include=fakeroot,symlinks,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype-dev,libpng-dev @@ -151,6 +152,9 @@ jobs: rm -rf sysroot/usr/{sbin,bin,share} rm -rf sysroot/usr/lib/{apt,gcc,udev,systemd} rm -rf sysroot/usr/libexec/gcc + # /{bin,sbin,lib}/ are not symbolic links to /usr/{bin,sbin,lib}/ when debootstrap with --no-merged-usr + rm -rf sysroot/{sbin,bin} + rm -rf sysroot/lib/{udev,systemd} if: steps.create-sysroot.outcome == 'success' && steps.get-cached-sysroot.outputs.cache-hit != 'true' - name: 'Remove broken sysroot' diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index b03fcb144fdba..c3d499a32a08e 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,9 @@ on: platform: required: true type: string + runs-on: + required: true + type: string extra-conf-options: required: false type: string @@ -55,7 +58,7 @@ on: jobs: build-macos: name: build - runs-on: macos-11 + runs-on: ${{ inputs.runs-on }} strategy: fail-fast: false @@ -74,7 +77,7 @@ jobs: id: bootjdk uses: ./.github/actions/get-bootjdk with: - platform: macos-x64 + platform: ${{ inputs.platform }} - name: 'Get JTReg' id: jtreg @@ -87,7 +90,7 @@ jobs: - name: 'Install toolchain and dependencies' run: | # Run Homebrew installation and xcode-select - brew install make + brew install autoconf make sudo xcode-select --switch /Applications/Xcode_${{ inputs.xcode-toolset-version }}.app/Contents/Developer # This will make GNU make available as 'make' and not only as 'gmake' echo '/usr/local/opt/make/libexec/gnubin' >> $GITHUB_PATH diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b238306271e2b..164b5cf6987bf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ on: platforms: description: 'Platform(s) to execute on (comma separated, e.g. "linux-x64, macos, aarch64")' required: true - default: 'linux-x64, linux-x86, linux-x64-variants, linux-cross-compile, macos-x64, macos-aarch64, windows-x64, windows-aarch64' + default: 'linux-x64, linux-x86-hs, linux-x64-variants, linux-cross-compile, macos-x64, macos-aarch64, windows-x64, windows-aarch64' configure-arguments: description: 'Additional configure arguments' required: false @@ -53,12 +53,12 @@ jobs: ### Determine platforms to include ### - select: - name: 'Select platforms' + prepare: + name: 'Prepare the run' runs-on: ubuntu-22.04 outputs: linux-x64: ${{ steps.include.outputs.linux-x64 }} - linux-x86: ${{ steps.include.outputs.linux-x86 }} + linux-x86-hs: ${{ steps.include.outputs.linux-x86-hs }} linux-x64-variants: ${{ steps.include.outputs.linux-x64-variants }} linux-cross-compile: ${{ steps.include.outputs.linux-cross-compile }} macos-x64: ${{ steps.include.outputs.macos-x64 }} @@ -67,7 +67,19 @@ jobs: windows-aarch64: ${{ steps.include.outputs.windows-aarch64 }} steps: - # This function must be inlined in main.yml, or we'd be forced to checkout the repo + - name: 'Checkout the scripts' + uses: actions/checkout@v4 + with: + sparse-checkout: | + .github + make/conf/github-actions.conf + + - name: 'Build JTReg' + id: jtreg + uses: ./.github/actions/build-jtreg + + # TODO: Now that we are checking out the repo scripts, we can put the following code + # into a separate file - name: 'Check what jobs to run' id: include run: | @@ -109,7 +121,7 @@ jobs: } echo "linux-x64=$(check_platform linux-x64 linux x64)" >> $GITHUB_OUTPUT - echo "linux-x86=$(check_platform linux-x86 linux x86)" >> $GITHUB_OUTPUT + echo "linux-x86-hs=$(check_platform linux-x86-hs linux x86)" >> $GITHUB_OUTPUT echo "linux-x64-variants=$(check_platform linux-x64-variants variants)" >> $GITHUB_OUTPUT echo "linux-cross-compile=$(check_platform linux-cross-compile cross-compile)" >> $GITHUB_OUTPUT echo "macos-x64=$(check_platform macos-x64 macos x64)" >> $GITHUB_OUTPUT @@ -123,21 +135,22 @@ jobs: build-linux-x64: name: linux-x64 - needs: select + needs: prepare uses: ./.github/workflows/build-linux.yml with: platform: linux-x64 gcc-major-version: '10' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.linux-x64 == 'true' + if: needs.prepare.outputs.linux-x64 == 'true' - build-linux-x86: - name: linux-x86 - needs: select + build-linux-x86-hs: + name: linux-x86-hs + needs: prepare uses: ./.github/workflows/build-linux.yml with: platform: linux-x86 + make-target: 'hotspot' gcc-major-version: '10' gcc-package-suffix: '-multilib' apt-architecture: 'i386' @@ -147,11 +160,11 @@ jobs: extra-conf-options: '--with-target-bits=32' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.linux-x86 == 'true' + if: needs.prepare.outputs.linux-x86-hs == 'true' build-linux-x64-hs-nopch: name: linux-x64-hs-nopch - needs: select + needs: prepare uses: ./.github/workflows/build-linux.yml with: platform: linux-x64 @@ -161,11 +174,11 @@ jobs: extra-conf-options: '--disable-precompiled-headers' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.linux-x64-variants == 'true' + if: needs.prepare.outputs.linux-x64-variants == 'true' build-linux-x64-hs-zero: name: linux-x64-hs-zero - needs: select + needs: prepare uses: ./.github/workflows/build-linux.yml with: platform: linux-x64 @@ -175,11 +188,11 @@ jobs: extra-conf-options: '--with-jvm-variants=zero --disable-precompiled-headers' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.linux-x64-variants == 'true' + if: needs.prepare.outputs.linux-x64-variants == 'true' build-linux-x64-hs-minimal: name: linux-x64-hs-minimal - needs: select + needs: prepare uses: ./.github/workflows/build-linux.yml with: platform: linux-x64 @@ -189,11 +202,11 @@ jobs: extra-conf-options: '--with-jvm-variants=minimal --disable-precompiled-headers' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.linux-x64-variants == 'true' + if: needs.prepare.outputs.linux-x64-variants == 'true' build-linux-x64-hs-optimized: name: linux-x64-hs-optimized - needs: select + needs: prepare uses: ./.github/workflows/build-linux.yml with: platform: linux-x64 @@ -204,45 +217,45 @@ jobs: extra-conf-options: '--with-debug-level=optimized --disable-precompiled-headers' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.linux-x64-variants == 'true' + if: needs.prepare.outputs.linux-x64-variants == 'true' build-linux-cross-compile: name: linux-cross-compile - needs: - - select + needs: prepare uses: ./.github/workflows/build-cross-compile.yml with: gcc-major-version: '10' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.linux-cross-compile == 'true' + if: needs.prepare.outputs.linux-cross-compile == 'true' build-macos-x64: name: macos-x64 - needs: select + needs: prepare uses: ./.github/workflows/build-macos.yml with: platform: macos-x64 - xcode-toolset-version: '12.5.1' + runs-on: 'macos-13' + xcode-toolset-version: '14.3.1' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.macos-x64 == 'true' + if: needs.prepare.outputs.macos-x64 == 'true' build-macos-aarch64: name: macos-aarch64 - needs: select + needs: prepare uses: ./.github/workflows/build-macos.yml with: platform: macos-aarch64 - xcode-toolset-version: '12.5.1' - extra-conf-options: '--openjdk-target=aarch64-apple-darwin' + runs-on: 'macos-14' + xcode-toolset-version: '15.4' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.macos-aarch64 == 'true' + if: needs.prepare.outputs.macos-aarch64 == 'true' build-windows-x64: name: windows-x64 - needs: select + needs: prepare uses: ./.github/workflows/build-windows.yml with: platform: windows-x64 @@ -250,11 +263,11 @@ jobs: msvc-toolset-architecture: 'x86.x64' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.windows-x64 == 'true' + if: needs.prepare.outputs.windows-x64 == 'true' build-windows-aarch64: name: windows-aarch64 - needs: select + needs: prepare uses: ./.github/workflows/build-windows.yml with: platform: windows-aarch64 @@ -264,7 +277,7 @@ jobs: extra-conf-options: '--openjdk-target=aarch64-unknown-cygwin' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.windows-aarch64 == 'true' + if: needs.prepare.outputs.windows-aarch64 == 'true' ### ### Test jobs @@ -308,7 +321,19 @@ jobs: with: platform: macos-x64 bootjdk-platform: macos-x64 - runs-on: macos-11 + runs-on: macos-13 + xcode-toolset-version: '14.3.1' + + test-macos-aarch64: + name: macos-aarch64 + needs: + - build-macos-aarch64 + uses: ./.github/workflows/test.yml + with: + platform: macos-aarch64 + bootjdk-platform: macos-aarch64 + runs-on: macos-14 + xcode-toolset-version: '15.4' test-windows-x64: name: windows-x64 @@ -327,7 +352,7 @@ jobs: if: always() needs: - build-linux-x64 - - build-linux-x86 + - build-linux-x86-hs - build-linux-x64-hs-nopch - build-linux-x64-hs-zero - build-linux-x64-hs-minimal @@ -341,29 +366,27 @@ jobs: - test-linux-x64-fips - test-linux-x86 - test-macos-x64 + - test-macos-aarch64 - test-windows-x64 steps: - # Hack to get hold of the api environment variables that are only defined for actions - - name: 'Get API configuration' - id: api - uses: actions/github-script@v7 - with: - script: 'return { url: process.env["ACTIONS_RUNTIME_URL"], token: process.env["ACTIONS_RUNTIME_TOKEN"] }' - - name: 'Remove bundle artifacts' run: | # Find and remove all bundle artifacts - ALL_ARTIFACT_URLS="$(curl -s \ - -H 'Accept: application/json;api-version=6.0-preview' \ - -H 'Authorization: Bearer ${{ fromJson(steps.api.outputs.result).token }}' \ - '${{ fromJson(steps.api.outputs.result).url }}_apis/pipelines/workflows/${{ github.run_id }}/artifacts?api-version=6.0-preview')" - BUNDLE_ARTIFACT_URLS="$(echo "$ALL_ARTIFACT_URLS" | jq -r -c '.value | map(select(.name|startswith("bundles-"))) | .[].url')" - for url in $BUNDLE_ARTIFACT_URLS; do - echo "Removing $url" - curl -s \ - -H 'Accept: application/json;api-version=6.0-preview' \ - -H 'Authorization: Bearer ${{ fromJson(steps.api.outputs.result).token }}' \ - -X DELETE "$url" \ + # See: https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28 + ALL_ARTIFACT_IDS="$(curl -sL \ + -H 'Accept: application/vnd.github+json' \ + -H 'Authorization: Bearer ${{ github.token }}' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + '${{ github.api_url }}/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts?per_page=100')" + BUNDLE_ARTIFACT_IDS="$(echo "$ALL_ARTIFACT_IDS" | jq -r -c '.artifacts | map(select(.name|startswith("bundles-"))) | .[].id')" + for id in $BUNDLE_ARTIFACT_IDS; do + echo "Removing $id" + curl -sL \ + -X DELETE \ + -H 'Accept: application/vnd.github+json' \ + -H 'Authorization: Bearer ${{ github.token }}' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "${{ github.api_url }}/repos/${{ github.repository }}/actions/artifacts/$id" \ || echo "Failed to remove bundle" done diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6889a5b0237ba..3517fa53941ea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,9 @@ on: runs-on: required: true type: string + xcode-toolset-version: + required: false + type: string env: # These are needed to make the MSYS2 bash work properly @@ -147,7 +150,7 @@ jobs: run: | # On macOS we need to install some dependencies for testing brew install make - sudo xcode-select --switch /Applications/Xcode_11.7.app/Contents/Developer + sudo xcode-select --switch /Applications/Xcode_${{ inputs.xcode-toolset-version }}.app/Contents/Developer # This will make GNU make available as 'make' and not only as 'gmake' echo '/usr/local/opt/make/libexec/gnubin' >> $GITHUB_PATH if: runner.os == 'macOS' diff --git a/.jcheck/conf b/.jcheck/conf index c39a4fe49e6f7..70d144818091a 100644 --- a/.jcheck/conf +++ b/.jcheck/conf @@ -1,10 +1,11 @@ [general] project=jdk-updates jbs=JDK -version=17.0.12 +version=17.0.15 [checks] error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists +warning=issuestitle,binary [repository] tags=(?:jdk-(?:[1-9]([0-9]*)(?:\.(?:0|[1-9][0-9]*)){0,4})(?:\+(?:(?:[0-9]+))|(?:-ga)))|(?:jdk[4-9](?:u\d{1,3})?-(?:(?:b\d{2,3})|(?:ga)))|(?:hs\d\d(?:\.\d{1,2})?-b\d\d) diff --git a/README.md b/README.md index 399e7cc311f57..55e2a24a1756c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ -# Welcome to the JDK! +# Welcome to OpenJDK 17 Updates! + +The JDK 17 Updates project uses two GitHub repositories. +Updates are continuously developed in the repository [jdk17u-dev](https://github.com/openjdk/jdk17u-dev). This is the repository usually targeted by contributors. +The [jdk17u](https://github.com/openjdk/jdk17u) repository is used for rampdown of the update releases of jdk17u and only accepts critical changes that must make the next release during rampdown. (You probably do not want to target jdk17u). + +For more OpenJDK 17 updates specific information such as timelines and contribution guidelines see the [project wiki page](https://wiki.openjdk.org/display/JDKUpdates/JDK+17u/). + For build instructions please see the [online documentation](https://openjdk.java.net/groups/build/doc/building.html), diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000..f4c5e7e67cb46 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +# JDK Vulnerabilities + +Please follow the process outlined in the [OpenJDK Vulnerability Policy](https://openjdk.org/groups/vulnerability/report) to disclose vulnerabilities in the JDK. diff --git a/doc/building.html b/doc/building.html index 31044787bba59..bcc81dd3d38a0 100644 --- a/doc/building.html +++ b/doc/building.html @@ -514,10 +514,10 @@

Advanced Make Control Variables

Running Tests

Most of the JDK tests are using the JTReg test framework. Make sure that your configuration knows where to find your installation of JTReg. If this is not picked up automatically, use the --with-jtreg=<path to jtreg home> option to point to the JTReg framework. Note that this option should point to the JTReg home, i.e. the top directory, containing lib/jtreg.jar etc.

The Adoption Group provides recent builds of jtreg here. Download the latest .tar.gz file, unpack it, and point --with-jtreg to the jtreg directory that you just unpacked.

-

Building of Hotspot Gtest suite requires the source code of Google Test framework. The top directory, which contains both googletest and googlemock directories, should be specified via --with-gtest. The supported version of Google Test is 1.8.1, whose source code can be obtained:

+

Building of Hotspot Gtest suite requires the source code of Google Test framework. The top directory, which contains both googletest and googlemock directories, should be specified via --with-gtest. The supported version of Google Test is 1.13.0, whose source code can be obtained:

To execute the most basic tests (tier 1), use:

make run-test-tier1
diff --git a/doc/building.md b/doc/building.md index 6a8eabfc7b559..150a3306c29b6 100644 --- a/doc/building.md +++ b/doc/building.md @@ -852,13 +852,14 @@ https://ci.adoptium.net/view/Dependencies/job/dependency_pipeline/lastSuccessful Download the latest `.tar.gz` file, unpack it, and point `--with-jtreg` to the `jtreg` directory that you just unpacked. -Building of Hotspot Gtest suite requires the source code of Google Test framework. -The top directory, which contains both `googletest` and `googlemock` -directories, should be specified via `--with-gtest`. -The supported version of Google Test is 1.8.1, whose source code can be obtained: - - * by downloading and unpacking the source bundle from [here](https://github.com/google/googletest/releases/tag/release-1.8.1) - * or by checking out `release-1.8.1` tag of `googletest` project: `git clone -b release-1.8.1 https://github.com/google/googletest` +Building of Hotspot Gtest suite requires the source code of Google +Test framework. The top directory, which contains both `googletest` +and `googlemock` directories, should be specified via `--with-gtest`. +The minimum supported version of Google Test is 1.13.0, whose source +code can be obtained: + + * by downloading and unpacking the source bundle from [here](https://github.com/google/googletest/releases/tag/v1.13.0) + * or by checking out `v1.13.0` tag of `googletest` project: `git clone -b v1.13.0 https://github.com/google/googletest` To execute the most basic tests (tier 1), use: ``` diff --git a/doc/testing.html b/doc/testing.html index a64a891b5b832..1c8b98557bb60 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -44,6 +44,9 @@

Testing the JDK

  • Docker Tests
  • Non-US locale
  • PKCS11 Tests
  • +
  • Testing with +alternative security providers
  • Client UI Tests
  • @@ -67,7 +70,7 @@

    Configuration

    Test selection

    All functionality is available using the test make target. In this use case, the test or tests to be executed is controlled using the TEST variable. To speed up subsequent test runs with no source code changes, test-only can be used instead, which do not depend on the source and test image build.

    For some common top-level tests, direct make targets have been generated. This includes all JTReg test groups, the hotspot gtest, and custom tests (if present). This means that make test-tier1 is equivalent to make test TEST="tier1", but the latter is more tab-completion friendly. For more complex test runs, the test TEST="x" solution needs to be used.

    -

    The test specifications given in TEST is parsed into fully qualified test descriptors, which clearly and unambigously show which tests will be run. As an example, :tier1 will expand to jtreg:$(TOPDIR)/test/hotspot/jtreg:tier1 jtreg:$(TOPDIR)/test/jdk:tier1 jtreg:$(TOPDIR)/test/langtools:tier1 jtreg:$(TOPDIR)/test/nashorn:tier1 jtreg:$(TOPDIR)/test/jaxp:tier1. You can always submit a list of fully qualified test descriptors in the TEST variable if you want to shortcut the parser.

    +

    The test specifications given in TEST is parsed into fully qualified test descriptors, which clearly and unambigously show which tests will be run. As an example, :tier1 will expand to include all subcomponent test directories that define `tier1`, for example: jtreg:$(TOPDIR)/test/hotspot/jtreg:tier1 jtreg:$(TOPDIR)/test/jdk:tier1 jtreg:$(TOPDIR)/test/langtools:tier1 .... You can always submit a list of fully qualified test descriptors in the TEST variable if you want to shortcut the parser.

    Common Test Groups

    Ideally, all tests are run for every change but this may not be practical due to the limited testing resources, the scope of the change, etc.

    The source tree currently defines a few common test groups in the relevant TEST.groups files. There are test groups that cover a specific component, for example hotspot_gc. It is a good idea to look into TEST.groups files to get a sense what tests are relevant to a particular JDK component.

    @@ -242,6 +245,18 @@

    PKCS11 Tests

    $ make test TEST="jtreg:sun/security/pkcs11/Secmod/AddTrustedCert.java" \
         JTREG="JAVA_OPTIONS=-Djdk.test.lib.artifacts.nsslib-linux_aarch64=/path/to/NSS-libs"

    For more notes about the PKCS11 tests, please refer to test/jdk/sun/security/pkcs11/README.

    +

    Testing with +alternative security providers

    +

    Some security tests use a hardcoded provider for +KeyFactory, Cipher, +KeyPairGenerator, KeyGenerator, +AlgorithmParameterGenerator, KeyAgreement, +Mac, MessageDigest, SecureRandom, +Signature, AlgorithmParameters, +Configuration, Policy, or +SecretKeyFactory objects. Specify the +-Dtest.provider.name=NAME property to use a different +provider for the service(s).

    Client UI Tests

    System key shortcuts

    Some Client UI tests use key sequences which may be reserved by the operating system. Usually that causes the test failure. So it is highly recommended to disable system key shortcuts prior testing. The steps to access and disable system key shortcuts for various platforms are provided below.

    diff --git a/doc/testing.md b/doc/testing.md index 3cdc0d662d6ce..2ac8bcaf12aac 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -58,11 +58,11 @@ test runs, the `test TEST="x"` solution needs to be used. The test specifications given in `TEST` is parsed into fully qualified test descriptors, which clearly and unambigously show which tests will be run. As an -example, `:tier1` will expand to `jtreg:$(TOPDIR)/test/hotspot/jtreg:tier1 -jtreg:$(TOPDIR)/test/jdk:tier1 jtreg:$(TOPDIR)/test/langtools:tier1 -jtreg:$(TOPDIR)/test/nashorn:tier1 jtreg:$(TOPDIR)/test/jaxp:tier1`. You can -always submit a list of fully qualified test descriptors in the `TEST` variable -if you want to shortcut the parser. +example, `:tier1` will expand to include all subcomponent test directories +that define `tier1`, for example: `jtreg:$(TOPDIR)/test/hotspot/jtreg:tier1 +jtreg:$(TOPDIR)/test/jdk:tier1 jtreg:$(TOPDIR)/test/langtools:tier1 ...`. You +can always submit a list of fully qualified test descriptors in the `TEST` +variable if you want to shortcut the parser. ### Common Test Groups @@ -551,6 +551,15 @@ $ make test TEST="jtreg:sun/security/pkcs11/Secmod/AddTrustedCert.java" \ For more notes about the PKCS11 tests, please refer to test/jdk/sun/security/pkcs11/README. +### Testing with alternative security providers + +Some security tests use a hardcoded provider for `KeyFactory`, `Cipher`, +`KeyPairGenerator`, `KeyGenerator`, `AlgorithmParameterGenerator`, +`KeyAgreement`, `Mac`, `MessageDigest`, `SecureRandom`, `Signature`, +`AlgorithmParameters`, `Configuration`, `Policy`, or `SecretKeyFactory` objects. +Specify the `-Dtest.provider.name=NAME` property to use a different provider for +the service(s). + ### Client UI Tests #### System key shortcuts diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 08783c8432105..fc835158ed7ec 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -200,9 +200,10 @@ $(eval $(call SetTestOpt,FAILURE_HANDLER_TIMEOUT,JTREG)) $(eval $(call ParseKeywordVariable, JTREG, \ SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \ TEST_MODE ASSERT VERBOSE RETAIN MAX_MEM RUN_PROBLEM_LISTS \ - RETRY_COUNT REPEAT_COUNT MAX_OUTPUT, \ + RETRY_COUNT REPEAT_COUNT MAX_OUTPUT $(CUSTOM_JTREG_SINGLE_KEYWORDS), \ STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \ - EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS, \ + EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS\ + $(CUSTOM_JTREG_STRING_KEYWORDS), \ )) ifneq ($(JTREG), ) @@ -738,8 +739,6 @@ define SetupRunJtregTestBody # we may end up with a lot of JVM's $1_JTREG_MAX_RAM_PERCENTAGE := $$(shell $(AWK) 'BEGIN { print 25 / $$($1_JTREG_JOBS); }') - JTREG_TIMEOUT_FACTOR ?= 4 - JTREG_VERBOSE ?= fail,error,summary JTREG_RETAIN ?= fail,error JTREG_RUN_PROBLEM_LISTS ?= false @@ -814,6 +813,24 @@ define SetupRunJtregTestBody $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$($1_JTREG_PROBLEM_LIST)) endif + JTREG_ALL_OPTIONS := $$(JTREG_JAVA_OPTIONS) $$(JTREG_VM_OPTIONS) + + JTREG_AUTO_PROBLEM_LISTS := + JTREG_AUTO_TIMEOUT_FACTOR := 4 + + ifneq ($$(findstring -Xcomp, $$(JTREG_ALL_OPTIONS)), ) + JTREG_AUTO_PROBLEM_LISTS += ProblemList-Xcomp.txt + JTREG_AUTO_TIMEOUT_FACTOR := 10 + endif + + ifneq ($$(findstring -XX:+UseZGC, $$(JTREG_ALL_OPTIONS)), ) + ifneq ($$(findstring -XX:-ZGenerational, $$(JTREG_ALL_OPTIONS)), ) + JTREG_AUTO_PROBLEM_LISTS += ProblemList-zgc.txt + else + JTREG_AUTO_PROBLEM_LISTS += ProblemList-generational-zgc.txt + endif + endif + ifneq ($$(JTREG_EXTRA_PROBLEM_LISTS), ) # Accept both absolute paths as well as relative to the current test root. $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ @@ -843,6 +860,20 @@ define SetupRunJtregTestBody endif endif + $$(eval $$(call SetupRunJtregTestCustom, $1)) + + # SetupRunJtregTestCustom might also adjust JTREG_AUTO_ variables + # so set the final results after setting values from custom setup + ifneq ($$(JTREG_AUTO_PROBLEM_LISTS), ) + # Accept both absolute paths as well as relative to the current test root. + $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ + $$(JTREG_AUTO_PROBLEM_LISTS) \ + $$(addprefix $$($1_TEST_ROOT)/, $$(JTREG_AUTO_PROBLEM_LISTS)) \ + )) + endif + + JTREG_TIMEOUT_FACTOR ?= $$(JTREG_AUTO_TIMEOUT_FACTOR) + clean-outputdirs-$1: $$(RM) -r $$($1_TEST_SUPPORT_DIR) $$(RM) -r $$($1_TEST_RESULTS_DIR) @@ -945,7 +976,7 @@ define SetupRunSpecialTestBody $1_EXITCODE := $$($1_TEST_RESULTS_DIR)/exitcode.txt $1_FULL_TEST_NAME := $$(strip $$(patsubst special:%, %, $$($1_TEST))) - ifneq ($$(findstring :, $$($1_FULL_TEST_NAME)), ) + ifneq ($$(findstring:, $$($1_FULL_TEST_NAME)), ) $1_TEST_NAME := $$(firstword $$(subst :, ,$$($1_FULL_TEST_NAME))) $1_TEST_ARGS := $$(strip $$(patsubst special:$$($1_TEST_NAME):%, %, $$($1_TEST))) else diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index 52baed8af935d..173d533359d0f 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -300,9 +300,11 @@ AC_OUTPUT # After AC_OUTPUT, we need to do final work CUSTOM_CONFIG_OUTPUT_GENERATED_HOOK -BASIC_POST_CONFIG_OUTPUT # Finally output some useful information to the user HELP_PRINT_SUMMARY_AND_WARNINGS CUSTOM_SUMMARY_AND_WARNINGS_HOOK HELP_REPEAT_WARNINGS + +# All output is done. Do the post-config output management. +BASIC_POST_CONFIG_OUTPUT diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index f5ce6d27992af..f7f2ad53000e5 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -419,7 +419,7 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER], [ #### OS DEFINES, these should be independent on toolchain if test "x$OPENJDK_TARGET_OS" = xlinux; then - CFLAGS_OS_DEF_JVM="-DLINUX" + CFLAGS_OS_DEF_JVM="-DLINUX -D_FILE_OFFSET_BITS=64" CFLAGS_OS_DEF_JDK="-D_GNU_SOURCE -D_REENTRANT -D_LARGEFILE64_SOURCE" elif test "x$OPENJDK_TARGET_OS" = xmacosx; then CFLAGS_OS_DEF_JVM="-D_ALLBSD_SOURCE -D_DARWIN_C_SOURCE -D_XOPEN_SOURCE" diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index a5557fe0a1881..e8d8565ff6491 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -204,6 +204,17 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_OPTIONS], fi AC_SUBST(INCLUDE_SA) + # Setup default CDS alignment. On platforms where one build may run on machines with different + # page sizes, the JVM choses a compatible alignment to fit all possible page sizes. This slightly + # increases archive size. + # The only platform having this problem at the moment is Linux on aarch64, which may encounter + # three different page sizes: 4K, 64K, and if run on Mac m1 hardware, 16K. + COMPATIBLE_CDS_ALIGNMENT_DEFAULT=false + if test "x$OPENJDK_TARGET_OS" = "xlinux" && test "x$OPENJDK_TARGET_CPU" = "xaarch64"; then + COMPATIBLE_CDS_ALIGNMENT_DEFAULT=true + fi + AC_SUBST(COMPATIBLE_CDS_ALIGNMENT_DEFAULT) + # Compress jars COMPRESS_JARS=false @@ -582,7 +593,7 @@ AC_DEFUN([JDKOPT_ENABLE_DISABLE_CDS_ARCHIVE], # AC_DEFUN([JDKOPT_ENABLE_DISABLE_COMPATIBLE_CDS_ALIGNMENT], [ - UTIL_ARG_ENABLE(NAME: compatible-cds-alignment, DEFAULT: false, + UTIL_ARG_ENABLE(NAME: compatible-cds-alignment, DEFAULT: $COMPATIBLE_CDS_ALIGNMENT_DEFAULT, RESULT: ENABLE_COMPATIBLE_CDS_ALIGNMENT, DESC: [enable use alternative compatible cds core region alignment], DEFAULT_DESC: [disabled], diff --git a/make/autoconf/lib-alsa.m4 b/make/autoconf/lib-alsa.m4 index 19a91f94809f0..8d0fb324cd092 100644 --- a/make/autoconf/lib-alsa.m4 +++ b/make/autoconf/lib-alsa.m4 @@ -70,6 +70,25 @@ AC_DEFUN_ONCE([LIB_SETUP_ALSA], PKG_CHECK_MODULES(ALSA, alsa, [ALSA_FOUND=yes], [ALSA_FOUND=no]) fi fi + if test "x$ALSA_FOUND" = xno; then + # If we have sysroot set, and no explicit library location is set, + # look at known locations in sysroot. + if test "x$SYSROOT" != "x" && test "x${with_alsa_lib}" == x; then + if test -f "$SYSROOT/usr/lib64/libasound.so" && test "x$OPENJDK_TARGET_CPU_BITS" = x64; then + ALSA_LIBS="-L$SYSROOT/usr/lib64 -lasound" + ALSA_FOUND=yes + elif test -f "$SYSROOT/usr/lib/libasound.so"; then + ALSA_LIBS="-L$SYSROOT/usr/lib -lasound" + ALSA_FOUND=yes + elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libasound.so"; then + ALSA_LIBS="-L$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI -lasound" + ALSA_FOUND=yes + elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libasound.so"; then + ALSA_LIBS="-L$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI -lasound" + ALSA_FOUND=yes + fi + fi + fi if test "x$ALSA_FOUND" = xno; then AC_CHECK_HEADERS([alsa/asoundlib.h], [ diff --git a/make/autoconf/lib-tests.m4 b/make/autoconf/lib-tests.m4 index 69ea97ddf4a43..93a2b30dc7e6b 100644 --- a/make/autoconf/lib-tests.m4 +++ b/make/autoconf/lib-tests.m4 @@ -27,8 +27,9 @@ # Setup libraries and functionalities needed to test the JDK. ################################################################################ -# Minimum supported version +# Minimum supported versions JTREG_MINIMUM_VERSION=7.3.1 +GTEST_MINIMUM_VERSION=1.13.0 ############################################################################### # @@ -58,20 +59,13 @@ AC_DEFUN_ONCE([LIB_TESTS_SETUP_GTEST], AC_MSG_RESULT([$GTEST_FRAMEWORK_SRC]) UTIL_FIXUP_PATH([GTEST_FRAMEWORK_SRC]) - # Try to verify version. We require 1.8.1, but this can not be directly - # determined. :-( Instead, there are different, incorrect version - # numbers we can look for. - GTEST_VERSION_1="`$GREP GOOGLETEST_VERSION $GTEST_FRAMEWORK_SRC/CMakeLists.txt | $SED -E -e 's/set\(GOOGLETEST_VERSION (.*)\)/\1/'`" - if test "x$GTEST_VERSION_1" != "x1.9.0"; then - AC_MSG_ERROR([gtest at $GTEST_FRAMEWORK_SRC does not seem to be version 1.8.1]) - fi - - # We cannot grep for "AC_IN*T" as a literal since then m4 will treat it as a macro - # and expand it. - # Additional [] needed to keep m4 from mangling shell constructs. - [ GTEST_VERSION_2="`$GREP -A1 ^.C_INIT $GTEST_FRAMEWORK_SRC/configure.ac | $TAIL -n 1 | $SED -E -e 's/ +\[(.*)],/\1/'`" ] - if test "x$GTEST_VERSION_2" != "x1.8.0"; then - AC_MSG_ERROR([gtest at $GTEST_FRAMEWORK_SRC does not seem to be version 1.8.1 B]) + # Verify that the version is the required one. + # This is a simplified version of TOOLCHAIN_CHECK_COMPILER_VERSION + gtest_version="`$GREP GOOGLETEST_VERSION $GTEST_FRAMEWORK_SRC/CMakeLists.txt | $SED -E -e 's/set\(GOOGLETEST_VERSION (.*)\)/\1/'`" + comparable_actual_version=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", [$]1, [$]2, [$]3, [$]4) }' <<< "$gtest_version"` + comparable_minimum_version=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", [$]1, [$]2, [$]3, [$]4) }' <<< "$GTEST_MINIMUM_VERSION"` + if test $comparable_actual_version -lt $comparable_minimum_version ; then + AC_MSG_ERROR([gtest version is too old, at least version $GTEST_MINIMUM_VERSION is required]) fi fi fi diff --git a/make/autoconf/lib-x11.m4 b/make/autoconf/lib-x11.m4 index b1902a432a1e0..6849b4a26c776 100644 --- a/make/autoconf/lib-x11.m4 +++ b/make/autoconf/lib-x11.m4 @@ -71,9 +71,9 @@ AC_DEFUN_ONCE([LIB_SETUP_X11], elif test -f "$SYSROOT/usr/lib/libX11.so"; then x_libraries="$SYSROOT/usr/lib" elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so"; then - x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so" + x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI" elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so"; then - x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so" + x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI" fi fi fi diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4 index f0bb4333fc94a..5c3a137e65ca1 100644 --- a/make/autoconf/libraries.m4 +++ b/make/autoconf/libraries.m4 @@ -156,7 +156,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES], if test "x$OPENJDK_TARGET_OS" = xwindows; then BASIC_JVM_LIBS="$BASIC_JVM_LIBS kernel32.lib user32.lib gdi32.lib winspool.lib \ comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib powrprof.lib uuid.lib \ - wsock32.lib winmm.lib version.lib psapi.lib" + ws2_32.lib winmm.lib version.lib psapi.lib" fi JDKLIB_LIBS="$BASIC_JDKLIB_LIBS" diff --git a/make/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4 index 8105ce9e80a8e..98573f965b315 100644 --- a/make/autoconf/toolchain.m4 +++ b/make/autoconf/toolchain.m4 @@ -429,6 +429,11 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_COMPILER_VERSION], # Copyright (C) 2013 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + # or look like + # gcc (GCC) 10.2.1 20200825 (Alibaba 10.2.1-3.8 2.32) + # Copyright (C) 2020 Free Software Foundation, Inc. + # This is free software; see the source for copying conditions. There is NO + # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. COMPILER_VERSION_OUTPUT=`$COMPILER --version 2>&1` # Check that this is likely to be GCC. $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "Free Software Foundation" > /dev/null @@ -442,7 +447,8 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_COMPILER_VERSION], COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT | \ $SED -e 's/ *Copyright .*//'` COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \ - $SED -e 's/^.* \(@<:@1-9@:>@<:@0-9@:>@*\.@<:@0-9.@:>@*\)@<:@^0-9.@:>@.*$/\1/'` + $AWK -F ')' '{print [$]2}' | \ + $AWK '{print [$]1}'` elif test "x$TOOLCHAIN_TYPE" = xclang; then # clang --version output typically looks like # Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn) diff --git a/make/common/MakeBase.gmk b/make/common/MakeBase.gmk index 49ad3d60f90e5..2a06d8eb8b3c5 100644 --- a/make/common/MakeBase.gmk +++ b/make/common/MakeBase.gmk @@ -290,6 +290,7 @@ ifeq ($(call isTargetOs, macosx), true) $(CP) -fRP '$(call DecodeSpace, $<)' '$(call DecodeSpace, $@)'; \ fi if [ -n "`$(XATTR) -ls '$(call DecodeSpace, $@)'`" ]; then \ + $(CHMOD) -h u+w '$(call DecodeSpace, $@)'; \ $(XATTR) -cs '$(call DecodeSpace, $@)'; \ fi endef diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk index 215d90d17e96f..af601bac1d42b 100644 --- a/make/common/NativeCompilation.gmk +++ b/make/common/NativeCompilation.gmk @@ -215,7 +215,21 @@ DEPENDENCY_TARGET_SED_PATTERN := \ # The fix-deps-file macro is used to adjust the contents of the generated make # dependency files to contain paths compatible with make. # +REWRITE_PATHS_RELATIVE = false ifeq ($(ALLOW_ABSOLUTE_PATHS_IN_OUTPUT)-$(FILE_MACRO_CFLAGS), false-) + REWRITE_PATHS_RELATIVE = true +endif + +# CCACHE_BASEDIR needs fix-deps-file as makefiles use absolute filenames for +# object files while CCACHE_BASEDIR will make ccache relativize all paths for +# its compiler. The compiler then produces relative dependency files. +# make does not know a relative and absolute filename is the same so it will +# ignore such dependencies. +ifneq ($(CCACHE), ) + REWRITE_PATHS_RELATIVE = true +endif + +ifeq ($(REWRITE_PATHS_RELATIVE), true) # Need to handle -I flags as both '-Ifoo' and '-I foo'. MakeCommandRelative = \ $(CD) $(WORKSPACE_ROOT) && \ diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index 1d2c643d0d4dc..47c93e320a578 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,21 @@ # Versions and download locations for dependencies used by GitHub Actions (GHA) -GTEST_VERSION=1.8.1 +GTEST_VERSION=1.13.0 JTREG_VERSION=7.3.1+1 LINUX_X64_BOOT_JDK_EXT=tar.gz -LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.11%2B9/OpenJDK17U-jdk_x64_linux_hotspot_17.0.11_9.tar.gz -LINUX_X64_BOOT_JDK_SHA256=aa7fb6bb342319d227a838af5c363bfa1b4a670c209372f9e6585bd79da6220c +LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.14%2B7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.14_7.tar.gz +LINUX_X64_BOOT_JDK_SHA256=a3af83983fb94dd7d11b13ba2dba0fb6819dc2caaf87e6937afd22ad4680ae9a -WINDOWS_X64_BOOT_JDK_EXT=zip -WINDOWS_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.11%2B9/OpenJDK17U-jdk_x64_windows_hotspot_17.0.11_9.zip -WINDOWS_X64_BOOT_JDK_SHA256=fdd6664d4131370398fbc8bfbb7b46dbfec4a22a090a511fe5c379dae188c390 +MACOS_AARCH64_BOOT_JDK_EXT=tar.gz +MACOS_AARCH64_BOOT_JDK_URL=https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.14%2B7/OpenJDK17U-jdk_aarch64_mac_hotspot_17.0.14_7.tar.gz +MACOS_AARCH64_BOOT_JDK_SHA256=95bcc8052340394b87644d71a60fb26f31857f4090a7dfee57113e9e0f2dfacb MACOS_X64_BOOT_JDK_EXT=tar.gz -MACOS_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.11%2B9/OpenJDK17U-jdk_x64_mac_hotspot_17.0.11_9.tar.gz -MACOS_X64_BOOT_JDK_SHA256=f8b96724618f4df557c47f11048d1084e98ed3eb87f0dbd5b84f768a80c3348e +MACOS_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.14%2B7/OpenJDK17U-jdk_x64_mac_hotspot_17.0.14_7.tar.gz +MACOS_X64_BOOT_JDK_SHA256=bc2e9225d156d27149fc7a91817e6b64f76132b2b81d1f44cb8c90d7497b6ea7 + +WINDOWS_X64_BOOT_JDK_EXT=zip +WINDOWS_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.14%2B7/OpenJDK17U-jdk_x64_windows_hotspot_17.0.14_7.zip +WINDOWS_X64_BOOT_JDK_SHA256=dddb108e0bf8c3e3a9c5c782fee5874a6a86d5323189969f17094260cf3a1125 diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 4ee43abe774bd..64dcb7723a9b5 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -1242,7 +1242,7 @@ var getJibProfilesDependencies = function (input, common) { gtest: { organization: common.organization, ext: "tar.gz", - revision: "1.8.1" + revision: "1.13.0+1.0" }, }; diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 37266259ef2bd..29ccfb0801b37 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -28,12 +28,12 @@ DEFAULT_VERSION_FEATURE=17 DEFAULT_VERSION_INTERIM=0 -DEFAULT_VERSION_UPDATE=12 +DEFAULT_VERSION_UPDATE=15 DEFAULT_VERSION_PATCH=0 DEFAULT_VERSION_EXTRA1=0 DEFAULT_VERSION_EXTRA2=0 DEFAULT_VERSION_EXTRA3=0 -DEFAULT_VERSION_DATE=2024-07-16 +DEFAULT_VERSION_DATE=2025-04-15 DEFAULT_VERSION_CLASSFILE_MAJOR=61 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 diff --git a/make/data/cacerts/globalsigne46 b/make/data/cacerts/globalsigne46 new file mode 100644 index 0000000000000..50c7dadefdd4e --- /dev/null +++ b/make/data/cacerts/globalsigne46 @@ -0,0 +1,20 @@ +Owner: CN=GlobalSign Root E46, O=GlobalSign nv-sa, C=BE +Issuer: CN=GlobalSign Root E46, O=GlobalSign nv-sa, C=BE +Serial number: 11d2bbba336ed4bce62468c50d841d98e843 +Valid from: Wed Mar 20 00:00:00 GMT 2019 until: Tue Mar 20 00:00:00 GMT 2046 +Signature algorithm name: SHA384withECDSA +Subject Public Key Algorithm: 384-bit EC (secp384r1) key +Version: 3 +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- diff --git a/make/data/cacerts/globalsignr46 b/make/data/cacerts/globalsignr46 new file mode 100644 index 0000000000000..80d7d92d4fb63 --- /dev/null +++ b/make/data/cacerts/globalsignr46 @@ -0,0 +1,38 @@ +Owner: CN=GlobalSign Root R46, O=GlobalSign nv-sa, C=BE +Issuer: CN=GlobalSign Root R46, O=GlobalSign nv-sa, C=BE +Serial number: 11d2bbb9d723189e405f0a9d2dd0df2567d1 +Valid from: Wed Mar 20 00:00:00 GMT 2019 until: Tue Mar 20 00:00:00 GMT 2046 +Signature algorithm name: SHA384withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE----- diff --git a/make/data/cacerts/ssltlsrootecc2022 b/make/data/cacerts/ssltlsrootecc2022 new file mode 100644 index 0000000000000..706e6aefb4ee0 --- /dev/null +++ b/make/data/cacerts/ssltlsrootecc2022 @@ -0,0 +1,21 @@ +Owner: CN=SSL.com TLS ECC Root CA 2022, O=SSL Corporation, C=US +Issuer: CN=SSL.com TLS ECC Root CA 2022, O=SSL Corporation, C=US +Serial number: 1403f5abfb378b17405be243b2a5d1c4 +Valid from: Thu Aug 25 16:33:48 GMT 2022 until: Sun Aug 19 16:33:47 GMT 2046 +Signature algorithm name: SHA384withECDSA +Subject Public Key Algorithm: 384-bit EC (secp384r1) key +Version: 3 +-----BEGIN CERTIFICATE----- +MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT +U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2 +MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh +dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm +acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN +SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME +GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW +uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp +15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN +b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g== +-----END CERTIFICATE----- diff --git a/make/data/cacerts/ssltlsrootrsa2022 b/make/data/cacerts/ssltlsrootrsa2022 new file mode 100644 index 0000000000000..ad456b0b5f472 --- /dev/null +++ b/make/data/cacerts/ssltlsrootrsa2022 @@ -0,0 +1,39 @@ +Owner: CN=SSL.com TLS RSA Root CA 2022, O=SSL Corporation, C=US +Issuer: CN=SSL.com TLS RSA Root CA 2022, O=SSL Corporation, C=US +Serial number: 6fbedaad73bd0840e28b4dbed4f75b91 +Valid from: Thu Aug 25 16:34:22 GMT 2022 until: Sun Aug 19 16:34:21 GMT 2046 +Signature algorithm name: SHA256withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO +MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD +DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX +DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw +b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP +L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY +t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins +S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3 +PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO +L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3 +R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w +dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS ++YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS +d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG +AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f +gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z +NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt +hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM +QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf +R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ +DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW +P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy +lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq +bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w +AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q +r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji +Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU +98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= +-----END CERTIFICATE----- diff --git a/make/data/cldr/common/main/ff_Adlm.xml b/make/data/cldr/common/main/ff_Adlm.xml index 02feba420c829..05bb2c6db9561 100644 --- a/make/data/cldr/common/main/ff_Adlm.xml +++ b/make/data/cldr/common/main/ff_Adlm.xml @@ -272,7 +272,7 @@ CLDR data files are interpreted according to the LDML specification (http://unic 𞤄𞤢𞤸𞤢𞤥𞤢𞥄𞤧 𞤄𞤵𞥅𞤼𞤢𞥄𞤲 𞤅𞤵𞤪𞤭𞥅𞤪𞤫 𞤄𞤵𞥅𞤾𞤫𞥅 - ‮𞤄𞤮𞤼𞤧𞤵𞤱𞤢𞥄𞤲𞤢 + 𞤄𞤮𞤼𞤧𞤵𞤱𞤢𞥄𞤲𞤢 𞤄𞤫𞤤𞤢𞤪𞤵𞥅𞤧 𞤄𞤫𞤤𞤭𞥅𞥁 𞤑𞤢𞤲𞤢𞤣𞤢𞥄 @@ -2245,7 +2245,7 @@ CLDR data files are interpreted according to the LDML specification (http://unic 𞤐𞤵𞥅𞤳 - ‮𞤋𞤼𞥆𞤮𞤳𞤮𞤪𞤼𞤮𞥅𞤪𞤥𞤭𞥅𞤼 + 𞤋𞤼𞥆𞤮𞤳𞤮𞤪𞤼𞤮𞥅𞤪𞤥𞤭𞥅𞤼 𞤁𞤢𞥄𞤲𞤥𞤢𞤪𞤳𞥃𞤢𞥄𞤾𞤲 diff --git a/make/data/currency/CurrencyData.properties b/make/data/currency/CurrencyData.properties index 26f4aa24d88d8..550662ec38a14 100644 --- a/make/data/currency/CurrencyData.properties +++ b/make/data/currency/CurrencyData.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ formatVersion=3 # Version of the currency code information in this class. # It is a serial number that accompanies with each amendment. -dataVersion=176 +dataVersion=177 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. @@ -56,8 +56,8 @@ all=ADP020-AED784-AFA004-AFN971-ALL008-AMD051-ANG532-AOA973-ARS032-ATS040-AUD036 TPE626-TRL792-TRY949-TTD780-TWD901-TZS834-UAH980-UGX800-USD840-USN997-USS998-UYI940-\ UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\ XBB956-XBC957-XBD958-XCD951-XCG532-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\ - XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWL932-\ - ZWN942-ZWR935 + XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWG924-\ + ZWL932-ZWN942-ZWR935 # Mappings from ISO 3166 country codes to ISO 4217 currency codes. @@ -582,7 +582,7 @@ YE=YER # ZAMBIA ZM=ZMW # ZIMBABWE -ZW=ZWL +ZW=ZWG # List of currencies with non-2digit decimals for minor units, diff --git a/make/data/lsrdata/language-subtag-registry.txt b/make/data/lsrdata/language-subtag-registry.txt index 4737c50e425c5..b00ea67e7e8ca 100644 --- a/make/data/lsrdata/language-subtag-registry.txt +++ b/make/data/lsrdata/language-subtag-registry.txt @@ -1,4 +1,4 @@ -File-Date: 2024-03-07 +File-Date: 2024-11-19 %% Type: language Subtag: aa @@ -9402,6 +9402,7 @@ Macrolanguage: doi %% Type: language Subtag: dgr +Description: Tlicho Description: Dogrib Description: Tłı̨chǫ Added: 2005-10-16 @@ -15255,6 +15256,11 @@ Description: Isu (Menchum Division) Added: 2009-07-29 %% Type: language +Subtag: isv +Description: Interslavic +Added: 2024-05-15 +%% +Type: language Subtag: itb Description: Binongan Itneg Added: 2009-07-29 @@ -47985,6 +47991,16 @@ Added: 2008-10-14 Prefix: kw %% Type: variant +Subtag: kleinsch +Description: Kleinschmidt orthography +Description: Allattaasitaamut +Added: 2024-07-20 +Prefix: kl +Prefix: kl-tunumiit +Comments: Orthography for Greenlandic designed by Samuel Kleinschmidt, + used from 1851 to 1973. +%% +Type: variant Subtag: kociewie Description: The Kociewie dialect of Polish Added: 2014-11-27 @@ -48003,7 +48019,9 @@ Type: variant Subtag: laukika Description: Classical Sanskrit Added: 2010-07-28 +Deprecated: 2024-06-08 Prefix: sa +Comments: Preferred tag is cls %% Type: variant Subtag: lemosin @@ -48379,9 +48397,11 @@ Type: variant Subtag: vaidika Description: Vedic Sanskrit Added: 2010-07-28 +Deprecated: 2024-06-08 Prefix: sa Comments: The most ancient dialect of Sanskrit used in verse and prose composed until about the 4th century B.C.E. +Comments: Preferred tag is vsn %% Type: variant Subtag: valbadia diff --git a/make/data/publicsuffixlist/VERSION b/make/data/publicsuffixlist/VERSION index f86d2df03ca38..24f4407cc89ab 100644 --- a/make/data/publicsuffixlist/VERSION +++ b/make/data/publicsuffixlist/VERSION @@ -1,2 +1,2 @@ -Github: https://raw.githubusercontent.com/publicsuffix/list/b5bf572c52988dbe9d865b8f090ea819024a9936/public_suffix_list.dat -Date: 2023-11-09 +Github: https://raw.githubusercontent.com/publicsuffix/list/1cbd6e71a9b83620b1d0b11e49d3d9ff48c27e22/public_suffix_list.dat +Date: 2024-05-07 diff --git a/make/data/publicsuffixlist/public_suffix_list.dat b/make/data/publicsuffixlist/public_suffix_list.dat index fff6e9a494d64..34733674ea00f 100644 --- a/make/data/publicsuffixlist/public_suffix_list.dat +++ b/make/data/publicsuffixlist/public_suffix_list.dat @@ -6710,7 +6710,7 @@ org.zw // newGTLDs -// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2023-11-03T15:13:18Z +// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2024-05-04T15:12:50Z // This list is auto-generated, don't edit it manually. // aaa : American Automobile Association, Inc. // https://www.iana.org/domains/root/db/aaa.html @@ -6896,7 +6896,7 @@ anquan // https://www.iana.org/domains/root/db/anz.html anz -// aol : Oath Inc. +// aol : Yahoo Inc. // https://www.iana.org/domains/root/db/aol.html aol @@ -6988,10 +6988,6 @@ auto // https://www.iana.org/domains/root/db/autos.html autos -// avianca : Avianca Inc. -// https://www.iana.org/domains/root/db/avianca.html -avianca - // aws : AWS Registry LLC // https://www.iana.org/domains/root/db/aws.html aws @@ -7016,10 +7012,6 @@ baidu // https://www.iana.org/domains/root/db/banamex.html banamex -// bananarepublic : The Gap, Inc. -// https://www.iana.org/domains/root/db/bananarepublic.html -bananarepublic - // band : Dog Beach, LLC // https://www.iana.org/domains/root/db/band.html band @@ -7544,10 +7536,6 @@ college // https://www.iana.org/domains/root/db/cologne.html cologne -// comcast : Comcast IP Holdings I, LLC -// https://www.iana.org/domains/root/db/comcast.html -comcast - // commbank : COMMONWEALTH BANK OF AUSTRALIA // https://www.iana.org/domains/root/db/commbank.html commbank @@ -7752,6 +7740,10 @@ dental // https://www.iana.org/domains/root/db/dentist.html dentist +// desi +// https://www.iana.org/domains/root/db/desi.html +desi + // design : Registry Services, LLC // https://www.iana.org/domains/root/db/design.html design @@ -7796,7 +7788,7 @@ discover // https://www.iana.org/domains/root/db/dish.html dish -// diy : Lifestyle Domain Holdings, Inc. +// diy : Internet Naming Company LLC // https://www.iana.org/domains/root/db/diy.html diy @@ -7928,10 +7920,6 @@ esq // https://www.iana.org/domains/root/db/estate.html estate -// etisalat : Emirates Telecommunications Corporation (trading as Etisalat) -// https://www.iana.org/domains/root/db/etisalat.html -etisalat - // eurovision : European Broadcasting Union (EBU) // https://www.iana.org/domains/root/db/eurovision.html eurovision @@ -8104,7 +8092,7 @@ fly // https://www.iana.org/domains/root/db/foo.html foo -// food : Lifestyle Domain Holdings, Inc. +// food : Internet Naming Company LLC // https://www.iana.org/domains/root/db/food.html food @@ -8164,7 +8152,7 @@ ftr // https://www.iana.org/domains/root/db/fujitsu.html fujitsu -// fun : Radix FZC DMCC +// fun : Radix Technologies Inc. // https://www.iana.org/domains/root/db/fun.html fun @@ -8312,7 +8300,7 @@ goldpoint // https://www.iana.org/domains/root/db/golf.html golf -// goo : NTT Resonant Inc. +// goo : NTT DOCOMO, INC. // https://www.iana.org/domains/root/db/goo.html goo @@ -8364,10 +8352,6 @@ grocery // https://www.iana.org/domains/root/db/group.html group -// guardian : The Guardian Life Insurance Company of America -// https://www.iana.org/domains/root/db/guardian.html -guardian - // gucci : Guccio Gucci S.p.a. // https://www.iana.org/domains/root/db/gucci.html gucci @@ -8500,7 +8484,7 @@ horse // https://www.iana.org/domains/root/db/hospital.html hospital -// host : Radix FZC DMCC +// host : Radix Technologies Inc. // https://www.iana.org/domains/root/db/host.html host @@ -8720,7 +8704,7 @@ jpmorgan // https://www.iana.org/domains/root/db/jprs.html jprs -// juegos : Internet Naming Company LLC +// juegos : Dog Beach, LLC // https://www.iana.org/domains/root/db/juegos.html juegos @@ -8908,7 +8892,7 @@ life // https://www.iana.org/domains/root/db/lifeinsurance.html lifeinsurance -// lifestyle : Lifestyle Domain Holdings, Inc. +// lifestyle : Internet Naming Company LLC // https://www.iana.org/domains/root/db/lifestyle.html lifestyle @@ -8948,7 +8932,7 @@ lipsy // https://www.iana.org/domains/root/db/live.html live -// living : Lifestyle Domain Holdings, Inc. +// living : Internet Naming Company LLC // https://www.iana.org/domains/root/db/living.html living @@ -9392,10 +9376,6 @@ olayan // https://www.iana.org/domains/root/db/olayangroup.html olayangroup -// oldnavy : The Gap, Inc. -// https://www.iana.org/domains/root/db/oldnavy.html -oldnavy - // ollo : Dish DBS Corporation // https://www.iana.org/domains/root/db/ollo.html ollo @@ -9416,7 +9396,7 @@ ong // https://www.iana.org/domains/root/db/onl.html onl -// online : Radix FZC DMCC +// online : Radix Technologies Inc. // https://www.iana.org/domains/root/db/online.html online @@ -9620,7 +9600,7 @@ pramerica // https://www.iana.org/domains/root/db/praxi.html praxi -// press : Radix FZC DMCC +// press : Radix Technologies Inc. // https://www.iana.org/domains/root/db/press.html press @@ -9928,10 +9908,6 @@ sbi // https://www.iana.org/domains/root/db/sbs.html sbs -// sca : SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ) -// https://www.iana.org/domains/root/db/sca.html -sca - // scb : The Siam Commercial Bank Public Company Limited ("SCB") // https://www.iana.org/domains/root/db/scb.html scb @@ -10076,7 +10052,7 @@ sina // https://www.iana.org/domains/root/db/singles.html singles -// site : Radix FZC DMCC +// site : Radix Technologies Inc. // https://www.iana.org/domains/root/db/site.html site @@ -10156,7 +10132,7 @@ soy // https://www.iana.org/domains/root/db/spa.html spa -// space : Radix FZC DMCC +// space : Radix Technologies Inc. // https://www.iana.org/domains/root/db/space.html space @@ -10208,7 +10184,7 @@ stockholm // https://www.iana.org/domains/root/db/storage.html storage -// store : Radix FZC DMCC +// store : Radix Technologies Inc. // https://www.iana.org/domains/root/db/store.html store @@ -10324,7 +10300,7 @@ tdk // https://www.iana.org/domains/root/db/team.html team -// tech : Radix FZC DMCC +// tech : Radix Technologies Inc. // https://www.iana.org/domains/root/db/tech.html tech @@ -10508,7 +10484,7 @@ unicom // https://www.iana.org/domains/root/db/university.html university -// uno : Radix FZC DMCC +// uno : Radix Technologies Inc. // https://www.iana.org/domains/root/db/uno.html uno @@ -10524,7 +10500,7 @@ ups // https://www.iana.org/domains/root/db/vacations.html vacations -// vana : Lifestyle Domain Holdings, Inc. +// vana : Internet Naming Company LLC // https://www.iana.org/domains/root/db/vana.html vana @@ -10608,10 +10584,6 @@ vlaanderen // https://www.iana.org/domains/root/db/vodka.html vodka -// volkswagen : Volkswagen Group of America Inc. -// https://www.iana.org/domains/root/db/volkswagen.html -volkswagen - // volvo : Volvo Holding Sverige Aktiebolag // https://www.iana.org/domains/root/db/volvo.html volvo @@ -10676,10 +10648,14 @@ webcam // https://www.iana.org/domains/root/db/weber.html weber -// website : Radix FZC DMCC +// website : Radix Technologies Inc. // https://www.iana.org/domains/root/db/website.html website +// wed +// https://www.iana.org/domains/root/db/wed.html +wed + // wedding : Registry Services, LLC // https://www.iana.org/domains/root/db/wedding.html wedding @@ -10768,10 +10744,6 @@ xbox // https://www.iana.org/domains/root/db/xerox.html xerox -// xfinity : Comcast IP Holdings I, LLC -// https://www.iana.org/domains/root/db/xfinity.html -xfinity - // xihuan : Beijing Qihu Keji Co., Ltd. // https://www.iana.org/domains/root/db/xihuan.html xihuan @@ -11012,10 +10984,6 @@ xin // https://www.iana.org/domains/root/db/xn--mgba7c0bbn0a.html العليان -// xn--mgbaakc7dvf : Emirates Telecommunications Corporation (trading as Etisalat) -// https://www.iana.org/domains/root/db/xn--mgbaakc7dvf.html -اتصالات - // xn--mgbab2bd : CORE Association // https://www.iana.org/domains/root/db/xn--mgbab2bd.html بازار @@ -11152,7 +11120,7 @@ xyz // https://www.iana.org/domains/root/db/yachts.html yachts -// yahoo : Oath Inc. +// yahoo : Yahoo Inc. // https://www.iana.org/domains/root/db/yahoo.html yahoo @@ -11217,6 +11185,12 @@ zuerich // ===BEGIN PRIVATE DOMAINS=== // (Note: these are in alphabetical order by company name) +// 12CHARS: https://12chars.com +// Submitted by Kenny Niehage +12chars.dev +12chars.it +12chars.pro + // 1GB LLC : https://www.1gb.ua/ // Submitted by 1GB LLC cc.ua @@ -11226,6 +11200,15 @@ ltd.ua // 611coin : https://611project.org/ 611.to +// AAA workspace : https://aaa.vodka +// Submitted by Kirill Rezraf +aaa.vodka + +// A2 Hosting +// Submitted by Tyler Hall +a2hosted.com +cpserver.com + // Aaron Marais' Gitlab pages: https://lab.aaronleem.co.za // Submitted by Aaron Marais graphox.us @@ -11242,12 +11225,18 @@ graphox.us // Submitted by Ofer Kalaora activetrail.biz +// Adaptable.io : https://adaptable.io +// Submitted by Mark Terrel +adaptable.app + // Adobe : https://www.adobe.com/ // Submitted by Ian Boston and Lars Trieloff adobeaemcloud.com *.dev.adobeaemcloud.com +aem.live hlx.live adobeaemcloud.net +aem.page hlx.page hlx3.page @@ -11319,7 +11308,7 @@ myamaze.net // Amazon API Gateway // Submitted by AWS Security -// Reference: 4d863337-ff98-4501-a6f2-361eba8445d6 +// Reference: 9e37648f-a66c-4655-9ab1-5981f8737197 execute-api.cn-north-1.amazonaws.com.cn execute-api.cn-northwest-1.amazonaws.com.cn execute-api.af-south-1.amazonaws.com @@ -11334,6 +11323,7 @@ execute-api.ap-southeast-2.amazonaws.com execute-api.ap-southeast-3.amazonaws.com execute-api.ap-southeast-4.amazonaws.com execute-api.ca-central-1.amazonaws.com +execute-api.ca-west-1.amazonaws.com execute-api.eu-central-1.amazonaws.com execute-api.eu-central-2.amazonaws.com execute-api.eu-north-1.amazonaws.com @@ -11360,23 +11350,28 @@ cloudfront.net // Amazon Cognito // Submitted by AWS Security -// Reference: 7bee1013-f456-47df-bfe8-03c78d946d61 +// Reference: 09588633-91fe-49d8-b4e7-ec36496d11f3 auth.af-south-1.amazoncognito.com auth.ap-northeast-1.amazoncognito.com auth.ap-northeast-2.amazoncognito.com auth.ap-northeast-3.amazoncognito.com auth.ap-south-1.amazoncognito.com +auth.ap-south-2.amazoncognito.com auth.ap-southeast-1.amazoncognito.com auth.ap-southeast-2.amazoncognito.com auth.ap-southeast-3.amazoncognito.com +auth.ap-southeast-4.amazoncognito.com auth.ca-central-1.amazoncognito.com auth.eu-central-1.amazoncognito.com +auth.eu-central-2.amazoncognito.com auth.eu-north-1.amazoncognito.com auth.eu-south-1.amazoncognito.com +auth.eu-south-2.amazoncognito.com auth.eu-west-1.amazoncognito.com auth.eu-west-2.amazoncognito.com auth.eu-west-3.amazoncognito.com auth.il-central-1.amazoncognito.com +auth.me-central-1.amazoncognito.com auth.me-south-1.amazoncognito.com auth.sa-east-1.amazoncognito.com auth.us-east-1.amazoncognito.com @@ -11399,7 +11394,7 @@ us-east-1.amazonaws.com // Amazon EMR // Submitted by AWS Security -// Reference: 597f3f8e-9283-4e48-8e32-7ee25a1ff6ab +// Reference: 82f43f9f-bbb8-400e-8349-854f5a62f20d emrappui-prod.cn-north-1.amazonaws.com.cn emrnotebooks-prod.cn-north-1.amazonaws.com.cn emrstudio-prod.cn-north-1.amazonaws.com.cn @@ -11424,6 +11419,9 @@ emrstudio-prod.ap-northeast-3.amazonaws.com emrappui-prod.ap-south-1.amazonaws.com emrnotebooks-prod.ap-south-1.amazonaws.com emrstudio-prod.ap-south-1.amazonaws.com +emrappui-prod.ap-south-2.amazonaws.com +emrnotebooks-prod.ap-south-2.amazonaws.com +emrstudio-prod.ap-south-2.amazonaws.com emrappui-prod.ap-southeast-1.amazonaws.com emrnotebooks-prod.ap-southeast-1.amazonaws.com emrstudio-prod.ap-southeast-1.amazonaws.com @@ -11433,18 +11431,30 @@ emrstudio-prod.ap-southeast-2.amazonaws.com emrappui-prod.ap-southeast-3.amazonaws.com emrnotebooks-prod.ap-southeast-3.amazonaws.com emrstudio-prod.ap-southeast-3.amazonaws.com +emrappui-prod.ap-southeast-4.amazonaws.com +emrnotebooks-prod.ap-southeast-4.amazonaws.com +emrstudio-prod.ap-southeast-4.amazonaws.com emrappui-prod.ca-central-1.amazonaws.com emrnotebooks-prod.ca-central-1.amazonaws.com emrstudio-prod.ca-central-1.amazonaws.com +emrappui-prod.ca-west-1.amazonaws.com +emrnotebooks-prod.ca-west-1.amazonaws.com +emrstudio-prod.ca-west-1.amazonaws.com emrappui-prod.eu-central-1.amazonaws.com emrnotebooks-prod.eu-central-1.amazonaws.com emrstudio-prod.eu-central-1.amazonaws.com +emrappui-prod.eu-central-2.amazonaws.com +emrnotebooks-prod.eu-central-2.amazonaws.com +emrstudio-prod.eu-central-2.amazonaws.com emrappui-prod.eu-north-1.amazonaws.com emrnotebooks-prod.eu-north-1.amazonaws.com emrstudio-prod.eu-north-1.amazonaws.com emrappui-prod.eu-south-1.amazonaws.com emrnotebooks-prod.eu-south-1.amazonaws.com emrstudio-prod.eu-south-1.amazonaws.com +emrappui-prod.eu-south-2.amazonaws.com +emrnotebooks-prod.eu-south-2.amazonaws.com +emrstudio-prod.eu-south-2.amazonaws.com emrappui-prod.eu-west-1.amazonaws.com emrnotebooks-prod.eu-west-1.amazonaws.com emrstudio-prod.eu-west-1.amazonaws.com @@ -11454,6 +11464,9 @@ emrstudio-prod.eu-west-2.amazonaws.com emrappui-prod.eu-west-3.amazonaws.com emrnotebooks-prod.eu-west-3.amazonaws.com emrstudio-prod.eu-west-3.amazonaws.com +emrappui-prod.il-central-1.amazonaws.com +emrnotebooks-prod.il-central-1.amazonaws.com +emrstudio-prod.il-central-1.amazonaws.com emrappui-prod.me-central-1.amazonaws.com emrnotebooks-prod.me-central-1.amazonaws.com emrstudio-prod.me-central-1.amazonaws.com @@ -11484,9 +11497,11 @@ emrstudio-prod.us-west-2.amazonaws.com // Amazon Managed Workflows for Apache Airflow // Submitted by AWS Security -// Reference: 4ab55e6f-90c0-4a8d-b6a0-52ca5dbb1c2e +// Reference: 87f24ece-a77e-40e8-bb4a-f6b74fe9f975 *.cn-north-1.airflow.amazonaws.com.cn *.cn-northwest-1.airflow.amazonaws.com.cn +*.af-south-1.airflow.amazonaws.com +*.ap-east-1.airflow.amazonaws.com *.ap-northeast-1.airflow.amazonaws.com *.ap-northeast-2.airflow.amazonaws.com *.ap-south-1.airflow.amazonaws.com @@ -11495,17 +11510,20 @@ emrstudio-prod.us-west-2.amazonaws.com *.ca-central-1.airflow.amazonaws.com *.eu-central-1.airflow.amazonaws.com *.eu-north-1.airflow.amazonaws.com +*.eu-south-1.airflow.amazonaws.com *.eu-west-1.airflow.amazonaws.com *.eu-west-2.airflow.amazonaws.com *.eu-west-3.airflow.amazonaws.com +*.me-south-1.airflow.amazonaws.com *.sa-east-1.airflow.amazonaws.com *.us-east-1.airflow.amazonaws.com *.us-east-2.airflow.amazonaws.com +*.us-west-1.airflow.amazonaws.com *.us-west-2.airflow.amazonaws.com // Amazon S3 // Submitted by AWS Security -// Reference: 0e801048-08f2-4064-9cb8-e7373e0b57f4 +// Reference: cd5c8b3a-67b7-4b40-9236-c87ce81a3d10 s3.dualstack.cn-north-1.amazonaws.com.cn s3-accesspoint.dualstack.cn-north-1.amazonaws.com.cn s3-website.dualstack.cn-north-1.amazonaws.com.cn @@ -11604,6 +11622,16 @@ s3-accesspoint-fips.ca-central-1.amazonaws.com s3-fips.ca-central-1.amazonaws.com s3-object-lambda.ca-central-1.amazonaws.com s3-website.ca-central-1.amazonaws.com +s3.dualstack.ca-west-1.amazonaws.com +s3-accesspoint.dualstack.ca-west-1.amazonaws.com +s3-accesspoint-fips.dualstack.ca-west-1.amazonaws.com +s3-fips.dualstack.ca-west-1.amazonaws.com +s3-website.dualstack.ca-west-1.amazonaws.com +s3.ca-west-1.amazonaws.com +s3-accesspoint.ca-west-1.amazonaws.com +s3-accesspoint-fips.ca-west-1.amazonaws.com +s3-fips.ca-west-1.amazonaws.com +s3-website.ca-west-1.amazonaws.com s3.dualstack.eu-central-1.amazonaws.com s3-accesspoint.dualstack.eu-central-1.amazonaws.com s3-website.dualstack.eu-central-1.amazonaws.com @@ -11784,9 +11812,25 @@ s3-fips.us-west-2.amazonaws.com s3-object-lambda.us-west-2.amazonaws.com s3-website.us-west-2.amazonaws.com +// Amazon SageMaker Ground Truth +// Submitted by AWS Security +// Reference: 98dbfde4-7802-48c3-8751-b60f204e0d9c +labeling.ap-northeast-1.sagemaker.aws +labeling.ap-northeast-2.sagemaker.aws +labeling.ap-south-1.sagemaker.aws +labeling.ap-southeast-1.sagemaker.aws +labeling.ap-southeast-2.sagemaker.aws +labeling.ca-central-1.sagemaker.aws +labeling.eu-central-1.sagemaker.aws +labeling.eu-west-1.sagemaker.aws +labeling.eu-west-2.sagemaker.aws +labeling.us-east-1.sagemaker.aws +labeling.us-east-2.sagemaker.aws +labeling.us-west-2.sagemaker.aws + // Amazon SageMaker Notebook Instances // Submitted by AWS Security -// Reference: fe8c9e94-5a22-486d-8750-991a3a9b13c6 +// Reference: b5ea56df-669e-43cc-9537-14aa172f5dfc notebook.af-south-1.sagemaker.aws notebook.ap-east-1.sagemaker.aws notebook.ap-northeast-1.sagemaker.aws @@ -11799,6 +11843,9 @@ notebook.ap-southeast-2.sagemaker.aws notebook.ap-southeast-3.sagemaker.aws notebook.ap-southeast-4.sagemaker.aws notebook.ca-central-1.sagemaker.aws +notebook-fips.ca-central-1.sagemaker.aws +notebook.ca-west-1.sagemaker.aws +notebook-fips.ca-west-1.sagemaker.aws notebook.eu-central-1.sagemaker.aws notebook.eu-central-2.sagemaker.aws notebook.eu-north-1.sagemaker.aws @@ -11820,6 +11867,7 @@ notebook-fips.us-gov-east-1.sagemaker.aws notebook.us-gov-west-1.sagemaker.aws notebook-fips.us-gov-west-1.sagemaker.aws notebook.us-west-1.sagemaker.aws +notebook-fips.us-west-1.sagemaker.aws notebook.us-west-2.sagemaker.aws notebook-fips.us-west-2.sagemaker.aws notebook.cn-north-1.sagemaker.com.cn @@ -11827,7 +11875,7 @@ notebook.cn-northwest-1.sagemaker.com.cn // Amazon SageMaker Studio // Submitted by AWS Security -// Reference: 057ee397-6bf8-4f20-b807-d7bc145ac980 +// Reference: 69c723d9-6e1a-4bff-a203-48eecd203183 studio.af-south-1.sagemaker.aws studio.ap-east-1.sagemaker.aws studio.ap-northeast-1.sagemaker.aws @@ -11841,6 +11889,7 @@ studio.ca-central-1.sagemaker.aws studio.eu-central-1.sagemaker.aws studio.eu-north-1.sagemaker.aws studio.eu-south-1.sagemaker.aws +studio.eu-south-2.sagemaker.aws studio.eu-west-1.sagemaker.aws studio.eu-west-2.sagemaker.aws studio.eu-west-3.sagemaker.aws @@ -11885,7 +11934,7 @@ analytics-gateway.us-west-2.amazonaws.com // AWS Cloud9 // Submitted by: AWS Security -// Reference: 05c44955-977c-4b57-938a-f2af92733f9f +// Reference: 30717f72-4007-4f0f-8ed4-864c6f2efec9 webview-assets.aws-cloud9.af-south-1.amazonaws.com vfs.cloud9.af-south-1.amazonaws.com webview-assets.cloud9.af-south-1.amazonaws.com @@ -11931,6 +11980,8 @@ webview-assets.cloud9.eu-west-2.amazonaws.com webview-assets.aws-cloud9.eu-west-3.amazonaws.com vfs.cloud9.eu-west-3.amazonaws.com webview-assets.cloud9.eu-west-3.amazonaws.com +webview-assets.aws-cloud9.il-central-1.amazonaws.com +vfs.cloud9.il-central-1.amazonaws.com webview-assets.aws-cloud9.me-south-1.amazonaws.com vfs.cloud9.me-south-1.amazonaws.com webview-assets.cloud9.me-south-1.amazonaws.com @@ -11950,6 +12001,11 @@ webview-assets.aws-cloud9.us-west-2.amazonaws.com vfs.cloud9.us-west-2.amazonaws.com webview-assets.cloud9.us-west-2.amazonaws.com +// AWS Directory Service +// Submitted by AWS Security +// Reference: a13203e8-42dc-4045-a0d2-2ee67bed1068 +awsapps.com + // AWS Elastic Beanstalk // Submitted by AWS Security // Reference: bb5a965c-dec3-4967-aa22-e306ad064797 @@ -11993,6 +12049,11 @@ us-west-2.elasticbeanstalk.com // Reference: d916759d-a08b-4241-b536-4db887383a6a awsglobalaccelerator.com +// AWS re:Post Private +// Submitted by AWS Security +// Reference: 83385945-225f-416e-9aa0-ad0632bfdcee +*.private.repost.aws + // eero // Submitted by Yue Kang // Reference: 264afe70-f62c-4c02-8ab9-b5281ed24461 @@ -12010,6 +12071,10 @@ tele.amune.org // Submitted by Apigee Security Team apigee.io +// Apis Networks: https://apisnetworks.com +// Submitted by Matt Saladna +panel.dev + // Apphud : https://apphud.com // Submitted by Alexander Selivanov siiites.com @@ -12027,6 +12092,10 @@ appudo.net // Submitted by Thomas Orozco on-aptible.com +// Aquapal : https://aquapal.net/ +// Submitted by Aki Ueno +f5.si + // ASEINet : https://www.aseinet.com/ // Submitted by Asei SEKIGUCHI user.aseinet.ne.jp @@ -12062,6 +12131,7 @@ autocode.dev // AVM : https://avm.de // Submitted by Andreas Weise +myfritz.link myfritz.net // AVStack Pte. Ltd. : https://avstack.io @@ -12121,10 +12191,18 @@ beagleboard.io // Submitted by Lev Nekrasov *.beget.app +// Besties : https://besties.house +// Submitted by Hazel Cora +pages.gay + // BetaInABox // Submitted by Adrian betainabox.com +// University of Bielsko-Biala regional domain: http://dns.bielsko.pl/ +// Submitted by Marcin +bielsko.pl + // BinaryLane : http://www.binarylane.com // Submitted by Nathan O'Sullivan bnr.la @@ -12166,8 +12244,13 @@ square7.de bplaced.net square7.net +// Brave : https://brave.com +// Submitted by Andrea Brancaleoni +*.s.brave.io + // Brendly : https://brendly.rs -// Submitted by Dusan Radovanovic +// Submitted by Dusan Radovanovic +shop.brendly.hr shop.brendly.rs // BrowserSafetyMark @@ -12191,7 +12274,9 @@ mycd.eu // Canva Pty Ltd : https://canva.com/ // Submitted by Joel Aquilina canva-apps.cn +*.my.canvasite.cn canva-apps.com +*.my.canva.site // Carrd : https://carrd.co // Submitted by AJ @@ -12329,6 +12414,12 @@ pages.dev r2.dev workers.dev +// cloudscale.ch AG : https://www.cloudscale.ch/ +// Submitted by Gaudenz Steinlin +cust.cloudscale.ch +objects.lpg.cloudscale.ch +objects.rma.cloudscale.ch + // Clovyr : https://clovyr.io // Submitted by Patrick Nielsen wnext.app @@ -12346,22 +12437,33 @@ co.cz // CDN77.com : http://www.cdn77.com // Submitted by Jan Krpes -c.cdn77.org +cdn77-storage.com +rsc.contentproxy9.cz cdn77-ssl.net r.cdn77.net -rsc.cdn77.org ssl.origin.cdn77-secure.org +c.cdn77.org +rsc.cdn77.org // Cloud DNS Ltd : http://www.cloudns.net -// Submitted by Aleksander Hristov +// Submitted by Aleksander Hristov & Boyan Peychev cloudns.asia +cloudns.be cloudns.biz -cloudns.club cloudns.cc +cloudns.ch +cloudns.cl +cloudns.club +dnsabr.com +cloudns.cx cloudns.eu cloudns.in cloudns.info +dns-cloud.net +dns-dynamic.net +cloudns.nz cloudns.org +cloudns.ph cloudns.pro cloudns.pw cloudns.us @@ -12374,6 +12476,11 @@ cnpy.gdn // Submitted by Moritz Marquardt codeberg.page +// CodeSandbox B.V. : https://codesandbox.io +// Submitted by Ives van Hoorne +csb.app +preview.csb.app + // CoDNS B.V. co.nl co.no @@ -12383,6 +12490,10 @@ co.no webhosting.be hosting-cluster.nl +// Convex : https://convex.dev/ +// Submitted by James Cowling +convex.site + // Coordination Center for TLD RU and XN--P1AI : https://cctld.ru/en/domains/domens_ru/reserved/ // Submitted by George Georgievsky ac.ru @@ -12404,10 +12515,18 @@ feste-ip.net knx-server.net static-access.net +// cPanel L.L.C. : https://www.cpanel.net/ +// Submitted by Dustin Scherer +*.cprapid.com + // Craynic, s.r.o. : http://www.craynic.com/ // Submitted by Ales Krajnik realm.cz +// Crisp IM SAS : https://crisp.chat/ +// Submitted by Baptiste Jamin +on.crisp.email + // Cryptonomic : https://cryptonomic.net/ // Submitted by Andrew Cady *.cryptonomic.net @@ -12428,6 +12547,13 @@ curv.dev *.ocp.customer-oci.com *.ocs.customer-oci.com +// Cyclic Software : https://www.cyclic.sh +// Submitted by Kam Lasater +cyclic.app +cyclic.cloud +cyclic-app.com +cyclic.co.in + // cyon GmbH : https://www.cyon.ch/ // Submitted by Dominic Luechinger cyon.link @@ -12473,6 +12599,7 @@ dyndns.dappnode.io // Dark, Inc. : https://darklang.com // Submitted by Paul Biggar builtwithdark.com +darklang.io // DataDetect, LLC. : https://datadetect.com // Submitted by Andrew Banchich @@ -12871,6 +12998,10 @@ ondigitalocean.app // Submitted by Robin H. Johnson *.digitaloceanspaces.com +// DigitalPlat : https://www.digitalplat.org/ +// Submitted by Edward Hsing +us.kg + // dnstrace.pro : https://dnstrace.pro/ // Submitted by Chris Partridge bci.dnstrace.pro @@ -12908,6 +13039,18 @@ e4.cz easypanel.app easypanel.host +// EasyWP : https://www.easywp.com +// Submitted by +*.ewp.live + +// Electromagnetic Field : https://www.emfcamp.org +// Submitted by +at.emf.camp + +// Elefunc, Inc. : https://elefunc.com +// Submitted by Cetin Sert +rt.ht + // Elementor : Elementor Ltd. // Submitted by Anton Barkan elementor.cloud @@ -13022,10 +13165,6 @@ url.tw // Submitted by Eric Jiang onfabrica.com -// Facebook, Inc. -// Submitted by Peter Ruibal -apps.fbsbx.com - // FAITID : https://faitid.org/ // Submitted by Maxim Alzoba // https://www.flexireg.net/stat_info @@ -13183,6 +13322,10 @@ flap.id onflashdrive.app fldrv.com +// FlutterFlow : https://flutterflow.io +// Submitted by Anton Emelyanov +flutterflow.app + // fly.io: https://fly.io // Submitted by Kurt Mackey fly.dev @@ -13193,13 +13336,14 @@ shw.io // Submitted by Jonathan Rudenberg flynnhosting.net -// Forgerock : https://www.forgerock.com +// Forgerock : https://www.forgerock.com // Submitted by Roderick Parr forgeblocks.com id.forgerock.io // Framer : https://www.framer.com -// Submitted by Koen Rouwhorst +// Submitted by Koen Rouwhorst +framer.ai framer.app framercanvas.com framer.media @@ -13240,6 +13384,24 @@ freemyip.com // Submitted by Daniel A. Maierhofer wien.funkfeuer.at +// Future Versatile Group. :https://www.fvg-on.net/ +// T.Kabu +daemon.asia +dix.asia +mydns.bz +0am.jp +0g0.jp +0j0.jp +0t0.jp +mydns.jp +pgw.jp +wjg.jp +keyword-on.net +live-on.net +server-on.net +mydns.tw +mydns.vc + // Futureweb GmbH : https://www.futureweb.at // Submitted by Andreas Schnederle-Wagner *.futurecms.at @@ -13251,6 +13413,10 @@ futuremailing.at *.kunden.ortsinfo.at *.statics.cloud +// GCom Internet : https://www.gcom.net.au +// Submitted by Leo Julius +aliases121.com + // GDS : https://www.gov.uk/service-manual/technology/managing-domain-names // Submitted by Stephen Ford independent-commission.uk @@ -13279,9 +13445,11 @@ gentlentapis.com lab.ms cdn-edges.net -// Ghost Foundation : https://ghost.org -// Submitted by Matt Hanley -ghost.io +// Getlocalcert: https://www.getlocalcert.net +// Submitted by Robert Alexander +localcert.net +localhostcert.net +corpnet.work // GignoSystemJapan: http://gsj.bz // Submitted by GignoSystemJapan @@ -13425,6 +13593,10 @@ whitesnow.jp zombie.jp heteml.net +// GoDaddy Registry : https://registry.godaddy +// Submitted by Rohan Durrant +graphic.design + // GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/ // Submitted by Tom Whitwell cloudapps.digital @@ -13434,10 +13606,6 @@ london.cloudapps.digital // Submitted by Richard Baker pymnt.uk -// UKHomeOffice : https://www.gov.uk/government/organisations/home-office -// Submitted by Jon Shanks -homeoffice.gov.uk - // GlobeHosting, Inc. // Submitted by Zoltan Egresi ro.im @@ -13448,8 +13616,7 @@ goip.de // Google, Inc. // Submitted by Eduardo Vela -run.app -a.run.app +*.run.app web.app *.0emm.com appspot.com @@ -13549,6 +13716,10 @@ goupile.fr // Submitted by gov.nl +// GrayJay Web Solutions Inc. : https://grayjaysports.ca +// Submitted by Matt Yamkowy +grayjayleagues.com + // Group 53, LLC : https://www.group53.com // Submitted by Tyler Todd awsmppl.com @@ -13583,6 +13754,11 @@ hasura-app.io // Submitted by Richard Zowalla pages.it.hs-heilbronn.de +// Helio Networks : https://heliohost.org +// Submitted by Ben Frede +helioho.st +heliohost.us + // Hepforge : https://www.hepforge.org // Submitted by David Grellscheid hepforge.org @@ -13596,7 +13772,6 @@ herokussl.com // Submitted by Oren Eini ravendb.cloud ravendb.community -ravendb.me development.run ravendb.run @@ -13604,6 +13779,12 @@ ravendb.run // Submitted by Krzysztof Wolski homesklep.pl +// Homebase : https://homebase.id/ +// Submitted by Jason Babo +*.kin.one +*.id.pub +*.kin.pub + // Hong Kong Productivity Council: https://www.hkpc.org/ // Submitted by SECaaS Team secaas.hk @@ -13681,7 +13862,7 @@ biz.at info.at // info.cx : http://info.cx -// Submitted by Jacob Slater +// Submitted by June Slater info.cx // Interlegis : http://www.interlegis.leg.br @@ -13730,6 +13911,14 @@ iopsys.se // Submitted by Matthew Hardeman ipifony.net +// is-a.dev : https://www.is-a.dev +// Submitted by William Harrison +is-a.dev + +// ir.md : https://nic.ir.md +// Submitted by Ali Soizi +ir.md + // IServ GmbH : https://iserv.de // Submitted by Mario Hoberg iservschule.de @@ -13838,6 +14027,11 @@ myjino.ru // Submitted by Daniel Fariña jotelulu.cloud +// JouwWeb B.V. : https://www.jouwweb.nl +// Submitted by Camilo Sperberg +jouwweb.site +webadorsite.com + // Joyent : https://www.joyent.com/ // Submitted by Brian Bennett *.triton.zone @@ -13911,6 +14105,10 @@ lpusercontent.com // Submitted by Lelux Admin lelux.site +// Libre IT Ltd : https://libre.nz +// Submitted by Tomas Maggio +runcontainers.dev + // Lifetime Hosting : https://Lifetime.Hosting/ // Submitted by Mike Fillator co.business @@ -13921,10 +14119,6 @@ co.network co.place co.technology -// Lightmaker Property Manager, Inc. : https://app.lmpm.com/ -// Submitted by Greg Holland -app.lmpm.com - // linkyard ldt: https://www.linkyard.ch/ // Submitted by Mario Siegenthaler linkyard.cloud @@ -14057,6 +14251,11 @@ memset.net // Submitted by Ruben Schmidmeister messerli.app +// Meta Platforms, Inc. : https://meta.com/ +// Submitted by Jacob Cordero +atmeta.com +apps.fbsbx.com + // MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/ // Submitted by Zdeněk Šustr *.cloud.metacentrum.cz @@ -14077,10 +14276,14 @@ co.pl // Microsoft Corporation : http://microsoft.com // Submitted by Public Suffix List Admin +// Managed by Corporate Domains +// Microsoft Azure : https://home.azure *.azurecontainer.io +azure-api.net +azureedge.net +azurefd.net azurewebsites.net azure-mobile.net -cloudapp.net azurestaticapps.net 1.azurestaticapps.net 2.azurestaticapps.net @@ -14094,6 +14297,10 @@ eastasia.azurestaticapps.net eastus2.azurestaticapps.net westeurope.azurestaticapps.net westus2.azurestaticapps.net +cloudapp.net +trafficmanager.net +blob.core.windows.net +servicebus.windows.net // minion.systems : http://minion.systems // Submitted by Robert Böttinger @@ -14107,6 +14314,10 @@ mintere.site // Submitted by Grayson Martin forte.id +// MODX Systems LLC : https://modx.com +// Submitted by Elizabeth Southwell +modx.dev + // Mozilla Corporation : https://mozilla.com // Submitted by Ben Francis mozilla-iot.org @@ -14153,6 +14364,10 @@ netlify.app // Submitted by Trung Tran 4u.com +// NGO.US Registry : https://nic.ngo.us +// Submitted by Alstra Solutions Ltd. Networking Team +ngo.us + // ngrok : https://ngrok.com/ // Submitted by Alan Shreve ngrok.app @@ -14168,18 +14383,24 @@ jp.ngrok.io sa.ngrok.io us.ngrok.io ngrok.pizza +ngrok.pro // Nicolaus Copernicus University in Torun - MSK TORMAN (https://www.man.torun.pl) torun.pl // Nimbus Hosting Ltd. : https://www.nimbushosting.co.uk/ -// Submitted by Nicholas Ford +// Submitted by Nicholas Ford nh-serv.co.uk +nimsite.uk // NFSN, Inc. : https://www.NearlyFreeSpeech.NET/ // Submitted by Jeff Wheelhouse nfshost.com +// NFT.Storage : https://nft.storage/ +// Submitted by Vasco Santos or +ipfs.nftstorage.link + // Noop : https://noop.app // Submitted by Nathaniel Schweinberg *.developer.app @@ -14330,6 +14551,10 @@ pcloud.host // Submitted by Matthew Brown nyc.mn +// O3O.Foundation : https://o3o.foundation/ +// Submitted by the prvcy.page Registry Team +prvcy.page + // Observable, Inc. : https://observablehq.com // Submitted by Mike Bostock static.observableusercontent.com @@ -14359,7 +14584,6 @@ omniwe.site 123minsida.se 123miweb.es 123paginaweb.pt -123sait.ru 123siteweb.fr 123webseite.at 123webseite.de @@ -14377,6 +14601,13 @@ simplesite.pl // Submitted by Eddie Jones nid.io +// Open Domains : https://open-domains.net +// Submitted by William Harrison +is-cool.dev +is-not-a.dev +localplayer.dev +is-local.org + // Open Social : https://www.getopensocial.com/ // Submitted by Alexander Varwijk opensocial.site @@ -14397,6 +14628,11 @@ operaunite.com // Submitted by Alexandre Linte tech.orange +// OsSav Technology Ltd. : https://ossav.com/ +// TLD Nic: http://nic.can.re - TLD Whois Server: whois.can.re +// Submitted by OsSav Technology Ltd. +can.re + // Oursky Limited : https://authgear.com/, https://skygear.io/ // Submitted by Authgear Team , Skygear Developer authgear-staging.com @@ -14447,10 +14683,11 @@ pagexl.com // pcarrier.ca Software Inc: https://pcarrier.ca/ // Submitted by Pierre Carrier -bar0.net -bar1.net -bar2.net -rdv.to +*.xmit.co +xmit.dev +srv.us +gh.srv.us +gl.srv.us // .pl domains (grandfathered) art.pl @@ -14483,7 +14720,8 @@ on-web.fr // Platform.sh : https://platform.sh // Submitted by Nikola Kotur -bc.platform.sh +*.upsun.app +upsunapp.com ent.platform.sh eu.platform.sh us.platform.sh @@ -14502,6 +14740,10 @@ pdns.page plesk.page pleskns.com +// Pley AB : https://www.pley.com/ +// Submitted by Henning Pohl +pley.games + // Port53 : https://port53.io/ // Submitted by Maximilian Schieder dyn53.io @@ -14533,10 +14775,6 @@ xen.prgmr.com // Submitted by registry priv.at -// privacytools.io : https://www.privacytools.io/ -// Submitted by Jonah Aragon -prvcy.page - // Protocol Labs : https://protocol.ai/ // Submitted by Michael Burns *.dweb.link @@ -14578,6 +14816,8 @@ qbuser.com // Rad Web Hosting: https://radwebhosting.com // Submitted by Scott Claeys cloudsite.builders +myradweb.net +servername.us // Redgate Software: https://red-gate.com // Submitted by Andrew Farries @@ -14601,9 +14841,12 @@ qcx.io *.sys.qcx.io // QNAP System Inc : https://www.qnap.com -// Submitted by Nick Chang -dev-myqnapcloud.com +// Submitted by Nick Chang +myqnapcloud.cn alpha-myqnapcloud.com +dev-myqnapcloud.com +mycloudnas.com +mynascloud.com myqnapcloud.com // Quip : https://quip.com @@ -14644,11 +14887,40 @@ app.render.com onrender.com // Repl.it : https://repl.it -// Submitted by Lincoln Bergeson +// Submitted by Lincoln Bergeson +replit.app +id.replit.app firewalledreplit.co id.firewalledreplit.co repl.co id.repl.co +replit.dev +archer.replit.dev +bones.replit.dev +canary.replit.dev +global.replit.dev +hacker.replit.dev +id.replit.dev +janeway.replit.dev +kim.replit.dev +kira.replit.dev +kirk.replit.dev +odo.replit.dev +paris.replit.dev +picard.replit.dev +pike.replit.dev +prerelease.replit.dev +reed.replit.dev +riker.replit.dev +sisko.replit.dev +spock.replit.dev +staging.replit.dev +sulu.replit.dev +tarpit.replit.dev +teams.replit.dev +tucker.replit.dev +wesley.replit.dev +worf.replit.dev repl.run // Resin.io : https://resin.io @@ -14745,10 +15017,11 @@ from.tv sakura.tv // Salesforce.com, Inc. https://salesforce.com/ -// Submitted by Michael Biven +// Submitted by Michael Biven and Aaron Romeo *.builder.code.com *.dev-builder.code.com *.stg-builder.code.com +*.001.test.code-builder-stg.platform.salesforce.com // Sandstorm Development Group, Inc. : https://sandcats.io/ // Submitted by Asheesh Laroia @@ -14764,6 +15037,7 @@ logoip.com fr-par-1.baremetal.scw.cloud fr-par-2.baremetal.scw.cloud nl-ams-1.baremetal.scw.cloud +cockpit.fr-par.scw.cloud fnc.fr-par.scw.cloud functions.fnc.fr-par.scw.cloud k8s.fr-par.scw.cloud @@ -14774,11 +15048,13 @@ whm.fr-par.scw.cloud priv.instances.scw.cloud pub.instances.scw.cloud k8s.scw.cloud +cockpit.nl-ams.scw.cloud k8s.nl-ams.scw.cloud nodes.k8s.nl-ams.scw.cloud s3.nl-ams.scw.cloud s3-website.nl-ams.scw.cloud whm.nl-ams.scw.cloud +cockpit.pl-waw.scw.cloud k8s.pl-waw.scw.cloud nodes.k8s.pl-waw.scw.cloud s3.pl-waw.scw.cloud @@ -14800,6 +15076,10 @@ service.gov.scot // Submitted by Shante Adam scrysec.com +// Scrypted : https://scrypted.app +// Submitted by Koushik Dutta +client.scrypted.io + // Securepoint GmbH : https://www.securepoint.de // Submitted by Erik Anders firewall-gateway.com @@ -14839,6 +15119,10 @@ biz.ua co.ua pp.ua +// Sheezy.Art : https://sheezy.art +// Submitted by Nyoom +sheezy.games + // Shift Crypto AG : https://shiftcrypto.ch // Submitted by alex shiftcrypto.dev @@ -14887,7 +15171,7 @@ alpha.bounty-full.com beta.bounty-full.com // Smallregistry by Promopixel SARL: https://www.smallregistry.net -// Former AFNIC's SLDs +// Former AFNIC's SLDs // Submitted by Jérôme Lipowicz aeroport.fr avocat.fr @@ -14909,9 +15193,9 @@ small-web.org vp4.me // Snowflake Inc : https://www.snowflake.com/ -// Submitted by Faith Olapade -snowflake.app -privatelink.snowflake.app +// Submitted by Sam Haar +*.snowflake.app +*.privatelink.snowflake.app streamlit.app streamlitapp.com @@ -14923,10 +15207,24 @@ try-snowplow.com // Submitted by Drew DeVault srht.site +// StackBlitz : https://stackblitz.com +// Submitted by Dominic Elm +w-corp-staticblitz.com +w-credentialless-staticblitz.com +w-staticblitz.com + // Stackhero : https://www.stackhero.io // Submitted by Adrien Gillon stackhero-network.com +// STACKIT : https://www.stackit.de/en/ +// Submitted by STACKIT-DNS Team (Simon Stier) +runs.onstackit.cloud +stackit.gg +stackit.rocks +stackit.run +stackit.zone + // Staclar : https://staclar.com // Submitted by Q Misell musician.io @@ -14993,6 +15291,19 @@ myspreadshop.co.uk // Submitted by Jacob Lee api.stdlib.com +// stereosense GmbH : https://www.involve.me +// Submitted by Florian Burmann +feedback.ac +forms.ac +assessments.cx +calculators.cx +funnels.cx +paynow.cx +quizzes.cx +researched.cx +tests.cx +surveys.so + // Storipress : https://storipress.com // Submitted by Benno Liu storipress.app @@ -15001,6 +15312,12 @@ storipress.app // Submitted by Philip Hutchins storj.farm +// Streak : https://streak.com +// Submitted by Blake Kadatz +streak-link.com +streaklinks.com +streakusercontent.com + // Studenten Net Twente : http://www.snt.utwente.nl/ // Submitted by Silke Hofstra utwente.io @@ -15063,6 +15380,7 @@ taifun-dns.de // Submitted by David Anderson beta.tailscale.net ts.net +*.c.ts.net // TASK geographical domains (www.task.gda.pl/uslugi/dns) gda.pl @@ -15196,6 +15514,10 @@ inc.hk // Submitted by ITComdomains it.com +// Unison Computing, PBC : https://unison.cloud +// Submitted by Simon Højberg +unison-services.cloud + // UNIVERSAL DOMAIN REGISTRY : https://www.udr.org.yt/ // see also: whois -h whois.udr.org.yt help // Submitted by Atanunu Igbunuroghene @@ -15245,48 +15567,6 @@ v-info.info // Submitted by Nathan van Bakel voorloper.cloud -// Voxel.sh DNS : https://voxel.sh/dns/ -// Submitted by Mia Rehlinger -neko.am -nyaa.am -be.ax -cat.ax -es.ax -eu.ax -gg.ax -mc.ax -us.ax -xy.ax -nl.ci -xx.gl -app.gp -blog.gt -de.gt -to.gt -be.gy -cc.hn -blog.kg -io.kg -jp.kg -tv.kg -uk.kg -us.kg -de.ls -at.md -de.md -jp.md -to.md -indie.porn -vxl.sh -ch.tc -me.tc -we.tc -nyan.to -at.vg -blog.vu -dev.vu -me.vu - // V.UA Domain Administrator : https://domain.v.ua/ // Submitted by Serhii Rostilo v.ua @@ -15299,6 +15579,11 @@ v.ua // Submitted by Masayuki Note wafflecell.com +// Webflow, Inc. : https://www.webflow.com +// Submitted by Webflow Security Team +webflow.io +webflowtest.io + // WebHare bv: https://www.webhare.com/ // Submitted by Arnold Hendriks *.webhare.dev @@ -15310,6 +15595,10 @@ reserve-online.com bookonline.app hotelwithflight.com +// WebWaddle Ltd: https://webwaddle.com/ +// Submitted by Merlin Glander +*.wadl.top + // WeDeploy by Liferay, Inc. : https://www.wedeploy.com // Submitted by Henrique Vicente wedeploy.io @@ -15418,6 +15707,10 @@ noho.st za.net za.org +// ZAP-Hosting GmbH & Co. KG : https://zap-hosting.com +// Submitted by Julian Alker +zap.cloud + // Zine EOOD : https://zine.bg/ // Submitted by Martin Angelov bss.design diff --git a/make/data/tzdata/VERSION b/make/data/tzdata/VERSION index b138ed7fa78f5..9c056fac34575 100644 --- a/make/data/tzdata/VERSION +++ b/make/data/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2024a +tzdata2025a diff --git a/make/data/tzdata/africa b/make/data/tzdata/africa index 72b188f074deb..8098f4bea9d30 100644 --- a/make/data/tzdata/africa +++ b/make/data/tzdata/africa @@ -126,17 +126,16 @@ Zone Africa/Algiers 0:12:12 - LMT 1891 Mar 16 # Cape Verde / Cabo Verde # -# From Paul Eggert (2018-02-16): -# Shanks gives 1907 for the transition to +02. -# For now, ignore that and follow the 1911-05-26 Portuguese decree -# (see Europe/Lisbon). +# From Tim Parenti (2024-07-01), per Paul Eggert (2018-02-16): +# For timestamps before independence, see commentary for Europe/Lisbon. +# Shanks gives 1907 instead for the transition to -02. # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Atlantic/Cape_Verde -1:34:04 - LMT 1912 Jan 01 2:00u # Praia - -2:00 - -02 1942 Sep - -2:00 1:00 -01 1945 Oct 15 - -2:00 - -02 1975 Nov 25 2:00 - -1:00 - -01 + -2:00 - %z 1942 Sep + -2:00 1:00 %z 1945 Oct 15 + -2:00 - %z 1975 Nov 25 2:00 + -1:00 - %z # Chad # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -368,14 +367,12 @@ Zone Africa/Cairo 2:05:09 - LMT 1900 Oct # Guinea-Bissau # -# From Paul Eggert (2018-02-16): -# Shanks gives 1911-05-26 for the transition to WAT, -# evidently confusing the date of the Portuguese decree -# (see Europe/Lisbon) with the date that it took effect. +# From Tim Parenti (2024-07-01), per Paul Eggert (2018-02-16): +# For timestamps before independence, see commentary for Europe/Lisbon. # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Bissau -1:02:20 - LMT 1912 Jan 1 1:00u - -1:00 - -01 1975 + -1:00 - %z 1975 0:00 - GMT # Comoros @@ -440,10 +437,10 @@ Zone Africa/Bissau -1:02:20 - LMT 1912 Jan 1 1:00u # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Nairobi 2:27:16 - LMT 1908 May - 2:30 - +0230 1928 Jun 30 24:00 + 2:30 - %z 1928 Jun 30 24:00 3:00 - EAT 1930 Jan 4 24:00 - 2:30 - +0230 1936 Dec 31 24:00 - 2:45 - +0245 1942 Jul 31 24:00 + 2:30 - %z 1936 Dec 31 24:00 + 2:45 - %z 1942 Jul 31 24:00 3:00 - EAT # Liberia @@ -614,7 +611,7 @@ Rule Mauritius 2008 only - Oct lastSun 2:00 1:00 - Rule Mauritius 2009 only - Mar lastSun 2:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis - 4:00 Mauritius +04/+05 + 4:00 Mauritius %z # Agalega Is, Rodriguez # no information; probably like Indian/Mauritius @@ -1094,10 +1091,10 @@ Rule Morocco 2087 only - May 11 2:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26 - 0:00 Morocco +00/+01 1984 Mar 16 - 1:00 - +01 1986 - 0:00 Morocco +00/+01 2018 Oct 28 3:00 - 1:00 Morocco +01/+00 + 0:00 Morocco %z 1984 Mar 16 + 1:00 - %z 1986 + 0:00 Morocco %z 2018 Oct 28 3:00 + 1:00 Morocco %z # Western Sahara # @@ -1111,9 +1108,9 @@ Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26 # since most of it was then controlled by Morocco. Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún - -1:00 - -01 1976 Apr 14 - 0:00 Morocco +00/+01 2018 Oct 28 3:00 - 1:00 Morocco +01/+00 + -1:00 - %z 1976 Apr 14 + 0:00 Morocco %z 2018 Oct 28 3:00 + 1:00 Morocco %z # Botswana # Burundi @@ -1124,13 +1121,27 @@ Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún # Zambia # Zimbabwe # -# Shanks gives 1903-03-01 for the transition to CAT. -# Perhaps the 1911-05-26 Portuguese decree -# https://dre.pt/pdf1sdip/1911/05/12500/23132313.pdf -# merely made it official? +# From Tim Parenti (2024-07-01): +# For timestamps before Mozambique's independence, see commentary for +# Europe/Lisbon. +# +# From Paul Eggert (2024-05-24): +# The London Gazette, 1903-04-03, page 2245, says that +# as of 1903-03-03 a time ball at the port of Lourenço Marques +# (as Maputo was then called) was dropped daily at 13:00:00 LMT, +# corresponding to 22:49:41.7 GMT, so local time was +02:10:18.3. +# Conversely, the newspaper South Africa, 1909-02-09, page 321, +# says the port had just installed an apparatus that communicated +# "from the controlling clock in the new Observatory at Reuben Point ... +# exact mean South African time, i.e., 30 deg., or 2 hours East of Greenwich". +# Although Shanks gives 1903-03-01 for the transition to CAT, +# evidently the port transitioned to CAT after 1903-03-03 but before +# the Portuguese legal transition of 1912-01-01 (see Europe/Lisbon commentary). +# For lack of better info, list 1909 as the transition date. # # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Africa/Maputo 2:10:20 - LMT 1903 Mar + #STDOFF 2:10:18.3 +Zone Africa/Maputo 2:10:18 - LMT 1909 2:00 - CAT # Namibia @@ -1195,7 +1206,7 @@ Rule Namibia 1995 2017 - Apr Sun>=1 2:00 -1:00 WAT # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 - 1:30 - +0130 1903 Mar + 1:30 - %z 1903 Mar 2:00 - SAST 1942 Sep 20 2:00 2:00 1:00 SAST 1943 Mar 21 2:00 2:00 - SAST 1990 Mar 21 # independence @@ -1283,7 +1294,7 @@ Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 Zone Africa/Lagos 0:13:35 - LMT 1905 Jul 1 0:00 - GMT 1908 Jul 1 0:13:35 - LMT 1914 Jan 1 - 0:30 - +0030 1919 Sep 1 + 0:30 - %z 1919 Sep 1 1:00 - WAT # São Tomé and Príncipe diff --git a/make/data/tzdata/antarctica b/make/data/tzdata/antarctica index fc7176cd0d57a..87787d31cfef6 100644 --- a/make/data/tzdata/antarctica +++ b/make/data/tzdata/antarctica @@ -110,34 +110,34 @@ # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Antarctica/Casey 0 - -00 1969 - 8:00 - +08 2009 Oct 18 2:00 - 11:00 - +11 2010 Mar 5 2:00 - 8:00 - +08 2011 Oct 28 2:00 - 11:00 - +11 2012 Feb 21 17:00u - 8:00 - +08 2016 Oct 22 - 11:00 - +11 2018 Mar 11 4:00 - 8:00 - +08 2018 Oct 7 4:00 - 11:00 - +11 2019 Mar 17 3:00 - 8:00 - +08 2019 Oct 4 3:00 - 11:00 - +11 2020 Mar 8 3:00 - 8:00 - +08 2020 Oct 4 0:01 - 11:00 - +11 2021 Mar 14 0:00 - 8:00 - +08 2021 Oct 3 0:01 - 11:00 - +11 2022 Mar 13 0:00 - 8:00 - +08 2022 Oct 2 0:01 - 11:00 - +11 2023 Mar 9 3:00 - 8:00 - +08 + 8:00 - %z 2009 Oct 18 2:00 + 11:00 - %z 2010 Mar 5 2:00 + 8:00 - %z 2011 Oct 28 2:00 + 11:00 - %z 2012 Feb 21 17:00u + 8:00 - %z 2016 Oct 22 + 11:00 - %z 2018 Mar 11 4:00 + 8:00 - %z 2018 Oct 7 4:00 + 11:00 - %z 2019 Mar 17 3:00 + 8:00 - %z 2019 Oct 4 3:00 + 11:00 - %z 2020 Mar 8 3:00 + 8:00 - %z 2020 Oct 4 0:01 + 11:00 - %z 2021 Mar 14 0:00 + 8:00 - %z 2021 Oct 3 0:01 + 11:00 - %z 2022 Mar 13 0:00 + 8:00 - %z 2022 Oct 2 0:01 + 11:00 - %z 2023 Mar 9 3:00 + 8:00 - %z Zone Antarctica/Davis 0 - -00 1957 Jan 13 - 7:00 - +07 1964 Nov + 7:00 - %z 1964 Nov 0 - -00 1969 Feb - 7:00 - +07 2009 Oct 18 2:00 - 5:00 - +05 2010 Mar 10 20:00u - 7:00 - +07 2011 Oct 28 2:00 - 5:00 - +05 2012 Feb 21 20:00u - 7:00 - +07 + 7:00 - %z 2009 Oct 18 2:00 + 5:00 - %z 2010 Mar 10 20:00u + 7:00 - %z 2011 Oct 28 2:00 + 5:00 - %z 2012 Feb 21 20:00u + 7:00 - %z Zone Antarctica/Mawson 0 - -00 1954 Feb 13 - 6:00 - +06 2009 Oct 18 2:00 - 5:00 - +05 + 6:00 - %z 2009 Oct 18 2:00 + 5:00 - %z # References: # Casey Weather (1998-02-26) # http://www.antdiv.gov.au/aad/exop/sfo/casey/casey_aws.html @@ -197,6 +197,8 @@ Zone Antarctica/Mawson 0 - -00 1954 Feb 13 # France & Italy - year-round base # Concordia, -750600+1232000, since 2005 +# https://en.wikipedia.org/wiki/Concordia_Station +# Can use Asia/Singapore, which it has agreed with since inception. # Germany - year-round base # Neumayer III, -704080-0081602, since 2009 @@ -313,10 +315,10 @@ Zone Antarctica/Troll 0 - -00 2005 Feb 12 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Antarctica/Vostok 0 - -00 1957 Dec 16 - 7:00 - +07 1994 Feb + 7:00 - %z 1994 Feb 0 - -00 1994 Nov - 7:00 - +07 2023 Dec 18 2:00 - 5:00 - +05 + 7:00 - %z 2023 Dec 18 2:00 + 5:00 - %z # S Africa - year-round bases # Marion Island, -4653+03752 @@ -349,7 +351,7 @@ Zone Antarctica/Vostok 0 - -00 1957 Dec 16 # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Antarctica/Rothera 0 - -00 1976 Dec 1 - -3:00 - -03 + -3:00 - %z # Uruguay - year round base # Artigas, King George Island, -621104-0585107 diff --git a/make/data/tzdata/asia b/make/data/tzdata/asia index 3a54291919d60..b0a6fa01d2081 100644 --- a/make/data/tzdata/asia +++ b/make/data/tzdata/asia @@ -106,8 +106,8 @@ Rule RussiaAsia 1996 2010 - Oct lastSun 2:00s 0 - # Afghanistan # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Kabul 4:36:48 - LMT 1890 - 4:00 - +04 1945 - 4:30 - +0430 + 4:00 - %z 1945 + 4:30 - %z # Armenia # From Paul Eggert (2006-03-22): @@ -139,12 +139,12 @@ Rule Armenia 2011 only - Mar lastSun 2:00s 1:00 - Rule Armenia 2011 only - Oct lastSun 2:00s 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Yerevan 2:58:00 - LMT 1924 May 2 - 3:00 - +03 1957 Mar - 4:00 RussiaAsia +04/+05 1991 Mar 31 2:00s - 3:00 RussiaAsia +03/+04 1995 Sep 24 2:00s - 4:00 - +04 1997 - 4:00 RussiaAsia +04/+05 2011 - 4:00 Armenia +04/+05 + 3:00 - %z 1957 Mar + 4:00 RussiaAsia %z 1991 Mar 31 2:00s + 3:00 RussiaAsia %z 1995 Sep 24 2:00s + 4:00 - %z 1997 + 4:00 RussiaAsia %z 2011 + 4:00 Armenia %z # Azerbaijan @@ -165,12 +165,12 @@ Rule Azer 1997 2015 - Mar lastSun 4:00 1:00 - Rule Azer 1997 2015 - Oct lastSun 5:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Baku 3:19:24 - LMT 1924 May 2 - 3:00 - +03 1957 Mar - 4:00 RussiaAsia +04/+05 1991 Mar 31 2:00s - 3:00 RussiaAsia +03/+04 1992 Sep lastSun 2:00s - 4:00 - +04 1996 - 4:00 EUAsia +04/+05 1997 - 4:00 Azer +04/+05 + 3:00 - %z 1957 Mar + 4:00 RussiaAsia %z 1991 Mar 31 2:00s + 3:00 RussiaAsia %z 1992 Sep lastSun 2:00s + 4:00 - %z 1996 + 4:00 EUAsia %z 1997 + 4:00 Azer %z # Bangladesh # From Alexander Krivenyshev (2009-05-13): @@ -251,17 +251,17 @@ Rule Dhaka 2009 only - Dec 31 24:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Dhaka 6:01:40 - LMT 1890 5:53:20 - HMT 1941 Oct # Howrah Mean Time? - 6:30 - +0630 1942 May 15 - 5:30 - +0530 1942 Sep - 6:30 - +0630 1951 Sep 30 - 6:00 - +06 2009 - 6:00 Dhaka +06/+07 + 6:30 - %z 1942 May 15 + 5:30 - %z 1942 Sep + 6:30 - %z 1951 Sep 30 + 6:00 - %z 2009 + 6:00 Dhaka %z # Bhutan # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Thimphu 5:58:36 - LMT 1947 Aug 15 # or Thimbu - 5:30 - +0530 1987 Oct - 6:00 - +06 + 5:30 - %z 1987 Oct + 6:00 - %z # British Indian Ocean Territory # Whitman and the 1995 CIA time zone map say 5:00, but the @@ -271,8 +271,8 @@ Zone Asia/Thimphu 5:58:36 - LMT 1947 Aug 15 # or Thimbu # then contained the Chagos Archipelago). # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Indian/Chagos 4:49:40 - LMT 1907 - 5:00 - +05 1996 - 6:00 - +06 + 5:00 - %z 1996 + 6:00 - %z # Cocos (Keeling) Islands # Myanmar (Burma) @@ -288,9 +288,9 @@ Zone Indian/Chagos 4:49:40 - LMT 1907 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Yangon 6:24:47 - LMT 1880 # or Rangoon 6:24:47 - RMT 1920 # Rangoon local time - 6:30 - +0630 1942 May - 9:00 - +09 1945 May 3 - 6:30 - +0630 + 6:30 - %z 1942 May + 9:00 - %z 1945 May 3 + 6:30 - %z # China @@ -679,7 +679,7 @@ Zone Asia/Shanghai 8:05:43 - LMT 1901 # Xinjiang time, used by many in western China; represented by Ürümqi / Ürümchi # / Wulumuqi. (Please use Asia/Shanghai if you prefer Beijing time.) Zone Asia/Urumqi 5:50:20 - LMT 1928 - 6:00 - +06 + 6:00 - %z # Hong Kong @@ -1137,7 +1137,7 @@ Rule Macau 1979 only - Oct Sun>=16 03:30 0 S # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Macau 7:34:10 - LMT 1904 Oct 30 8:00 - CST 1941 Dec 21 23:00 - 9:00 Macau +09/+10 1945 Sep 30 24:00 + 9:00 Macau %z 1945 Sep 30 24:00 8:00 Macau C%sT @@ -1180,7 +1180,7 @@ Zone Asia/Nicosia 2:13:28 - LMT 1921 Nov 14 Zone Asia/Famagusta 2:15:48 - LMT 1921 Nov 14 2:00 Cyprus EE%sT 1998 Sep 2:00 EUAsia EE%sT 2016 Sep 8 - 3:00 - +03 2017 Oct 29 1:00u + 3:00 - %z 2017 Oct 29 1:00u 2:00 EUAsia EE%sT # Georgia @@ -1221,18 +1221,25 @@ Zone Asia/Famagusta 2:15:48 - LMT 1921 Nov 14 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Tbilisi 2:59:11 - LMT 1880 2:59:11 - TBMT 1924 May 2 # Tbilisi Mean Time - 3:00 - +03 1957 Mar - 4:00 RussiaAsia +04/+05 1991 Mar 31 2:00s - 3:00 RussiaAsia +03/+04 1992 - 3:00 E-EurAsia +03/+04 1994 Sep lastSun - 4:00 E-EurAsia +04/+05 1996 Oct lastSun - 4:00 1:00 +05 1997 Mar lastSun - 4:00 E-EurAsia +04/+05 2004 Jun 27 - 3:00 RussiaAsia +03/+04 2005 Mar lastSun 2:00 - 4:00 - +04 + 3:00 - %z 1957 Mar + 4:00 RussiaAsia %z 1991 Mar 31 2:00s + 3:00 RussiaAsia %z 1992 + 3:00 E-EurAsia %z 1994 Sep lastSun + 4:00 E-EurAsia %z 1996 Oct lastSun + 4:00 1:00 %z 1997 Mar lastSun + 4:00 E-EurAsia %z 2004 Jun 27 + 3:00 RussiaAsia %z 2005 Mar lastSun 2:00 + 4:00 - %z # East Timor +# From Tim Parenti (2024-07-01): +# The 1912-01-01 transition occurred at 00:00 new time, per the 1911-05-24 +# Portuguese decree (see Europe/Lisbon). A provision in article 5(c) of the +# decree prescribed that Timor "will keep counting time in harmony with +# neighboring foreign colonies, [for] as long as they do not adopt the time +# that belongs to them in [the Washington Convention] system." + # See Indonesia for the 1945 transition. # From João Carrascalão, brother of the former governor of East Timor, in @@ -1256,11 +1263,11 @@ Zone Asia/Tbilisi 2:59:11 - LMT 1880 # midnight on Saturday, September 16. # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Asia/Dili 8:22:20 - LMT 1912 Jan 1 - 8:00 - +08 1942 Feb 21 23:00 - 9:00 - +09 1976 May 3 - 8:00 - +08 2000 Sep 17 0:00 - 9:00 - +09 +Zone Asia/Dili 8:22:20 - LMT 1911 Dec 31 16:00u + 8:00 - %z 1942 Feb 21 23:00 + 9:00 - %z 1976 May 3 + 8:00 - %z 2000 Sep 17 0:00 + 9:00 - %z # India @@ -1326,9 +1333,9 @@ Zone Asia/Kolkata 5:53:28 - LMT 1854 Jun 28 # Kolkata 5:53:20 - HMT 1870 # Howrah Mean Time? 5:21:10 - MMT 1906 Jan 1 # Madras local time 5:30 - IST 1941 Oct - 5:30 1:00 +0630 1942 May 15 + 5:30 1:00 %z 1942 May 15 5:30 - IST 1942 Sep - 5:30 1:00 +0630 1945 Oct 15 + 5:30 1:00 %z 1945 Oct 15 5:30 - IST # Since 1970 the following are like Asia/Kolkata: # Andaman Is @@ -1380,33 +1387,33 @@ Zone Asia/Jakarta 7:07:12 - LMT 1867 Aug 10 # Shanks & Pottenger say the next transition was at 1924 Jan 1 0:13, # but this must be a typo. 7:07:12 - BMT 1923 Dec 31 16:40u # Batavia - 7:20 - +0720 1932 Nov - 7:30 - +0730 1942 Mar 23 - 9:00 - +09 1945 Sep 23 - 7:30 - +0730 1948 May - 8:00 - +08 1950 May - 7:30 - +0730 1964 + 7:20 - %z 1932 Nov + 7:30 - %z 1942 Mar 23 + 9:00 - %z 1945 Sep 23 + 7:30 - %z 1948 May + 8:00 - %z 1950 May + 7:30 - %z 1964 7:00 - WIB # west and central Borneo Zone Asia/Pontianak 7:17:20 - LMT 1908 May 7:17:20 - PMT 1932 Nov # Pontianak MT - 7:30 - +0730 1942 Jan 29 - 9:00 - +09 1945 Sep 23 - 7:30 - +0730 1948 May - 8:00 - +08 1950 May - 7:30 - +0730 1964 + 7:30 - %z 1942 Jan 29 + 9:00 - %z 1945 Sep 23 + 7:30 - %z 1948 May + 8:00 - %z 1950 May + 7:30 - %z 1964 8:00 - WITA 1988 Jan 1 7:00 - WIB # Sulawesi, Lesser Sundas, east and south Borneo Zone Asia/Makassar 7:57:36 - LMT 1920 7:57:36 - MMT 1932 Nov # Macassar MT - 8:00 - +08 1942 Feb 9 - 9:00 - +09 1945 Sep 23 + 8:00 - %z 1942 Feb 9 + 9:00 - %z 1945 Sep 23 8:00 - WITA # Maluku Islands, West Papua, Papua Zone Asia/Jayapura 9:22:48 - LMT 1932 Nov - 9:00 - +09 1944 Sep 1 - 9:30 - +0930 1964 + 9:00 - %z 1944 Sep 1 + 9:30 - %z 1964 9:00 - WIT # Iran @@ -1642,9 +1649,9 @@ Rule Iran 2021 2022 - Sep 21 24:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Tehran 3:25:44 - LMT 1916 3:25:44 - TMT 1935 Jun 13 # Tehran Mean Time - 3:30 Iran +0330/+0430 1977 Oct 20 24:00 - 4:00 Iran +04/+05 1979 - 3:30 Iran +0330/+0430 + 3:30 Iran %z 1977 Oct 20 24:00 + 4:00 Iran %z 1979 + 3:30 Iran %z # Iraq @@ -1687,8 +1694,8 @@ Rule Iraq 1991 2007 - Oct 1 3:00s 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Baghdad 2:57:40 - LMT 1890 2:57:36 - BMT 1918 # Baghdad Mean Time? - 3:00 - +03 1982 May - 3:00 Iraq +03/+04 + 3:00 - %z 1982 May + 3:00 Iraq %z ############################################################################### @@ -2285,7 +2292,7 @@ Rule Jordan 2022 only - Feb lastThu 24:00 1:00 S # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Amman 2:23:44 - LMT 1931 2:00 Jordan EE%sT 2022 Oct 28 0:00s - 3:00 - +03 + 3:00 - %z # Kazakhstan @@ -2496,88 +2503,88 @@ Zone Asia/Amman 2:23:44 - LMT 1931 # Almaty (formerly Alma-Ata), representing most locations in Kazakhstan # This includes Abai/Abay (ISO 3166-2 code KZ-10), Aqmola/Akmola (KZ-11), # Almaty (KZ-19), Almaty city (KZ-75), Astana city (KZ-71), -# East Kazkhstan (KZ-63), Jambyl/Zhambyl (KZ-31), Jetisu/Zhetysu (KZ-33), +# East Kazakhstan (KZ-63), Jambyl/Zhambyl (KZ-31), Jetisu/Zhetysu (KZ-33), # Karaganda (KZ-35), North Kazakhstan (KZ-59), Pavlodar (KZ-55), -# Shyumkent city (KZ-79), Turkistan (KZ-61), and Ulytau (KZ-62). +# Shymkent city (KZ-79), Turkistan (KZ-61), and Ulytau (KZ-62). Zone Asia/Almaty 5:07:48 - LMT 1924 May 2 # or Alma-Ata - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00s - 5:00 RussiaAsia +05/+06 1992 Jan 19 2:00s - 6:00 RussiaAsia +06/+07 2004 Oct 31 2:00s - 6:00 - +06 2024 Mar 1 0:00 - 5:00 - +05 + 5:00 - %z 1930 Jun 21 + 6:00 RussiaAsia %z 1991 Mar 31 2:00s + 5:00 RussiaAsia %z 1992 Jan 19 2:00s + 6:00 RussiaAsia %z 2004 Oct 31 2:00s + 6:00 - %z 2024 Mar 1 0:00 + 5:00 - %z # Qyzylorda (aka Kyzylorda, Kizilorda, Kzyl-Orda, etc.) (KZ-43) Zone Asia/Qyzylorda 4:21:52 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1991 Sep 29 2:00s - 5:00 RussiaAsia +05/+06 1992 Jan 19 2:00s - 6:00 RussiaAsia +06/+07 1992 Mar 29 2:00s - 5:00 RussiaAsia +05/+06 2004 Oct 31 2:00s - 6:00 - +06 2018 Dec 21 0:00 - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Apr 1 + 5:00 1:00 %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1991 Mar 31 2:00s + 4:00 RussiaAsia %z 1991 Sep 29 2:00s + 5:00 RussiaAsia %z 1992 Jan 19 2:00s + 6:00 RussiaAsia %z 1992 Mar 29 2:00s + 5:00 RussiaAsia %z 2004 Oct 31 2:00s + 6:00 - %z 2018 Dec 21 0:00 + 5:00 - %z # Qostanay (aka Kostanay, Kustanay) (KZ-39) # The 1991/2 rules are unclear partly because of the 1997 Turgai # reorganization. Zone Asia/Qostanay 4:14:28 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 2004 Oct 31 2:00s - 6:00 - +06 2024 Mar 1 0:00 - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Apr 1 + 5:00 1:00 %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1991 Mar 31 2:00s + 4:00 RussiaAsia %z 1992 Jan 19 2:00s + 5:00 RussiaAsia %z 2004 Oct 31 2:00s + 6:00 - %z 2024 Mar 1 0:00 + 5:00 - %z # Aqtöbe (aka Aktobe, formerly Aktyubinsk) (KZ-15) Zone Asia/Aqtobe 3:48:40 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 2004 Oct 31 2:00s - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Apr 1 + 5:00 1:00 %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1991 Mar 31 2:00s + 4:00 RussiaAsia %z 1992 Jan 19 2:00s + 5:00 RussiaAsia %z 2004 Oct 31 2:00s + 5:00 - %z # Mangghystaū (KZ-47) # Aqtau was not founded until 1963, but it represents an inhabited region, # so include timestamps before 1963. Zone Asia/Aqtau 3:21:04 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 1994 Sep 25 2:00s - 4:00 RussiaAsia +04/+05 2004 Oct 31 2:00s - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1991 Mar 31 2:00s + 4:00 RussiaAsia %z 1992 Jan 19 2:00s + 5:00 RussiaAsia %z 1994 Sep 25 2:00s + 4:00 RussiaAsia %z 2004 Oct 31 2:00s + 5:00 - %z # Atyraū (KZ-23) is like Mangghystaū except it switched from # +04/+05 to +05/+06 in spring 1999, not fall 1994. Zone Asia/Atyrau 3:27:44 - LMT 1924 May 2 - 3:00 - +03 1930 Jun 21 - 5:00 - +05 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 1999 Mar 28 2:00s - 4:00 RussiaAsia +04/+05 2004 Oct 31 2:00s - 5:00 - +05 + 3:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1991 Mar 31 2:00s + 4:00 RussiaAsia %z 1992 Jan 19 2:00s + 5:00 RussiaAsia %z 1999 Mar 28 2:00s + 4:00 RussiaAsia %z 2004 Oct 31 2:00s + 5:00 - %z # West Kazakhstan (KZ-27) # From Paul Eggert (2016-03-18): # The 1989 transition is from USSR act No. 227 (1989-03-14). Zone Asia/Oral 3:25:24 - LMT 1924 May 2 # or Ural'sk - 3:00 - +03 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1989 Mar 26 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 1992 Mar 29 2:00s - 4:00 RussiaAsia +04/+05 2004 Oct 31 2:00s - 5:00 - +05 + 3:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Apr 1 + 5:00 1:00 %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1989 Mar 26 2:00s + 4:00 RussiaAsia %z 1992 Jan 19 2:00s + 5:00 RussiaAsia %z 1992 Mar 29 2:00s + 4:00 RussiaAsia %z 2004 Oct 31 2:00s + 5:00 - %z # Kyrgyzstan (Kirgizstan) # Transitions through 1991 are from Shanks & Pottenger. @@ -2598,11 +2605,11 @@ Rule Kyrgyz 1997 2005 - Mar lastSun 2:30 1:00 - Rule Kyrgyz 1997 2004 - Oct lastSun 2:30 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Bishkek 4:58:24 - LMT 1924 May 2 - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00s - 5:00 RussiaAsia +05/+06 1991 Aug 31 2:00 - 5:00 Kyrgyz +05/+06 2005 Aug 12 - 6:00 - +06 + 5:00 - %z 1930 Jun 21 + 6:00 RussiaAsia %z 1991 Mar 31 2:00s + 5:00 RussiaAsia %z 1991 Aug 31 2:00 + 5:00 Kyrgyz %z 2005 Aug 12 + 6:00 - %z ############################################################################### @@ -2809,16 +2816,16 @@ Rule NBorneo 1935 1941 - Dec 14 0:00 0 - # and 1982 transition dates are from Mok Ly Yng. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Kuching 7:21:20 - LMT 1926 Mar - 7:30 - +0730 1933 - 8:00 NBorneo +08/+0820 1942 Feb 16 - 9:00 - +09 1945 Sep 12 - 8:00 - +08 + 7:30 - %z 1933 + 8:00 NBorneo %z 1942 Feb 16 + 9:00 - %z 1945 Sep 12 + 8:00 - %z # Maldives # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Indian/Maldives 4:54:00 - LMT 1880 # Malé 4:54:00 - MMT 1960 # Malé Mean Time - 5:00 - +05 + 5:00 - %z # Mongolia @@ -2920,9 +2927,37 @@ Zone Indian/Maldives 4:54:00 - LMT 1880 # Malé # From Arthur David Olson (2008-05-19): # Assume that Choibalsan is indeed offset by 8:00. -# XXX--in the absence of better information, assume that transition -# was at the start of 2008-03-31 (the day of Steffen Thorsen's report); -# this is almost surely wrong. + +# From Heitor David Pinto (2024-06-23): +# Sources about time zones in Mongolia seem to list one of two conflicting +# configurations. The first configuration, mentioned in a comment to the TZ +# database in 1999, citing a Mongolian government website, lists the provinces +# of Bayan-Ölgii, Khovd and Uvs in UTC+7, and the rest of the country in +# UTC+8. The second configuration, mentioned in a comment to the database in +# 2001, lists Bayan-Ölgii, Khovd, Uvs, Govi-Altai and Zavkhan in UTC+7, Dornod +# and Sükhbaatar in UTC+9, and the rest of the country in UTC+8. +# +# The first configuration is still mentioned by several Mongolian travel +# agencies: +# https://www.adventurerider.mn/en/page/about_mongolia +# http://www.naturetours.mn/nt/mongolia.php +# https://www.newjuulchin.mn/web/content/7506?unique=fa24a0f6e96e022a3578ee5195ac879638c734ce +# +# It also matches these flight schedules in 2013: +# http://web.archive.org/web/20130722023600/https://www.hunnuair.com/en/timetabled +# The flight times imply that the airports of Uliastai (Zavkhan), Choibalsan +# (Dornod) and Altai (Govi-Altai) are in the same time zone as Ulaanbaatar, +# and Khovd is one hour behind.... +# +# The second configuration was mentioned by an official of the Mongolian +# standards agency in an interview in 2014: https://ikon.mn/n/9v6 +# And it's still listed by the Mongolian aviation agency: +# https://ais.mn/files/aip/eAIP/2023-12-25/html/eSUP/ZM-eSUP-23-04-en-MN.html +# +# ... I believe that the first configuration is what is actually observed in +# Mongolia and has been so all along, at least since 1999. The second +# configuration closely matches the ideal time zone boundaries at 97.5° E and +# 112.5° E but it doesn't seem to be used in practice. # From Ganbold Tsagaankhuu (2015-03-10): # It seems like yesterday Mongolian Government meeting has concluded to use @@ -2961,25 +2996,18 @@ Rule Mongol 2015 2016 - Sep lastSat 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] # Hovd, a.k.a. Chovd, Dund-Us, Dzhargalant, Khovd, Jirgalanta Zone Asia/Hovd 6:06:36 - LMT 1905 Aug - 6:00 - +06 1978 - 7:00 Mongol +07/+08 + 6:00 - %z 1978 + 7:00 Mongol %z # Ulaanbaatar, a.k.a. Ulan Bataar, Ulan Bator, Urga Zone Asia/Ulaanbaatar 7:07:32 - LMT 1905 Aug - 7:00 - +07 1978 - 8:00 Mongol +08/+09 -# Choibalsan, a.k.a. Bajan Tümen, Bajan Tumen, Chojbalsan, -# Choybalsan, Sanbejse, Tchoibalsan -Zone Asia/Choibalsan 7:38:00 - LMT 1905 Aug - 7:00 - +07 1978 - 8:00 - +08 1983 Apr - 9:00 Mongol +09/+10 2008 Mar 31 - 8:00 Mongol +08/+09 + 7:00 - %z 1978 + 8:00 Mongol %z # Nepal # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Kathmandu 5:41:16 - LMT 1920 - 5:30 - +0530 1986 - 5:45 - +0545 + 5:30 - %z 1986 + 5:45 - %z # Pakistan @@ -3125,10 +3153,10 @@ Rule Pakistan 2009 only - Apr 15 0:00 1:00 S # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Karachi 4:28:12 - LMT 1907 - 5:30 - +0530 1942 Sep - 5:30 1:00 +0630 1945 Oct 15 - 5:30 - +0530 1951 Sep 30 - 5:00 - +05 1971 Mar 26 + 5:30 - %z 1942 Sep + 5:30 1:00 %z 1945 Oct 15 + 5:30 - %z 1951 Sep 30 + 5:00 - %z 1971 Mar 26 5:00 Pakistan PK%sT # Pakistan Time # Palestine @@ -3660,30 +3688,79 @@ Zone Asia/Hebron 2:20:23 - LMT 1900 Oct # be immediately followed by 1845-01-01; see R.H. van Gent's # History of the International Date Line # https://webspace.science.uu.nl/~gent0113/idl/idl_philippines.htm -# The rest of the data entries are from Shanks & Pottenger. - -# From Jesper Nørgaard Welen (2006-04-26): -# ... claims that Philippines had DST last time in 1990: -# http://story.philippinetimes.com/p.x/ct/9/id/145be20cc6b121c0/cid/3e5bbccc730d258c/ -# [a story dated 2006-04-25 by Cris Larano of Dow Jones Newswires, -# but no details] - -# From Paul Eggert (2014-08-14): -# The following source says DST may be instituted November-January and again -# March-June, but this is not definite. It also says DST was last proclaimed -# during the Ramos administration (1992-1998); but again, no details. -# Carcamo D. PNoy urged to declare use of daylight saving time. -# Philippine Star 2014-08-05 -# http://www.philstar.com/headlines/2014/08/05/1354152/pnoy-urged-declare-use-daylight-saving-time - -# From Paul Goyette (2018-06-15): + +# From P Chan (2021-05-10): +# Here's a fairly comprehensive article in Japanese: +# https://wiki.suikawiki.org/n/Philippine%20Time +# (2021-05-16): +# According to the references listed in the article, +# the periods that the Philippines (Manila) observed DST or used +9 are: +# +# 1936-10-31 24:00 to 1937-01-15 24:00 +# (Proclamation No. 104, Proclamation No. 126) +# 1941-12-15 24:00 to 1945-11-30 24:00 +# (Proclamation No. 789, Proclamation No. 20) +# 1954-04-11 24:00 to 1954-06-04 24:00 +# (Proclamation No. 13, Proclamation No. 33) +# 1977-03-27 24:00 to 1977-09-21 24:00 +# (Proclamation No. 1629, Proclamation No. 1641) +# 1990-05-21 00:00 to 1990-07-28 24:00 +# (National Emergency Memorandum Order No. 17, Executive Order No. 415) +# +# Proclamation No. 104 ... October 30, 1936 +# https://www.officialgazette.gov.ph/1936/10/30/proclamation-no-104-s-1936/ +# Proclamation No. 126 ... January 15, 1937 +# https://www.officialgazette.gov.ph/1937/01/15/proclamation-no-126-s-1937/ +# Proclamation No. 789 ... December 13, 1941 +# https://www.officialgazette.gov.ph/1941/12/13/proclamation-no-789-s-1941/ +# Proclamation No. 20 ... November 11, 1945 +# https://www.officialgazette.gov.ph/1945/11/11/proclamation-no-20-s-1945/ +# Proclamation No. 13 ... April 6, 1954 +# https://www.officialgazette.gov.ph/1954/04/06/proclamation-no-13-s-1954/ +# Proclamation No. 33 ... June 3, 1954 +# https://www.officialgazette.gov.ph/1954/06/03/proclamation-no-33-s-1954/ +# Proclamation No. 1629 ... March 25, 1977 +# https://www.officialgazette.gov.ph/1977/03/25/proclamation-no-1629-s-1977/ +# Proclamation No. 1641 ...May 26, 1977 +# https://www.officialgazette.gov.ph/1977/05/26/proclamation-no-1641-s-1977/ +# National Emergency Memorandum Order No. 17 ... May 2, 1990 +# https://www.officialgazette.gov.ph/1990/05/02/national-emergency-memorandum-order-no-17-s-1990/ +# Executive Order No. 415 ... July 20, 1990 +# https://www.officialgazette.gov.ph/1990/07/20/executive-order-no-415-s-1990/ +# +# During WWII, Proclamation No. 789 fixed two periods of DST. The first period +# was set to continue only until January 31, 1942. But Manila was occupied by +# the Japanese earlier in the month.... +# +# For the date of the adoption of standard time, Shank[s] gives 1899-05-11. +# The article is not able to state the basis of that. I guess it was based on +# a US War Department Circular issued on that date. +# https://books.google.com/books?id=JZ1PAAAAYAAJ&pg=RA3-PA8 +# +# However, according to other sources, standard time was adopted on +# 1899-09-06. Also, the LMT was GMT+8:03:52 +# https://books.google.com/books?id=MOYIAQAAIAAJ&pg=PA521 +# https://books.google.com/books?id=lSnqqatpYikC&pg=PA21 +# +# From Paul Eggert (2024-09-05): +# The penultimate URL in P Chan's email refers to page 521 of +# Selga M, The Time Service in the Philippines. +# Proc Pan-Pacific Science Congress. Vol. 1 (1923), 519-532. +# It says, "The change from the meridian 120° 58' 04" to the 120th implied a +# change of 3 min. 52s.26 in time; consequently on 6th September, 1899, +# Manila Observatory gave the noon signal 3 min. 52s.26 later than before". +# +# Wikipedia says the US declared Manila liberated on March 4, 1945; +# this doesn't affect clocks, just our time zone abbreviation and DST flag. + +# From Paul Goyette (2018-06-15) with URLs updated by Guy Harris (2024-02-15): # In the Philippines, there is a national law, Republic Act No. 10535 # which declares the official time here as "Philippine Standard Time". # The act [1] even specifies use of PST as the abbreviation, although # the FAQ provided by PAGASA [2] uses the "acronym PhST to distinguish # it from the Pacific Standard Time (PST)." -# [1] http://www.officialgazette.gov.ph/2013/05/15/republic-act-no-10535/ -# [2] https://www1.pagasa.dost.gov.ph/index.php/astronomy/philippine-standard-time#republic-act-10535 +# [1] https://www.officialgazette.gov.ph/2013/05/15/republic-act-no-10535/ +# [2] https://prsd.pagasa.dost.gov.ph/index.php/28-astronomy/302-philippine-standard-time # # From Paul Eggert (2018-06-19): # I surveyed recent news reports, and my impression is that "PST" is @@ -3692,32 +3769,34 @@ Zone Asia/Hebron 2:20:23 - LMT 1900 Oct # influence of the sources. There is no current abbreviation for DST, # so use "PDT", the usual American style. -# From P Chan (2021-05-10): -# Here's a fairly comprehensive article in Japanese: -# https://wiki.suikawiki.org/n/Philippine%20Time -# From Paul Eggert (2021-05-10): -# The info in the Japanese table has not been absorbed (yet) below. - # Rule NAME FROM TO - IN ON AT SAVE LETTER/S -Rule Phil 1936 only - Nov 1 0:00 1:00 D -Rule Phil 1937 only - Feb 1 0:00 0 S -Rule Phil 1954 only - Apr 12 0:00 1:00 D -Rule Phil 1954 only - Jul 1 0:00 0 S -Rule Phil 1978 only - Mar 22 0:00 1:00 D -Rule Phil 1978 only - Sep 21 0:00 0 S +Rule Phil 1936 only - Oct 31 24:00 1:00 D +Rule Phil 1937 only - Jan 15 24:00 0 S +Rule Phil 1941 only - Dec 15 24:00 1:00 D +# The following three rules were canceled by Japan: +#Rule Phil 1942 only - Jan 31 24:00 0 S +#Rule Phil 1942 only - Mar 1 0:00 1:00 D +#Rule Phil 1942 only - Jun 30 24:00 0 S +Rule Phil 1945 only - Nov 30 24:00 0 S +Rule Phil 1954 only - Apr 11 24:00 1:00 D +Rule Phil 1954 only - Jun 4 24:00 0 S +Rule Phil 1977 only - Mar 27 24:00 1:00 D +Rule Phil 1977 only - Sep 21 24:00 0 S +Rule Phil 1990 only - May 21 0:00 1:00 D +Rule Phil 1990 only - Jul 28 24:00 0 S # Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone Asia/Manila -15:56:00 - LMT 1844 Dec 31 - 8:04:00 - LMT 1899 May 11 - 8:00 Phil P%sT 1942 May - 9:00 - JST 1944 Nov +Zone Asia/Manila -15:56:08 - LMT 1844 Dec 31 + 8:03:52 - LMT 1899 Sep 6 4:00u + 8:00 Phil P%sT 1942 Feb 11 24:00 + 9:00 - JST 1945 Mar 4 8:00 Phil P%sT # Bahrain # Qatar # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Qatar 3:26:08 - LMT 1920 # Al Dawhah / Doha - 4:00 - +04 1972 Jun - 3:00 - +03 + 4:00 - %z 1972 Jun + 3:00 - %z # Kuwait # Saudi Arabia @@ -3767,7 +3846,7 @@ Zone Asia/Qatar 3:26:08 - LMT 1920 # Al Dawhah / Doha # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Riyadh 3:06:52 - LMT 1947 Mar 14 - 3:00 - +03 + 3:00 - %z # Singapore # taken from Mok Ly Yng (2003-10-30) @@ -3775,13 +3854,13 @@ Zone Asia/Riyadh 3:06:52 - LMT 1947 Mar 14 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 6:55:25 - SMT 1905 Jun 1 # Singapore M.T. - 7:00 - +07 1933 Jan 1 - 7:00 0:20 +0720 1936 Jan 1 - 7:20 - +0720 1941 Sep 1 - 7:30 - +0730 1942 Feb 16 - 9:00 - +09 1945 Sep 12 - 7:30 - +0730 1981 Dec 31 16:00u - 8:00 - +08 + 7:00 - %z 1933 Jan 1 + 7:00 0:20 %z 1936 Jan 1 + 7:20 - %z 1941 Sep 1 + 7:30 - %z 1942 Feb 16 + 9:00 - %z 1945 Sep 12 + 7:30 - %z 1981 Dec 31 16:00u + 8:00 - %z # Spratly Is # no information @@ -3839,13 +3918,13 @@ Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Colombo 5:19:24 - LMT 1880 5:19:32 - MMT 1906 # Moratuwa Mean Time - 5:30 - +0530 1942 Jan 5 - 5:30 0:30 +06 1942 Sep - 5:30 1:00 +0630 1945 Oct 16 2:00 - 5:30 - +0530 1996 May 25 0:00 - 6:30 - +0630 1996 Oct 26 0:30 - 6:00 - +06 2006 Apr 15 0:30 - 5:30 - +0530 + 5:30 - %z 1942 Jan 5 + 5:30 0:30 %z 1942 Sep + 5:30 1:00 %z 1945 Oct 16 2:00 + 5:30 - %z 1996 May 25 0:00 + 6:30 - %z 1996 Oct 26 0:30 + 6:00 - %z 2006 Apr 15 0:30 + 5:30 - %z # Syria # Rule NAME FROM TO - IN ON AT SAVE LETTER/S @@ -4016,16 +4095,16 @@ Rule Syria 2009 2022 - Oct lastFri 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Damascus 2:25:12 - LMT 1920 # Dimashq 2:00 Syria EE%sT 2022 Oct 28 0:00 - 3:00 - +03 + 3:00 - %z # Tajikistan # From Shanks & Pottenger. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Dushanbe 4:35:12 - LMT 1924 May 2 - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00s - 5:00 1:00 +06 1991 Sep 9 2:00s - 5:00 - +05 + 5:00 - %z 1930 Jun 21 + 6:00 RussiaAsia %z 1991 Mar 31 2:00s + 5:00 1:00 %z 1991 Sep 9 2:00s + 5:00 - %z # Cambodia # Christmas I @@ -4035,16 +4114,16 @@ Zone Asia/Dushanbe 4:35:12 - LMT 1924 May 2 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Bangkok 6:42:04 - LMT 1880 6:42:04 - BMT 1920 Apr # Bangkok Mean Time - 7:00 - +07 + 7:00 - %z # Turkmenistan # From Shanks & Pottenger. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Ashgabat 3:53:32 - LMT 1924 May 2 # or Ashkhabad - 4:00 - +04 1930 Jun 21 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00 - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00 - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 RussiaAsia %z 1991 Mar 31 2:00 + 4:00 RussiaAsia %z 1992 Jan 19 2:00 + 5:00 - %z # Oman # Réunion @@ -4054,25 +4133,25 @@ Zone Asia/Ashgabat 3:53:32 - LMT 1924 May 2 # or Ashkhabad # The Crozet Is also observe Réunion time; see the 'antarctica' file. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Dubai 3:41:12 - LMT 1920 - 4:00 - +04 + 4:00 - %z # Uzbekistan # Byalokoz 1919 says Uzbekistan was 4:27:53. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Samarkand 4:27:53 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1992 - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 - %z 1981 Apr 1 + 5:00 1:00 %z 1981 Oct 1 + 6:00 - %z 1982 Apr 1 + 5:00 RussiaAsia %z 1992 + 5:00 - %z # Milne says Tashkent was 4:37:10.8. #STDOFF 4:37:10.8 Zone Asia/Tashkent 4:37:11 - LMT 1924 May 2 - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00 - 5:00 RussiaAsia +05/+06 1992 - 5:00 - +05 + 5:00 - %z 1930 Jun 21 + 6:00 RussiaAsia %z 1991 Mar 31 2:00 + 5:00 RussiaAsia %z 1992 + 5:00 - %z # Vietnam (southern) @@ -4130,7 +4209,7 @@ Zone Asia/Tashkent 4:37:11 - LMT 1924 May 2 # Võ Nguyên Giáp, Việt Nam Dân Quốc Công Báo, No. 1 (1945-09-29), page 13 # http://baochi.nlv.gov.vn/baochi/cgi-bin/baochi?a=d&d=JwvzO19450929.2.5&dliv=none # It says that on 1945-09-01 at 24:00, Vietnam moved back two hours, to +07. -# It also mentions a 1945-03-29 decree (by a Japanese Goveror-General) +# It also mentions a 1945-03-29 decree (by a Japanese Governor-General) # to set the time zone to +09, but does not say whether that decree # merely legalized an earlier change to +09. # @@ -4151,14 +4230,14 @@ Zone Asia/Tashkent 4:37:11 - LMT 1924 May 2 #STDOFF 7:06:30.13 Zone Asia/Ho_Chi_Minh 7:06:30 - LMT 1906 Jul 1 7:06:30 - PLMT 1911 May 1 # Phù Liễn MT - 7:00 - +07 1942 Dec 31 23:00 - 8:00 - +08 1945 Mar 14 23:00 - 9:00 - +09 1945 Sep 1 24:00 - 7:00 - +07 1947 Apr 1 - 8:00 - +08 1955 Jul 1 01:00 - 7:00 - +07 1959 Dec 31 23:00 - 8:00 - +08 1975 Jun 13 - 7:00 - +07 + 7:00 - %z 1942 Dec 31 23:00 + 8:00 - %z 1945 Mar 14 23:00 + 9:00 - %z 1945 Sep 1 24:00 + 7:00 - %z 1947 Apr 1 + 8:00 - %z 1955 Jul 1 01:00 + 7:00 - %z 1959 Dec 31 23:00 + 8:00 - %z 1975 Jun 13 + 7:00 - %z # From Paul Eggert (2019-02-19): # diff --git a/make/data/tzdata/australasia b/make/data/tzdata/australasia index 624735be652d2..d659d1fb4b183 100644 --- a/make/data/tzdata/australasia +++ b/make/data/tzdata/australasia @@ -66,8 +66,8 @@ Zone Australia/Perth 7:43:24 - LMT 1895 Dec 8:00 Aus AW%sT 1943 Jul 8:00 AW AW%sT Zone Australia/Eucla 8:35:28 - LMT 1895 Dec - 8:45 Aus +0845/+0945 1943 Jul - 8:45 AW +0845/+0945 + 8:45 Aus %z 1943 Jul + 8:45 AW %z # Queensland # @@ -232,8 +232,8 @@ Rule LH 2008 max - Apr Sun>=1 2:00 0 - Rule LH 2008 max - Oct Sun>=1 2:00 0:30 - Zone Australia/Lord_Howe 10:36:20 - LMT 1895 Feb 10:00 - AEST 1981 Mar - 10:30 LH +1030/+1130 1985 Jul - 10:30 LH +1030/+11 + 10:30 LH %z 1985 Jul + 10:30 LH %z # Australian miscellany # @@ -439,16 +439,16 @@ Rule Fiji 2019 only - Nov Sun>=8 2:00 1:00 - Rule Fiji 2020 only - Dec 20 2:00 1:00 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Fiji 11:55:44 - LMT 1915 Oct 26 # Suva - 12:00 Fiji +12/+13 + 12:00 Fiji %z # French Polynesia # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Gambier -8:59:48 - LMT 1912 Oct 1 # Rikitea - -9:00 - -09 + -9:00 - %z Zone Pacific/Marquesas -9:18:00 - LMT 1912 Oct 1 - -9:30 - -0930 + -9:30 - %z Zone Pacific/Tahiti -9:58:16 - LMT 1912 Oct 1 # Papeete - -10:00 - -10 + -10:00 - %z # Clipperton (near North America) is administered from French Polynesia; # it is uninhabited. @@ -491,7 +491,7 @@ Rule Guam 1977 only - Aug 28 2:00 0 S Zone Pacific/Guam -14:21:00 - LMT 1844 Dec 31 9:39:00 - LMT 1901 # Agana 10:00 - GST 1941 Dec 10 # Guam - 9:00 - +09 1944 Jul 31 + 9:00 - %z 1944 Jul 31 10:00 Guam G%sT 2000 Dec 23 10:00 - ChST # Chamorro Standard Time @@ -503,30 +503,30 @@ Zone Pacific/Guam -14:21:00 - LMT 1844 Dec 31 # Wallis & Futuna # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Tarawa 11:32:04 - LMT 1901 # Bairiki - 12:00 - +12 + 12:00 - %z # Kiribati (except Gilbert Is) # See Pacific/Tarawa for the Gilbert Is. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Kanton 0 - -00 1937 Aug 31 - -12:00 - -12 1979 Oct - -11:00 - -11 1994 Dec 31 - 13:00 - +13 + -12:00 - %z 1979 Oct + -11:00 - %z 1994 Dec 31 + 13:00 - %z Zone Pacific/Kiritimati -10:29:20 - LMT 1901 - -10:40 - -1040 1979 Oct - -10:00 - -10 1994 Dec 31 - 14:00 - +14 + -10:40 - %z 1979 Oct + -10:00 - %z 1994 Dec 31 + 14:00 - %z # Marshall Is # See Pacific/Tarawa for most locations. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Kwajalein 11:09:20 - LMT 1901 - 11:00 - +11 1937 - 10:00 - +10 1941 Apr 1 - 9:00 - +09 1944 Feb 6 - 11:00 - +11 1969 Oct - -12:00 - -12 1993 Aug 20 24:00 - 12:00 - +12 + 11:00 - %z 1937 + 10:00 - %z 1941 Apr 1 + 9:00 - %z 1944 Feb 6 + 11:00 - %z 1969 Oct + -12:00 - %z 1993 Aug 20 24:00 + 12:00 - %z # Micronesia # For Chuuk and Yap see Pacific/Port_Moresby. @@ -534,22 +534,22 @@ Zone Pacific/Kwajalein 11:09:20 - LMT 1901 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Kosrae -13:08:04 - LMT 1844 Dec 31 10:51:56 - LMT 1901 - 11:00 - +11 1914 Oct - 9:00 - +09 1919 Feb 1 - 11:00 - +11 1937 - 10:00 - +10 1941 Apr 1 - 9:00 - +09 1945 Aug - 11:00 - +11 1969 Oct - 12:00 - +12 1999 - 11:00 - +11 + 11:00 - %z 1914 Oct + 9:00 - %z 1919 Feb 1 + 11:00 - %z 1937 + 10:00 - %z 1941 Apr 1 + 9:00 - %z 1945 Aug + 11:00 - %z 1969 Oct + 12:00 - %z 1999 + 11:00 - %z # Nauru # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Nauru 11:07:40 - LMT 1921 Jan 15 # Uaobe - 11:30 - +1130 1942 Aug 29 - 9:00 - +09 1945 Sep 8 - 11:30 - +1130 1979 Feb 10 2:00 - 12:00 - +12 + 11:30 - %z 1942 Aug 29 + 9:00 - %z 1945 Sep 8 + 11:30 - %z 1979 Feb 10 2:00 + 12:00 - %z # New Caledonia # Rule NAME FROM TO - IN ON AT SAVE LETTER/S @@ -560,7 +560,7 @@ Rule NC 1996 only - Dec 1 2:00s 1:00 - Rule NC 1997 only - Mar 2 2:00s 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Noumea 11:05:48 - LMT 1912 Jan 13 # Nouméa - 11:00 NC +11/+12 + 11:00 NC %z ############################################################################### @@ -604,8 +604,8 @@ Zone Pacific/Auckland 11:39:04 - LMT 1868 Nov 2 12:00 NZ NZ%sT Zone Pacific/Chatham 12:13:48 - LMT 1868 Nov 2 - 12:15 - +1215 1946 Jan 1 - 12:45 Chatham +1245/+1345 + 12:15 - %z 1946 Jan 1 + 12:45 Chatham %z # Auckland Is # uninhabited; Māori and Moriori, colonial settlers, pastoralists, sealers, @@ -658,8 +658,8 @@ Rule Cook 1979 1990 - Oct lastSun 0:00 0:30 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Rarotonga 13:20:56 - LMT 1899 Dec 26 # Avarua -10:39:04 - LMT 1952 Oct 16 - -10:30 - -1030 1978 Nov 12 - -10:00 Cook -10/-0930 + -10:30 - %z 1978 Nov 12 + -10:00 Cook %z ############################################################################### @@ -676,30 +676,30 @@ Zone Pacific/Rarotonga 13:20:56 - LMT 1899 Dec 26 # Avarua # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Niue -11:19:40 - LMT 1952 Oct 16 # Alofi - -11:20 - -1120 1964 Jul - -11:00 - -11 + -11:20 - %z 1964 Jul + -11:00 - %z # Norfolk # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Norfolk 11:11:52 - LMT 1901 # Kingston - 11:12 - +1112 1951 - 11:30 - +1130 1974 Oct 27 02:00s - 11:30 1:00 +1230 1975 Mar 2 02:00s - 11:30 - +1130 2015 Oct 4 02:00s - 11:00 - +11 2019 Jul - 11:00 AN +11/+12 + 11:12 - %z 1951 + 11:30 - %z 1974 Oct 27 02:00s + 11:30 1:00 %z 1975 Mar 2 02:00s + 11:30 - %z 2015 Oct 4 02:00s + 11:00 - %z 2019 Jul + 11:00 AN %z # Palau (Belau) # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Palau -15:02:04 - LMT 1844 Dec 31 # Koror 8:57:56 - LMT 1901 - 9:00 - +09 + 9:00 - %z # Papua New Guinea # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Port_Moresby 9:48:40 - LMT 1880 9:48:32 - PMMT 1895 # Port Moresby Mean Time - 10:00 - +10 + 10:00 - %z # # From Paul Eggert (2014-10-13): # Base the Bougainville entry on the Arawa-Kieta region, which appears to have @@ -720,16 +720,16 @@ Zone Pacific/Port_Moresby 9:48:40 - LMT 1880 # Zone Pacific/Bougainville 10:22:16 - LMT 1880 9:48:32 - PMMT 1895 - 10:00 - +10 1942 Jul - 9:00 - +09 1945 Aug 21 - 10:00 - +10 2014 Dec 28 2:00 - 11:00 - +11 + 10:00 - %z 1942 Jul + 9:00 - %z 1945 Aug 21 + 10:00 - %z 2014 Dec 28 2:00 + 11:00 - %z # Pitcairn # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Pitcairn -8:40:20 - LMT 1901 # Adamstown - -8:30 - -0830 1998 Apr 27 0:00 - -8:00 - -08 + -8:30 - %z 1998 Apr 27 0:00 + -8:00 - %z # American Samoa # Midway @@ -818,15 +818,15 @@ Rule WS 2012 2020 - Sep lastSun 3:00 1 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Apia 12:33:04 - LMT 1892 Jul 5 -11:26:56 - LMT 1911 - -11:30 - -1130 1950 - -11:00 WS -11/-10 2011 Dec 29 24:00 - 13:00 WS +13/+14 + -11:30 - %z 1950 + -11:00 WS %z 2011 Dec 29 24:00 + 13:00 WS %z # Solomon Is # excludes Bougainville, for which see Papua New Guinea # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Guadalcanal 10:39:48 - LMT 1912 Oct 1 # Honiara - 11:00 - +11 + 11:00 - %z # Tokelau # @@ -849,8 +849,8 @@ Zone Pacific/Guadalcanal 10:39:48 - LMT 1912 Oct 1 # Honiara # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Fakaofo -11:24:56 - LMT 1901 - -11:00 - -11 2011 Dec 30 - 13:00 - +13 + -11:00 - %z 2011 Dec 30 + 13:00 - %z # Tonga # Rule NAME FROM TO - IN ON AT SAVE LETTER/S @@ -862,9 +862,9 @@ Rule Tonga 2016 only - Nov Sun>=1 2:00 1:00 - Rule Tonga 2017 only - Jan Sun>=15 3:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Tongatapu 12:19:12 - LMT 1945 Sep 10 - 12:20 - +1220 1961 - 13:00 - +13 1999 - 13:00 Tonga +13/+14 + 12:20 - %z 1961 + 13:00 - %z 1999 + 13:00 Tonga %z # US minor outlying islands @@ -953,7 +953,7 @@ Rule Vanuatu 1992 1993 - Jan Sat>=22 24:00 0 - Rule Vanuatu 1992 only - Oct Sat>=22 24:00 1:00 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila - 11:00 Vanuatu +11/+12 + 11:00 Vanuatu %z ############################################################################### @@ -1262,10 +1262,10 @@ Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila # The 1992 ending date used in the rules is a best guess; # it matches what was used in the past. -# The Australian Bureau of Meteorology FAQ -# http://www.bom.gov.au/faq/faqgen.htm -# (1999-09-27) writes that Giles Meteorological Station uses -# South Australian time even though it's located in Western Australia. +# From Christopher Hunt (2006-11-21), after an advance warning +# from Jesper Nørgaard Welen (2006-11-01): +# WA are trialing DST for three years. +# http://www.parliament.wa.gov.au/parliament/bills.nsf/9A1B183144403DA54825721200088DF1/$File/Bill175-1B.pdf # From Paul Eggert (2018-04-01): # The Guardian Express of Perth, Australia reported today that the @@ -1277,54 +1277,10 @@ Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila # https://www.communitynews.com.au/guardian-express/news/exclusive-daylight-savings-coming-wa-summer-2018/ # [The article ends with "Today's date is April 1."] -# Queensland - -# From Paul Eggert (2018-02-26): -# I lack access to the following source for Queensland DST: -# Pearce C. History of daylight saving time in Queensland. -# Queensland Hist J. 2017 Aug;23(6):389-403 -# https://search.informit.com.au/documentSummary;dn=994682348436426;res=IELHSS - -# From George Shepherd via Simon Woodhead via Robert Elz (1991-03-06): -# # The state of QUEENSLAND.. [ Courtesy Qld. Dept Premier Econ&Trade Devel ] -# # [ Dec 1990 ] -# ... -# Zone Australia/Queensland 10:00 AQ %sST -# ... -# Rule AQ 1971 only - Oct lastSun 2:00 1:00 D -# Rule AQ 1972 only - Feb lastSun 3:00 0 E -# Rule AQ 1989 max - Oct lastSun 2:00 1:00 D -# Rule AQ 1990 max - Mar Sun>=1 3:00 0 E - -# From Bradley White (1989-12-24): -# "Australia/Queensland" now observes daylight time (i.e. from -# October 1989). - -# From Bradley White (1991-03-04): -# A recent excerpt from an Australian newspaper... -# ...Queensland...[has] agreed to end daylight saving -# at 3am tomorrow (March 3)... - -# From John Mackin (1991-03-06): -# I can certainly confirm for my part that Daylight Saving in NSW did in fact -# end on Sunday, 3 March. I don't know at what hour, though. (It surprised -# me.) - -# From Bradley White (1992-03-08): -# ...there was recently a referendum in Queensland which resulted -# in the experimental daylight saving system being abandoned. So, ... -# ... -# Rule QLD 1989 1991 - Oct lastSun 2:00 1:00 D -# Rule QLD 1990 1992 - Mar Sun>=1 3:00 0 S -# ... - -# From Arthur David Olson (1992-03-08): -# The chosen rules the union of the 1971/1972 change and the 1989-1992 changes. - -# From Christopher Hunt (2006-11-21), after an advance warning -# from Jesper Nørgaard Welen (2006-11-01): -# WA are trialing DST for three years. -# http://www.parliament.wa.gov.au/parliament/bills.nsf/9A1B183144403DA54825721200088DF1/$File/Bill175-1B.pdf +# The Australian Bureau of Meteorology FAQ +# http://www.bom.gov.au/faq/faqgen.htm +# (1999-09-27) writes that Giles Meteorological Station uses +# South Australian time even though it's located in Western Australia. # From Rives McDow (2002-04-09): # The most interesting region I have found consists of three towns on the @@ -1382,6 +1338,59 @@ Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila # For lack of better info, assume the tradition dates back to the # introduction of standard time in 1895. +# From Stuart Bishop (2024-11-12): +# An article discussing the in-use but technically unofficial timezones +# in the Western Australian portion of the Nullarbor Plain. +# https://www.abc.net.au/news/2024-11-22/outback-wa-properties-strange-time-zones/104542494 +# From Paul Eggert (2024-11-12): +# As the article says, the Eyre Bird Observatory and nearby sheep stations +# can use Tokyo time. Other possibilities include Asia/Chita, Asia/Seoul, +# and Asia/Jayapura. + +# Queensland + +# From Paul Eggert (2018-02-26): +# I lack access to the following source for Queensland DST: +# Pearce C. History of daylight saving time in Queensland. +# Queensland Hist J. 2017 Aug;23(6):389-403 +# https://search.informit.com.au/documentSummary;dn=994682348436426;res=IELHSS + +# From George Shepherd via Simon Woodhead via Robert Elz (1991-03-06): +# # The state of QUEENSLAND.. [ Courtesy Qld. Dept Premier Econ&Trade Devel ] +# # [ Dec 1990 ] +# ... +# Zone Australia/Queensland 10:00 AQ %sST +# ... +# Rule AQ 1971 only - Oct lastSun 2:00 1:00 D +# Rule AQ 1972 only - Feb lastSun 3:00 0 E +# Rule AQ 1989 max - Oct lastSun 2:00 1:00 D +# Rule AQ 1990 max - Mar Sun>=1 3:00 0 E + +# From Bradley White (1989-12-24): +# "Australia/Queensland" now observes daylight time (i.e. from +# October 1989). + +# From Bradley White (1991-03-04): +# A recent excerpt from an Australian newspaper... +# ...Queensland...[has] agreed to end daylight saving +# at 3am tomorrow (March 3)... + +# From John Mackin (1991-03-06): +# I can certainly confirm for my part that Daylight Saving in NSW did in fact +# end on Sunday, 3 March. I don't know at what hour, though. (It surprised +# me.) + +# From Bradley White (1992-03-08): +# ...there was recently a referendum in Queensland which resulted +# in the experimental daylight saving system being abandoned. So, ... +# ... +# Rule QLD 1989 1991 - Oct lastSun 2:00 1:00 D +# Rule QLD 1990 1992 - Mar Sun>=1 3:00 0 S +# ... + +# From Arthur David Olson (1992-03-08): +# The chosen rules the union of the 1971/1972 change and the 1989-1992 changes. + # southeast Australia # diff --git a/make/data/tzdata/backward b/make/data/tzdata/backward index 7ddc6cc3d93b0..cda2ccc0c66ec 100644 --- a/make/data/tzdata/backward +++ b/make/data/tzdata/backward @@ -21,12 +21,13 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -# tzdb links for backward compatibility +# Links and zones for backward compatibility # This file is in the public domain, so clarified as of # 2009-05-17 by Arthur David Olson. # This file provides links from old or merged timezone names to current ones. +# It also provides a few zone entries for old naming conventions. # Many names changed in 1993 and in 1995, and many merged names moved here # in the period from 2013 through 2022. Several of these names are # also present in the file 'backzone', which has data important only @@ -67,6 +68,8 @@ Link America/Rio_Branco Brazil/Acre #= America/Porto_Acre Link America/Noronha Brazil/DeNoronha Link America/Sao_Paulo Brazil/East Link America/Manaus Brazil/West +Link Europe/Brussels CET +Link America/Chicago CST6CDT Link America/Halifax Canada/Atlantic Link America/Winnipeg Canada/Central # This line is commented out, as the name exceeded the 14-character limit @@ -81,6 +84,9 @@ Link America/Whitehorse Canada/Yukon Link America/Santiago Chile/Continental Link Pacific/Easter Chile/EasterIsland Link America/Havana Cuba +Link Europe/Athens EET +Link America/Panama EST +Link America/New_York EST5EDT Link Africa/Cairo Egypt Link Europe/Dublin Eire # Vanguard section, for most .zi parsers. @@ -119,6 +125,9 @@ Link America/Jamaica Jamaica Link Asia/Tokyo Japan Link Pacific/Kwajalein Kwajalein Link Africa/Tripoli Libya +Link Europe/Brussels MET +Link America/Phoenix MST +Link America/Denver MST7MDT Link America/Tijuana Mexico/BajaNorte Link America/Mazatlan Mexico/BajaSur Link America/Mexico_City Mexico/General @@ -298,6 +307,7 @@ Link America/Denver America/Shiprock Link America/Toronto America/Thunder_Bay Link America/Edmonton America/Yellowknife Link Pacific/Auckland Antarctica/South_Pole +Link Asia/Ulaanbaatar Asia/Choibalsan Link Asia/Shanghai Asia/Chongqing Link Asia/Shanghai Asia/Harbin Link Asia/Urumqi Asia/Kashgar @@ -312,6 +322,7 @@ Link Europe/Kyiv Europe/Zaporozhye Link Pacific/Kanton Pacific/Enderbury Link Pacific/Honolulu Pacific/Johnston Link Pacific/Port_Moresby Pacific/Yap +Link Europe/Lisbon WET # Alternate names for the same location @@ -337,5 +348,7 @@ Link Europe/Kyiv Europe/Kiev # Classically, Cyprus is in Asia; e.g. see Herodotus, Histories, I.72. # However, for various reasons many users expect to find it under Europe. Link Asia/Nicosia Europe/Nicosia +Link Pacific/Honolulu HST +Link America/Los_Angeles PST8PDT Link Pacific/Guadalcanal Pacific/Ponape #= Pacific/Pohnpei Link Pacific/Port_Moresby Pacific/Truk #= Pacific/Chuuk diff --git a/make/data/tzdata/etcetera b/make/data/tzdata/etcetera index 27147715ef6a5..41660b05dbad8 100644 --- a/make/data/tzdata/etcetera +++ b/make/data/tzdata/etcetera @@ -28,7 +28,7 @@ # These entries are for uses not otherwise covered by the tz database. # Their main practical use is for platforms like Android that lack -# support for POSIX.1-2017-style TZ strings. On such platforms these entries +# support for POSIX proleptic TZ strings. On such platforms these entries # can be useful if the timezone database is wrong or if a ship or # aircraft at sea is not in a timezone. @@ -74,29 +74,33 @@ Link Etc/GMT GMT # so we moved the names into the Etc subdirectory. # Also, the time zone abbreviations are now compatible with %z. -Zone Etc/GMT-14 14 - +14 -Zone Etc/GMT-13 13 - +13 -Zone Etc/GMT-12 12 - +12 -Zone Etc/GMT-11 11 - +11 -Zone Etc/GMT-10 10 - +10 -Zone Etc/GMT-9 9 - +09 -Zone Etc/GMT-8 8 - +08 -Zone Etc/GMT-7 7 - +07 -Zone Etc/GMT-6 6 - +06 -Zone Etc/GMT-5 5 - +05 -Zone Etc/GMT-4 4 - +04 -Zone Etc/GMT-3 3 - +03 -Zone Etc/GMT-2 2 - +02 -Zone Etc/GMT-1 1 - +01 -Zone Etc/GMT+1 -1 - -01 -Zone Etc/GMT+2 -2 - -02 -Zone Etc/GMT+3 -3 - -03 -Zone Etc/GMT+4 -4 - -04 -Zone Etc/GMT+5 -5 - -05 -Zone Etc/GMT+6 -6 - -06 -Zone Etc/GMT+7 -7 - -07 -Zone Etc/GMT+8 -8 - -08 -Zone Etc/GMT+9 -9 - -09 -Zone Etc/GMT+10 -10 - -10 -Zone Etc/GMT+11 -11 - -11 -Zone Etc/GMT+12 -12 - -12 +# There is no "Etc/Unknown" entry, as CLDR says that "Etc/Unknown" +# corresponds to an unknown or invalid time zone, and things would get +# confusing if Etc/Unknown were made valid here. + +Zone Etc/GMT-14 14 - %z +Zone Etc/GMT-13 13 - %z +Zone Etc/GMT-12 12 - %z +Zone Etc/GMT-11 11 - %z +Zone Etc/GMT-10 10 - %z +Zone Etc/GMT-9 9 - %z +Zone Etc/GMT-8 8 - %z +Zone Etc/GMT-7 7 - %z +Zone Etc/GMT-6 6 - %z +Zone Etc/GMT-5 5 - %z +Zone Etc/GMT-4 4 - %z +Zone Etc/GMT-3 3 - %z +Zone Etc/GMT-2 2 - %z +Zone Etc/GMT-1 1 - %z +Zone Etc/GMT+1 -1 - %z +Zone Etc/GMT+2 -2 - %z +Zone Etc/GMT+3 -3 - %z +Zone Etc/GMT+4 -4 - %z +Zone Etc/GMT+5 -5 - %z +Zone Etc/GMT+6 -6 - %z +Zone Etc/GMT+7 -7 - %z +Zone Etc/GMT+8 -8 - %z +Zone Etc/GMT+9 -9 - %z +Zone Etc/GMT+10 -10 - %z +Zone Etc/GMT+11 -11 - %z +Zone Etc/GMT+12 -12 - %z diff --git a/make/data/tzdata/europe b/make/data/tzdata/europe index 18865f33b6c59..7ba6c67960978 100644 --- a/make/data/tzdata/europe +++ b/make/data/tzdata/europe @@ -753,14 +753,6 @@ Rule Russia 1996 2010 - Oct lastSun 2:00s 0 - # Take "abolishing daylight saving time" to mean that time is now considered # to be standard. -# These are for backward compatibility with older versions. - -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone WET 0:00 EU WE%sT -Zone CET 1:00 C-Eur CE%sT -Zone MET 1:00 C-Eur ME%sT -Zone EET 2:00 EU EE%sT - # Previous editions of this database used abbreviations like MET DST # for Central European Summer Time, but this didn't agree with common usage. @@ -894,7 +886,7 @@ Zone Europe/Minsk 1:50:16 - LMT 1880 3:00 Russia MSK/MSD 1990 3:00 - MSK 1991 Mar 31 2:00s 2:00 Russia EE%sT 2011 Mar 27 2:00s - 3:00 - +03 + 3:00 - %z # Belgium # Luxembourg @@ -1178,7 +1170,7 @@ Zone Atlantic/Faroe -0:27:04 - LMT 1908 Jan 11 # Tórshavn # However, Greenland will change to Daylight Saving Time again in 2024 # and onwards. -# From a contributor who wishes to remain anonymous for now (2023-10-29): +# From Jule Dabars (2023-10-29): # https://www.dr.dk/nyheder/seneste/i-nat-skal-uret-stilles-en-time-tilbage-men-foerste-gang-sker-det-ikke-i-groenland # with a link to that page: # https://naalakkersuisut.gl/Nyheder/2023/10/2710_sommertid @@ -1199,22 +1191,22 @@ Rule Thule 2007 max - Nov Sun>=1 2:00 0 S # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Danmarkshavn -1:14:40 - LMT 1916 Jul 28 - -3:00 - -03 1980 Apr 6 2:00 - -3:00 EU -03/-02 1996 + -3:00 - %z 1980 Apr 6 2:00 + -3:00 EU %z 1996 0:00 - GMT # # Use the old name Scoresbysund, as the current name Ittoqqortoormiit # exceeds tzdb's 14-letter limit and has no common English abbreviation. Zone America/Scoresbysund -1:27:52 - LMT 1916 Jul 28 # Ittoqqortoormiit - -2:00 - -02 1980 Apr 6 2:00 - -2:00 C-Eur -02/-01 1981 Mar 29 - -1:00 EU -01/+00 2024 Mar 31 - -2:00 EU -02/-01 + -2:00 - %z 1980 Apr 6 2:00 + -2:00 C-Eur %z 1981 Mar 29 + -1:00 EU %z 2024 Mar 31 + -2:00 EU %z Zone America/Nuuk -3:26:56 - LMT 1916 Jul 28 # Godthåb - -3:00 - -03 1980 Apr 6 2:00 - -3:00 EU -03/-02 2023 Mar 26 1:00u - -2:00 - -02 2023 Oct 29 1:00u - -2:00 EU -02/-01 + -3:00 - %z 1980 Apr 6 2:00 + -3:00 EU %z 2023 Mar 26 1:00u + -2:00 - %z 2023 Oct 29 1:00u + -2:00 EU %z Zone America/Thule -4:35:08 - LMT 1916 Jul 28 # Pituffik -4:00 Thule A%sT @@ -2086,10 +2078,39 @@ Zone Europe/Warsaw 1:24:00 - LMT 1880 # Portugal -# From Paul Eggert (2014-08-11), after a heads-up from Stephen Colebourne: -# According to a Portuguese decree (1911-05-26) -# https://dre.pt/application/dir/pdf1sdip/1911/05/12500/23132313.pdf -# Lisbon was at -0:36:44.68, but switched to GMT on 1912-01-01 at 00:00. +# From Tim Parenti (2024-07-01), per Alois Treindl (2021-02-07) and Michael +# Deckers (2021-02-10): +# http://oal.ul.pt/documentos/2018/01/hl1911a2018.pdf/ +# The Astronomical Observatory of Lisbon has published a list detailing the +# historical transitions in legal time within continental Portugal. It +# directly references many decrees and ordinances which are, in turn, +# referenced below. They can be viewed in the public archives of the Diário da +# República (until 1976-04-09 known as the Diário do Govêrno) at +# https://dre.pt/ (in Portuguese). +# +# Most of the Rules below have been updated simply to match the Observatory's +# listing for continental (mainland) Portugal. Although there are over 50 +# referenced decrees and ordinances, only the handful with comments below have +# been verified against the text, typically to provide additional confidence +# wherever dates provided by Whitman and Shanks & Pottenger had disagreed. +# See further below for the Azores and Madeira. + +# From Tim Parenti (2024-07-01), per Paul Eggert (2014-08-11), after a +# heads-up from Stephen Colebourne: +# According to a 1911-05-24 Portuguese decree, Lisbon was at -0:36:44.68, but +# switched to GMT on 1912-01-01 at 00:00. +# https://dre.pt/dr/detalhe/decreto/593090 +# https://dre.pt/application/conteudo/593090 +# The decree made legal time throughout Portugal and her possessions +# "subordinate to the Greenwich meridian, according to the principle adopted at +# the Washington Convention in 1884" and eliminated the "difference of five +# minutes between the internal and external clocks of railway stations". +# +# The decree was gazetted in the 1911-05-30 issue of Diário do Govêrno, and is +# considered to be dated 1911-05-24 by that issue's summary; however, the text +# of the decree itself is dated 1911-05-26. The Diário da República website +# notes the discrepancy, but later laws and the Observatory all seem to refer +# to this decree by the 1911-05-24 date. # # From Michael Deckers (2018-02-15): # article 5 [of the 1911 decree; Deckers's translation] ...: @@ -2097,37 +2118,62 @@ Zone Europe/Warsaw 1:24:00 - LMT 1880 # according to the 2nd article, the civil day January 1, 1912 begins, # all clocks therefore having to be advanced or set back correspondingly ... -# From Rui Pedro Salgueiro (1992-11-12): -# Portugal has recently (September, 27) changed timezone -# (from WET to MET or CET) to harmonize with EEC. -# -# Martin Bruckmann (1996-02-29) reports via Peter Ilieve -# that Portugal is reverting to 0:00 by not moving its clocks this spring. -# The new Prime Minister was fed up with getting up in the dark in the winter. -# -# From Paul Eggert (1996-11-12): -# IATA SSIM (1991-09) reports several 1991-09 and 1992-09 transitions -# at 02:00u, not 01:00u. Assume that these are typos. -# IATA SSIM (1991/1992) reports that the Azores were at -1:00. -# IATA SSIM (1993-02) says +0:00; later issues (through 1996-09) say -1:00. -# Guess that the Azores changed to EU rules in 1992 (since that's when Portugal -# harmonized with EU rules), and that they stayed +0:00 that winter. -# # Rule NAME FROM TO - IN ON AT SAVE LETTER/S -# DSH writes that despite Decree 1,469 (1915), the change to the clocks was not -# done every year, depending on what Spain did, because of railroad schedules. -# Go with Shanks & Pottenger. +# From Tim Parenti (2024-07-01), per Paul Eggert (1999-01-30): +# DSH writes in their history that Decreto 1469 of 1915-03-30 established +# summer time and that, "despite" this, the change to the clocks was not done +# every year, depending on what Spain did, because of railroad schedules. +# In fact, that decree had nothing to do with DST; rather, it regulated the +# sending of time signals. But we do see linkage to Spain in the 1920s below. +# https://dre.pt/dr/detalhe/decreto/1469-1915-285721 +# https://dre.pt/application/conteudo/285721 +# +# According to the Observatory, standard time was first advanced by Decreto +# 2433 of 1916-06-09 and restored by Decreto 2712 of 1916-10-28. While Whitman +# gives 1916-10-31 for the latter transition, Shanks & Pottenger agrees more +# closely with the decree, which stated that its provision "will start sixty +# minutes after the end of 31 October, according to the current time," i.e., +# 01:00 on 1 November. +# https://dre.pt/dr/detalhe/decreto/2433-1916-267192 +# https://dre.pt/application/conteudo/267192 +# https://dre.pt/dr/detalhe/decreto/2712-1916-590937 +# https://dre.pt/application/conteudo/590937 Rule Port 1916 only - Jun 17 23:00 1:00 S -# Whitman gives 1916 Oct 31; go with Shanks & Pottenger. Rule Port 1916 only - Nov 1 1:00 0 - -Rule Port 1917 only - Feb 28 23:00s 1:00 S -Rule Port 1917 1921 - Oct 14 23:00s 0 - -Rule Port 1918 only - Mar 1 23:00s 1:00 S -Rule Port 1919 only - Feb 28 23:00s 1:00 S -Rule Port 1920 only - Feb 29 23:00s 1:00 S -Rule Port 1921 only - Feb 28 23:00s 1:00 S +# From Tim Parenti (2024-07-01): +# Article 7 of Decreto 2922 of 1916-12-30 stated that "the legal time will be +# advanced by sixty minutes from 1 March to 31 October." Per Article 15, this +# came into force from 1917-01-01. Just before the first fall back, Decreto +# 3446 of 1917-10-11 changed the annual end date to 14 October. +# https://dre.pt/dr/detalhe/decreto/2922-1916-261894 +# https://dre.pt/application/conteudo/261894 +# https://dre.pt/dr/detalhe/decreto/3446-1917-495161 +# https://dre.pt/application/conteudo/495161 +# This annual change was revoked by Decreto 8038 of 1922-02-18. +# https://dre.pt/dr/detalhe/decreto/8038-1922-569751 +# https://dre.pt/application/conteudo/569751 +Rule Port 1917 1921 - Mar 1 0:00 1:00 S +Rule Port 1917 1921 - Oct 14 24:00 0 - +# From Tim Parenti (2024-07-01): +# Decreto 9592 of 1924-04-14 noted that "France maintains the advance of legal +# time in the summer and Spain has now adopted it for the first time" and +# considered "that the absence of similar measures would cause serious +# difficulties for international rail connections with consequent repercussions +# on domestic service hours..." along with "inconvenient analogues...for postal +# and telegraph services." Summer time would be in effect from 17 April to 4 +# October, with the spring change explicitly specified by bringing clocks +# forward from 16 April 23:00. +# https://dre.pt/dr/detalhe/decreto/9592-1924-652133 +# https://dre.pt/application/conteudo/652133 +# +# Decreto 10700, issued 1925-04-16, noted that Spain had not continued summer +# time, declared that "the current legal hour prior to 17 April remains +# unchanged from that day forward", and revoked legislation to the contrary, +# just a day before summer time would have otherwise resumed. +# https://dre.pt/dr/detalhe/decreto/10700-1925-437826 +# https://dre.pt/application/conteudo/437826 Rule Port 1924 only - Apr 16 23:00s 1:00 S -Rule Port 1924 only - Oct 14 23:00s 0 - +Rule Port 1924 only - Oct 4 23:00s 0 - Rule Port 1926 only - Apr 17 23:00s 1:00 S Rule Port 1926 1929 - Oct Sat>=1 23:00s 0 - Rule Port 1927 only - Apr 9 23:00s 1:00 S @@ -2139,6 +2185,8 @@ Rule Port 1931 1932 - Oct Sat>=1 23:00s 0 - Rule Port 1932 only - Apr 2 23:00s 1:00 S Rule Port 1934 only - Apr 7 23:00s 1:00 S # Whitman gives 1934 Oct 5; go with Shanks & Pottenger. +# Note: The 1935 law specified 10-06 00:00, not 10-05 24:00, but the following +# is equivalent and more succinct. Rule Port 1934 1938 - Oct Sat>=1 23:00s 0 - # Shanks & Pottenger give 1935 Apr 30; go with Whitman. Rule Port 1935 only - Mar 30 23:00s 1:00 S @@ -2149,10 +2197,19 @@ Rule Port 1938 only - Mar 26 23:00s 1:00 S Rule Port 1939 only - Apr 15 23:00s 1:00 S # Whitman gives 1939 Oct 7; go with Shanks & Pottenger. Rule Port 1939 only - Nov 18 23:00s 0 - +# From Tim Parenti (2024-07-01): +# Portaria 9465 of 1940-02-17 advanced clocks from Saturday 1940-02-24 23:00. +# The clocks were restored by Portaria 9658, issued Monday 1940-10-07, +# effective from 24:00 that very night, which agrees with Shanks & Pottenger; +# Whitman gives Saturday 1940-10-05 instead. +# https://dre.pt/dr/detalhe/portaria/9465-1940-189096 +# https://dre.pt/application/conteudo/189096 +# https://dre.pt/dr/detalhe/portaria/9658-1940-196729 +# https://dre.pt/application/conteudo/196729 Rule Port 1940 only - Feb 24 23:00s 1:00 S -# Shanks & Pottenger give 1940 Oct 7; go with Whitman. -Rule Port 1940 1941 - Oct 5 23:00s 0 - +Rule Port 1940 only - Oct 7 23:00s 0 - Rule Port 1941 only - Apr 5 23:00s 1:00 S +Rule Port 1941 only - Oct 5 23:00s 0 - Rule Port 1942 1945 - Mar Sat>=8 23:00s 1:00 S Rule Port 1942 only - Apr 25 22:00s 2:00 M # Midsummer Rule Port 1942 only - Aug 15 22:00s 1:00 S @@ -2162,66 +2219,195 @@ Rule Port 1943 1945 - Aug Sat>=25 22:00s 1:00 S Rule Port 1944 1945 - Apr Sat>=21 22:00s 2:00 M Rule Port 1946 only - Apr Sat>=1 23:00s 1:00 S Rule Port 1946 only - Oct Sat>=1 23:00s 0 - -# Whitman says DST was not observed in 1950; go with Shanks & Pottenger. -# Whitman gives Oct lastSun for 1952 on; go with Shanks & Pottenger. -Rule Port 1947 1965 - Apr Sun>=1 2:00s 1:00 S +# From Tim Parenti (2024-07-01), per Alois Treindl (2021-02-07): +# The Astronomical Observatory of Lisbon cites Portaria 11767 of 1947-03-28 for +# 1947 and Portaria 12286 of 1948-02-19 for 1948. +# https://dre.pt/dr/detalhe/portaria/11767-1947-414787 +# https://dre.pt/application/conteudo/414787 +# https://dre.pt/dr/detalhe/portaria/12286-1948-152953 +# https://dre.pt/application/conteudo/152953 +# +# Although the latter ordinance explicitly had the 1948-10-03 transition +# scheduled for 02:00 rather than 03:00 as had been used in 1947, Decreto-Lei +# 37048 of 1948-09-07 recognized "that it is advisable to definitely set...the +# 'summer time' regime", and fixed the fall transition at 03:00 moving forward. +# https://dre.pt/dr/detalhe/decreto-lei/37048-1948-373810 +# https://dre.pt/application/conteudo/373810 +# While the Observatory only cites this act for 1949-1965 and not for 1948, it +# does not appear to have had any provision delaying its effect, so assume that +# it overrode the prior ordinance for 1948-10-03. +# +# Whitman says DST was not observed in 1950 and gives Oct lastSun for 1952 on. +# The Observatory, however, agrees with Shanks & Pottenger that 1950 was not an +# exception and that Oct Sun>=1 was maintained through 1965. +Rule Port 1947 1966 - Apr Sun>=1 2:00s 1:00 S Rule Port 1947 1965 - Oct Sun>=1 2:00s 0 - -Rule Port 1977 only - Mar 27 0:00s 1:00 S -Rule Port 1977 only - Sep 25 0:00s 0 - -Rule Port 1978 1979 - Apr Sun>=1 0:00s 1:00 S -Rule Port 1978 only - Oct 1 0:00s 0 - -Rule Port 1979 1982 - Sep lastSun 1:00s 0 - -Rule Port 1980 only - Mar lastSun 0:00s 1:00 S -Rule Port 1981 1982 - Mar lastSun 1:00s 1:00 S -Rule Port 1983 only - Mar lastSun 2:00s 1:00 S +# From Tim Parenti (2024-07-01): +# Decreto-Lei 47233 of 1966-10-01 considered that the "duality" in time was +# "the cause of serious disturbances" and noted that "the countries with which +# we have the most frequent contacts...have already adopted" a solution +# coinciding with the extant "summer time". It established that the former +# "summer time" would apply year-round on the mainland and adjacent islands +# with immediate effect, as the fall back would have otherwise occurred later +# that evening. +# https://dre.pt/dr/detalhe/decreto-lei/47233-1966-293729 +# Model this by changing zones without changing clocks at the +# previously-appointed fall back time. +# +# Decreto-Lei 309/76 of 1976-04-27 acknowledged that those international +# contacts had returned to adopting seasonal times, and considered that the +# year-round advancement "entails considerable sacrifices for the vast majority +# of the working population during the winter months", including morning +# visibility concerns for schoolchildren. It specified, beginning 1976-09-26 +# 01:00, an annual return to UT+00 on the mainland from 00:00 UT on Sep lastSun +# to 00:00 UT on Mar lastSun (unless the latter date fell on Easter, in which +# case it was to be brought forward to the preceding Sunday). It also assigned +# the Permanent Time Commission to study and propose revisions for the Azores +# and Madeira, neither of which resumed DST until 1982 (as described further +# below). +# https://dre.pt/dr/detalhe/decreto-lei/309-1976-502063 +Rule Port 1976 only - Sep lastSun 1:00 0 - +Rule Port 1977 only - Mar lastSun 0:00s 1:00 S +Rule Port 1977 only - Sep lastSun 0:00s 0 - +# From Tim Parenti (2024-07-01): +# Beginning in 1978, rather than triggering the Easter rule of the 1976 decree +# (Easter fell on 1978-03-26), Article 5 was used instead, which allowed DST +# dates to be changed by order of the Minister of Education and Scientific +# Research, upon consultation with the Permanent Time Commission, "whenever +# considered convenient." As such, a series of one-off ordinances were +# promulgated for the mainland in 1978 through 1980, after which the 1976 +# decree naturally came back into force from 1981. +Rule Port 1978 1980 - Apr Sun>=1 1:00s 1:00 S +Rule Port 1978 only - Oct 1 1:00s 0 - +Rule Port 1979 1980 - Sep lastSun 1:00s 0 - +Rule Port 1981 1986 - Mar lastSun 0:00s 1:00 S +Rule Port 1981 1985 - Sep lastSun 0:00s 0 - +# From Tim Parenti (2024-07-01): +# Decreto-Lei 44-B/86 of 1986-03-07 switched mainland Portugal's transition +# times from 0:00s to 1:00u to harmonize with the EEC from 1986-03-30. +# https://dre.pt/dr/detalhe/decreto-lei/44-b-1986-628280 +# (Transitions of 1:00s as previously reported and used by the W-Eur rules, +# though equivalent, appear to have been fiction here.) Madeira continued to +# use 0:00s for spring 1986 before joining with the mainland using 1:00u in the +# fall; meanwhile, in the Azores the two were equivalent, so the law specifying +# 0:00s wasn't touched until 1992. (See below for more on the islands.) +# +# From Rui Pedro Salgueiro (1992-11-12): +# Portugal has recently (September, 27) changed timezone +# (from WET to MET or CET) to harmonize with EEC. +# +# Martin Bruckmann (1996-02-29) reports via Peter Ilieve +# that Portugal is reverting to 0:00 by not moving its clocks this spring. +# The new Prime Minister was fed up with getting up in the dark in the winter. +# +# From Paul Eggert (1996-11-12): +# IATA SSIM (1991-09) reports several 1991-09 and 1992-09 transitions +# at 02:00u, not 01:00u. Assume that these are typos. # # Zone NAME STDOFF RULES FORMAT [UNTIL] #STDOFF -0:36:44.68 Zone Europe/Lisbon -0:36:45 - LMT 1884 -0:36:45 - LMT 1912 Jan 1 0:00u # Lisbon MT - 0:00 Port WE%sT 1966 Apr 3 2:00 + 0:00 Port WE%sT 1966 Oct 2 2:00s 1:00 - CET 1976 Sep 26 1:00 - 0:00 Port WE%sT 1983 Sep 25 1:00s - 0:00 W-Eur WE%sT 1992 Sep 27 1:00s + 0:00 Port WE%sT 1986 + 0:00 EU WE%sT 1992 Sep 27 1:00u 1:00 EU CE%sT 1996 Mar 31 1:00u 0:00 EU WE%sT + +# From Tim Parenti (2024-07-01): +# For the Azores and Madeira, legislation was followed from the laws currently +# in force as listed at: +# https://oal.ul.pt/hora-legal/legislacao/ +# working backward through references of revocation and abrogation to +# Decreto-Lei 47233 of 1966-10-01, the last time DST was abolished across the +# mainland and its adjacent islands. Because of that reference, it is +# therefore assumed that DST rules in the islands prior to 1966 were like that +# of the mainland, though most legislation of the time didn't explicitly +# specify DST practices for the islands. Zone Atlantic/Azores -1:42:40 - LMT 1884 # Ponta Delgada -1:54:32 - HMT 1912 Jan 1 2:00u # Horta MT # Vanguard section, for zic and other parsers that support %z. -# -2:00 Port %z 1966 Apr 3 2:00 -# -1:00 Port %z 1983 Sep 25 1:00s -# -1:00 W-Eur %z 1992 Sep 27 1:00s + -2:00 Port %z 1966 Oct 2 2:00s +# From Tim Parenti (2024-07-01): +# While Decreto-Lei 309/76 of 1976-04-27 reintroduced DST on the mainland by +# falling back on 1976-09-26, it assigned the Permanent Time Commission to +# study and propose revisions for the Azores and Madeira. Decreto Regional +# 9/77/A of 1977-05-17 affirmed that "the legal time remained unchanged in the +# Azores" at UT-1, and would remain there year-round. +# https://dre.pt/dr/detalhe/decreto-regional/9-1977-252066 +# +# Decreto Regional 2/82/A, published 1982-03-02, adopted DST in the same +# fashion as the mainland used at the time. +# https://dre.pt/dr/detalhe/decreto-regional/2-1982-599965 +# Though transitions in the Azores officially remained at 0:00s through 1992, +# this was equivalent to the EU-style 1:00u adopted by the mainland in 1986, so +# model it as such. + -1:00 - %z 1982 Mar 28 0:00s + -1:00 Port %z 1986 # Rearguard section, for parsers lacking %z; see ziguard.awk. - -2:00 Port -02/-01 1942 Apr 25 22:00s - -2:00 Port +00 1942 Aug 15 22:00s - -2:00 Port -02/-01 1943 Apr 17 22:00s - -2:00 Port +00 1943 Aug 28 22:00s - -2:00 Port -02/-01 1944 Apr 22 22:00s - -2:00 Port +00 1944 Aug 26 22:00s - -2:00 Port -02/-01 1945 Apr 21 22:00s - -2:00 Port +00 1945 Aug 25 22:00s - -2:00 Port -02/-01 1966 Apr 3 2:00 - -1:00 Port -01/+00 1983 Sep 25 1:00s - -1:00 W-Eur -01/+00 1992 Sep 27 1:00s +# -2:00 Port -02/-01 1942 Apr 25 22:00s +# -2:00 Port +00 1942 Aug 15 22:00s +# -2:00 Port -02/-01 1943 Apr 17 22:00s +# -2:00 Port +00 1943 Aug 28 22:00s +# -2:00 Port -02/-01 1944 Apr 22 22:00s +# -2:00 Port +00 1944 Aug 26 22:00s +# -2:00 Port -02/-01 1945 Apr 21 22:00s +# -2:00 Port +00 1945 Aug 25 22:00s +# -2:00 Port -02/-01 1966 Oct 2 2:00s +# -1:00 - -01 1982 Mar 28 0:00s +# -1:00 Port -01/+00 1986 # End of rearguard section. - 0:00 EU WE%sT 1993 Mar 28 1:00u - -1:00 EU -01/+00 +# +# From Paul Eggert (1996-11-12): +# IATA SSIM (1991/1992) reports that the Azores were at -1:00. +# IATA SSIM (1993-02) says +0:00; later issues (through 1996-09) say -1:00. +# +# From Tim Parenti (2024-07-01): +# After mainland Portugal had shifted forward an hour from 1992-09-27, Decreto +# Legislativo Regional 29/92/A of 1992-12-23 sought to "reduce the time +# difference" by shifting the Azores forward as well from 1992-12-27. Just six +# months later, this was revoked by Decreto Legislativo Regional 9/93/A, citing +# "major changes in work habits and way of life." Though the revocation didn't +# give a transition time, it was signed Wednesday 1993-06-16; assume it took +# effect later that evening, and that an EU-style spring forward (to +01) was +# still observed in the interim on 1993-03-28. +# https://dre.pt/dr/detalhe/decreto-legislativo-regional/29-1992-621553 +# https://dre.pt/dr/detalhe/decreto-legislativo-regional/9-1993-389633 + -1:00 EU %z 1992 Dec 27 1:00s + 0:00 EU WE%sT 1993 Jun 17 1:00u + -1:00 EU %z + Zone Atlantic/Madeira -1:07:36 - LMT 1884 # Funchal -1:07:36 - FMT 1912 Jan 1 1:00u # Funchal MT # Vanguard section, for zic and other parsers that support %z. -# -1:00 Port %z 1966 Apr 3 2:00 + -1:00 Port %z 1966 Oct 2 2:00s # Rearguard section, for parsers lacking %z; see ziguard.awk. - -1:00 Port -01/+00 1942 Apr 25 22:00s - -1:00 Port +01 1942 Aug 15 22:00s - -1:00 Port -01/+00 1943 Apr 17 22:00s - -1:00 Port +01 1943 Aug 28 22:00s - -1:00 Port -01/+00 1944 Apr 22 22:00s - -1:00 Port +01 1944 Aug 26 22:00s - -1:00 Port -01/+00 1945 Apr 21 22:00s - -1:00 Port +01 1945 Aug 25 22:00s - -1:00 Port -01/+00 1966 Apr 3 2:00 +# -1:00 Port -01/+00 1942 Apr 25 22:00s +# -1:00 Port +01 1942 Aug 15 22:00s +# -1:00 Port -01/+00 1943 Apr 17 22:00s +# -1:00 Port +01 1943 Aug 28 22:00s +# -1:00 Port -01/+00 1944 Apr 22 22:00s +# -1:00 Port +01 1944 Aug 26 22:00s +# -1:00 Port -01/+00 1945 Apr 21 22:00s +# -1:00 Port +01 1945 Aug 25 22:00s +# -1:00 Port -01/+00 1966 Oct 2 2:00s # End of rearguard section. - 0:00 Port WE%sT 1983 Sep 25 1:00s +# +# From Tim Parenti (2024-07-01): +# Decreto Regional 5/82/M, published 1982-04-03, established DST transitions at +# 0:00u, which for Madeira is equivalent to the mainland's rules (0:00s) at the +# time. It came into effect the day following its publication, Sunday +# 1982-04-04, thus resuming Madeira's DST practice about a week later than the +# mainland and the Azores. +# https://dre.pt/dr/detalhe/decreto-regional/5-1982-608273 +# +# Decreto Legislativo Regional 18/86/M, published 1986-10-01, adopted EU-style +# rules (1:00u) and entered into immediate force after being signed on +# 1986-07-31. +# https://dre.pt/dr/detalhe/decreto-legislativo-regional/18-1986-221705 + 0:00 - WET 1982 Apr 4 + 0:00 Port WE%sT 1986 Jul 31 0:00 EU WE%sT # Romania @@ -2433,7 +2619,7 @@ Zone Europe/Kaliningrad 1:22:00 - LMT 1893 Apr 2:00 Poland EE%sT 1946 Apr 7 3:00 Russia MSK/MSD 1989 Mar 26 2:00s 2:00 Russia EE%sT 2011 Mar 27 2:00s - 3:00 - +03 2014 Oct 26 2:00s + 3:00 - %z 2014 Oct 26 2:00s 2:00 - EET @@ -2683,14 +2869,14 @@ Zone Europe/Simferopol 2:16:24 - LMT 1880 # http://publication.pravo.gov.ru/Document/View/0001201602150056 Zone Europe/Astrakhan 3:12:12 - LMT 1924 May - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2016 Mar 27 2:00s - 4:00 - +04 + 3:00 - %z 1930 Jun 21 + 4:00 Russia %z 1989 Mar 26 2:00s + 3:00 Russia %z 1991 Mar 31 2:00s + 4:00 - %z 1992 Mar 29 2:00s + 3:00 Russia %z 2011 Mar 27 2:00s + 4:00 - %z 2014 Oct 26 2:00s + 3:00 - %z 2016 Mar 27 2:00s + 4:00 - %z # From Paul Eggert (2016-11-11): # Europe/Volgograd covers: @@ -2720,15 +2906,15 @@ Zone Europe/Astrakhan 3:12:12 - LMT 1924 May # http://publication.pravo.gov.ru/Document/View/0001202012220002 Zone Europe/Volgograd 2:57:40 - LMT 1920 Jan 3 - 3:00 - +03 1930 Jun 21 - 4:00 - +04 1961 Nov 11 - 4:00 Russia +04/+05 1988 Mar 27 2:00s + 3:00 - %z 1930 Jun 21 + 4:00 - %z 1961 Nov 11 + 4:00 Russia %z 1988 Mar 27 2:00s 3:00 Russia MSK/MSD 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s + 4:00 - %z 1992 Mar 29 2:00s 3:00 Russia MSK/MSD 2011 Mar 27 2:00s 4:00 - MSK 2014 Oct 26 2:00s 3:00 - MSK 2018 Oct 28 2:00s - 4:00 - +04 2020 Dec 27 2:00s + 4:00 - %z 2020 Dec 27 2:00s 3:00 - MSK # From Paul Eggert (2016-11-11): @@ -2743,14 +2929,14 @@ Zone Europe/Volgograd 2:57:40 - LMT 1920 Jan 3 # http://publication.pravo.gov.ru/Document/View/0001201611220031 Zone Europe/Saratov 3:04:18 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1988 Mar 27 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2016 Dec 4 2:00s - 4:00 - +04 + 3:00 - %z 1930 Jun 21 + 4:00 Russia %z 1988 Mar 27 2:00s + 3:00 Russia %z 1991 Mar 31 2:00s + 4:00 - %z 1992 Mar 29 2:00s + 3:00 Russia %z 2011 Mar 27 2:00s + 4:00 - %z 2014 Oct 26 2:00s + 3:00 - %z 2016 Dec 4 2:00s + 4:00 - %z # From Paul Eggert (2016-03-18): # Europe/Kirov covers: @@ -2758,10 +2944,10 @@ Zone Europe/Saratov 3:04:18 - LMT 1919 Jul 1 0:00u # The 1989 transition is from USSR act No. 227 (1989-03-14). # Zone Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1989 Mar 26 2:00s + 3:00 - %z 1930 Jun 21 + 4:00 Russia %z 1989 Mar 26 2:00s 3:00 Russia MSK/MSD 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s + 4:00 - %z 1992 Mar 29 2:00s 3:00 Russia MSK/MSD 2011 Mar 27 2:00s 4:00 - MSK 2014 Oct 26 2:00s 3:00 - MSK @@ -2776,15 +2962,15 @@ Zone Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0:00u # The 1989 transition is from USSR act No. 227 (1989-03-14). Zone Europe/Samara 3:20:20 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 - +04 1935 Jan 27 - 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 2:00 Russia +02/+03 1991 Sep 29 2:00s - 3:00 - +03 1991 Oct 20 3:00 - 4:00 Russia +04/+05 2010 Mar 28 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 + 3:00 - %z 1930 Jun 21 + 4:00 - %z 1935 Jan 27 + 4:00 Russia %z 1989 Mar 26 2:00s + 3:00 Russia %z 1991 Mar 31 2:00s + 2:00 Russia %z 1991 Sep 29 2:00s + 3:00 - %z 1991 Oct 20 3:00 + 4:00 Russia %z 2010 Mar 28 2:00s + 3:00 Russia %z 2011 Mar 27 2:00s + 4:00 - %z # From Paul Eggert (2016-03-18): # Europe/Ulyanovsk covers: @@ -2800,14 +2986,14 @@ Zone Europe/Samara 3:20:20 - LMT 1919 Jul 1 0:00u # http://publication.pravo.gov.ru/Document/View/0001201603090051 Zone Europe/Ulyanovsk 3:13:36 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 2:00 Russia +02/+03 1992 Jan 19 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2016 Mar 27 2:00s - 4:00 - +04 + 3:00 - %z 1930 Jun 21 + 4:00 Russia %z 1989 Mar 26 2:00s + 3:00 Russia %z 1991 Mar 31 2:00s + 2:00 Russia %z 1992 Jan 19 2:00s + 3:00 Russia %z 2011 Mar 27 2:00s + 4:00 - %z 2014 Oct 26 2:00s + 3:00 - %z 2016 Mar 27 2:00s + 4:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): # Asia/Yekaterinburg covers... @@ -2832,12 +3018,12 @@ Zone Europe/Ulyanovsk 3:13:36 - LMT 1919 Jul 1 0:00u #STDOFF 4:02:32.9 Zone Asia/Yekaterinburg 4:02:33 - LMT 1916 Jul 3 3:45:05 - PMT 1919 Jul 15 4:00 - 4:00 - +04 1930 Jun 21 - 5:00 Russia +05/+06 1991 Mar 31 2:00s - 4:00 Russia +04/+05 1992 Jan 19 2:00s - 5:00 Russia +05/+06 2011 Mar 27 2:00s - 6:00 - +06 2014 Oct 26 2:00s - 5:00 - +05 + 4:00 - %z 1930 Jun 21 + 5:00 Russia %z 1991 Mar 31 2:00s + 4:00 Russia %z 1992 Jan 19 2:00s + 5:00 Russia %z 2011 Mar 27 2:00s + 6:00 - %z 2014 Oct 26 2:00s + 5:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): @@ -2847,12 +3033,12 @@ Zone Asia/Yekaterinburg 4:02:33 - LMT 1916 Jul 3 # Byalokoz 1919 says Omsk was 4:53:30. Zone Asia/Omsk 4:53:30 - LMT 1919 Nov 14 - 5:00 - +05 1930 Jun 21 - 6:00 Russia +06/+07 1991 Mar 31 2:00s - 5:00 Russia +05/+06 1992 Jan 19 2:00s - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 + 5:00 - %z 1930 Jun 21 + 6:00 Russia %z 1991 Mar 31 2:00s + 5:00 Russia %z 1992 Jan 19 2:00s + 6:00 Russia %z 2011 Mar 27 2:00s + 7:00 - %z 2014 Oct 26 2:00s + 6:00 - %z # From Paul Eggert (2016-02-22): # Asia/Barnaul covers: @@ -2885,14 +3071,14 @@ Zone Asia/Omsk 4:53:30 - LMT 1919 Nov 14 # http://publication.pravo.gov.ru/Document/View/0001201603090038 Zone Asia/Barnaul 5:35:00 - LMT 1919 Dec 10 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 1995 May 28 - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 2016 Mar 27 2:00s - 7:00 - +07 + 6:00 - %z 1930 Jun 21 + 7:00 Russia %z 1991 Mar 31 2:00s + 6:00 Russia %z 1992 Jan 19 2:00s + 7:00 Russia %z 1995 May 28 + 6:00 Russia %z 2011 Mar 27 2:00s + 7:00 - %z 2014 Oct 26 2:00s + 6:00 - %z 2016 Mar 27 2:00s + 7:00 - %z # From Paul Eggert (2016-03-18): # Asia/Novosibirsk covers: @@ -2906,14 +3092,14 @@ Zone Asia/Barnaul 5:35:00 - LMT 1919 Dec 10 # http://publication.pravo.gov.ru/Document/View/0001201607040064 Zone Asia/Novosibirsk 5:31:40 - LMT 1919 Dec 14 6:00 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 1993 May 23 # say Shanks & P. - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 2016 Jul 24 2:00s - 7:00 - +07 + 6:00 - %z 1930 Jun 21 + 7:00 Russia %z 1991 Mar 31 2:00s + 6:00 Russia %z 1992 Jan 19 2:00s + 7:00 Russia %z 1993 May 23 # say Shanks & P. + 6:00 Russia %z 2011 Mar 27 2:00s + 7:00 - %z 2014 Oct 26 2:00s + 6:00 - %z 2016 Jul 24 2:00s + 7:00 - %z # From Paul Eggert (2016-03-18): # Asia/Tomsk covers: @@ -2958,14 +3144,14 @@ Zone Asia/Novosibirsk 5:31:40 - LMT 1919 Dec 14 6:00 # http://publication.pravo.gov.ru/Document/View/0001201604260048 Zone Asia/Tomsk 5:39:51 - LMT 1919 Dec 22 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 2002 May 1 3:00 - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 2016 May 29 2:00s - 7:00 - +07 + 6:00 - %z 1930 Jun 21 + 7:00 Russia %z 1991 Mar 31 2:00s + 6:00 Russia %z 1992 Jan 19 2:00s + 7:00 Russia %z 2002 May 1 3:00 + 6:00 Russia %z 2011 Mar 27 2:00s + 7:00 - %z 2014 Oct 26 2:00s + 6:00 - %z 2016 May 29 2:00s + 7:00 - %z # From Tim Parenti (2014-07-03): @@ -2996,12 +3182,12 @@ Zone Asia/Tomsk 5:39:51 - LMT 1919 Dec 22 # realigning itself with KRAT. Zone Asia/Novokuznetsk 5:48:48 - LMT 1924 May 1 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 2010 Mar 28 2:00s - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 + 6:00 - %z 1930 Jun 21 + 7:00 Russia %z 1991 Mar 31 2:00s + 6:00 Russia %z 1992 Jan 19 2:00s + 7:00 Russia %z 2010 Mar 28 2:00s + 6:00 Russia %z 2011 Mar 27 2:00s + 7:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): # Asia/Krasnoyarsk covers... @@ -3015,12 +3201,12 @@ Zone Asia/Novokuznetsk 5:48:48 - LMT 1924 May 1 # Byalokoz 1919 says Krasnoyarsk was 6:11:26. Zone Asia/Krasnoyarsk 6:11:26 - LMT 1920 Jan 6 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 2011 Mar 27 2:00s - 8:00 - +08 2014 Oct 26 2:00s - 7:00 - +07 + 6:00 - %z 1930 Jun 21 + 7:00 Russia %z 1991 Mar 31 2:00s + 6:00 Russia %z 1992 Jan 19 2:00s + 7:00 Russia %z 2011 Mar 27 2:00s + 8:00 - %z 2014 Oct 26 2:00s + 7:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): @@ -3037,12 +3223,12 @@ Zone Asia/Krasnoyarsk 6:11:26 - LMT 1920 Jan 6 Zone Asia/Irkutsk 6:57:05 - LMT 1880 6:57:05 - IMT 1920 Jan 25 # Irkutsk Mean Time - 7:00 - +07 1930 Jun 21 - 8:00 Russia +08/+09 1991 Mar 31 2:00s - 7:00 Russia +07/+08 1992 Jan 19 2:00s - 8:00 Russia +08/+09 2011 Mar 27 2:00s - 9:00 - +09 2014 Oct 26 2:00s - 8:00 - +08 + 7:00 - %z 1930 Jun 21 + 8:00 Russia %z 1991 Mar 31 2:00s + 7:00 Russia %z 1992 Jan 19 2:00s + 8:00 Russia %z 2011 Mar 27 2:00s + 9:00 - %z 2014 Oct 26 2:00s + 8:00 - %z # From Tim Parenti (2014-07-06): @@ -3059,13 +3245,13 @@ Zone Asia/Irkutsk 6:57:05 - LMT 1880 # http://publication.pravo.gov.ru/Document/View/0001201512300107 Zone Asia/Chita 7:33:52 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1991 Mar 31 2:00s - 8:00 Russia +08/+09 1992 Jan 19 2:00s - 9:00 Russia +09/+10 2011 Mar 27 2:00s - 10:00 - +10 2014 Oct 26 2:00s - 8:00 - +08 2016 Mar 27 2:00 - 9:00 - +09 + 8:00 - %z 1930 Jun 21 + 9:00 Russia %z 1991 Mar 31 2:00s + 8:00 Russia %z 1992 Jan 19 2:00s + 9:00 Russia %z 2011 Mar 27 2:00s + 10:00 - %z 2014 Oct 26 2:00s + 8:00 - %z 2016 Mar 27 2:00 + 9:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2009-11-29): @@ -3105,12 +3291,12 @@ Zone Asia/Chita 7:33:52 - LMT 1919 Dec 15 # Byalokoz 1919 says Yakutsk was 8:38:58. Zone Asia/Yakutsk 8:38:58 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1991 Mar 31 2:00s - 8:00 Russia +08/+09 1992 Jan 19 2:00s - 9:00 Russia +09/+10 2011 Mar 27 2:00s - 10:00 - +10 2014 Oct 26 2:00s - 9:00 - +09 + 8:00 - %z 1930 Jun 21 + 9:00 Russia %z 1991 Mar 31 2:00s + 8:00 Russia %z 1992 Jan 19 2:00s + 9:00 Russia %z 2011 Mar 27 2:00s + 10:00 - %z 2014 Oct 26 2:00s + 9:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2009-11-29): @@ -3128,12 +3314,12 @@ Zone Asia/Yakutsk 8:38:58 - LMT 1919 Dec 15 # Go with Byalokoz. Zone Asia/Vladivostok 8:47:31 - LMT 1922 Nov 15 - 9:00 - +09 1930 Jun 21 - 10:00 Russia +10/+11 1991 Mar 31 2:00s - 9:00 Russia +09/+10 1992 Jan 19 2:00s - 10:00 Russia +10/+11 2011 Mar 27 2:00s - 11:00 - +11 2014 Oct 26 2:00s - 10:00 - +10 + 9:00 - %z 1930 Jun 21 + 10:00 Russia %z 1991 Mar 31 2:00s + 9:00 Russia %z 1992 Jan 19 2:00s + 10:00 Russia %z 2011 Mar 27 2:00s + 11:00 - %z 2014 Oct 26 2:00s + 10:00 - %z # From Tim Parenti (2014-07-03): @@ -3151,14 +3337,14 @@ Zone Asia/Vladivostok 8:47:31 - LMT 1922 Nov 15 # This transition is no doubt wrong, but we have no better info. Zone Asia/Khandyga 9:02:13 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1991 Mar 31 2:00s - 8:00 Russia +08/+09 1992 Jan 19 2:00s - 9:00 Russia +09/+10 2004 - 10:00 Russia +10/+11 2011 Mar 27 2:00s - 11:00 - +11 2011 Sep 13 0:00s # Decree 725? - 10:00 - +10 2014 Oct 26 2:00s - 9:00 - +09 + 8:00 - %z 1930 Jun 21 + 9:00 Russia %z 1991 Mar 31 2:00s + 8:00 Russia %z 1992 Jan 19 2:00s + 9:00 Russia %z 2004 + 10:00 Russia %z 2011 Mar 27 2:00s + 11:00 - %z 2011 Sep 13 0:00s # Decree 725? + 10:00 - %z 2014 Oct 26 2:00s + 9:00 - %z # From Tim Parenti (2014-07-03): @@ -3174,14 +3360,14 @@ Zone Asia/Khandyga 9:02:13 - LMT 1919 Dec 15 # The Zone name should be Asia/Yuzhno-Sakhalinsk, but that's too long. Zone Asia/Sakhalin 9:30:48 - LMT 1905 Aug 23 - 9:00 - +09 1945 Aug 25 - 11:00 Russia +11/+12 1991 Mar 31 2:00s # Sakhalin T - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 1997 Mar lastSun 2:00s - 10:00 Russia +10/+11 2011 Mar 27 2:00s - 11:00 - +11 2014 Oct 26 2:00s - 10:00 - +10 2016 Mar 27 2:00s - 11:00 - +11 + 9:00 - %z 1945 Aug 25 + 11:00 Russia %z 1991 Mar 31 2:00s # Sakhalin T + 10:00 Russia %z 1992 Jan 19 2:00s + 11:00 Russia %z 1997 Mar lastSun 2:00s + 10:00 Russia %z 2011 Mar 27 2:00s + 11:00 - %z 2014 Oct 26 2:00s + 10:00 - %z 2016 Mar 27 2:00s + 11:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2009-11-29): @@ -3204,13 +3390,13 @@ Zone Asia/Sakhalin 9:30:48 - LMT 1905 Aug 23 # http://publication.pravo.gov.ru/Document/View/0001201604050038 Zone Asia/Magadan 10:03:12 - LMT 1924 May 2 - 10:00 - +10 1930 Jun 21 # Magadan Time - 11:00 Russia +11/+12 1991 Mar 31 2:00s - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 2014 Oct 26 2:00s - 10:00 - +10 2016 Apr 24 2:00s - 11:00 - +11 + 10:00 - %z 1930 Jun 21 # Magadan Time + 11:00 Russia %z 1991 Mar 31 2:00s + 10:00 Russia %z 1992 Jan 19 2:00s + 11:00 Russia %z 2011 Mar 27 2:00s + 12:00 - %z 2014 Oct 26 2:00s + 10:00 - %z 2016 Apr 24 2:00s + 11:00 - %z # From Tim Parenti (2014-07-06): @@ -3255,12 +3441,12 @@ Zone Asia/Magadan 10:03:12 - LMT 1924 May 2 # Go with Srednekolymsk. Zone Asia/Srednekolymsk 10:14:52 - LMT 1924 May 2 - 10:00 - +10 1930 Jun 21 - 11:00 Russia +11/+12 1991 Mar 31 2:00s - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 2014 Oct 26 2:00s - 11:00 - +11 + 10:00 - %z 1930 Jun 21 + 11:00 Russia %z 1991 Mar 31 2:00s + 10:00 Russia %z 1992 Jan 19 2:00s + 11:00 Russia %z 2011 Mar 27 2:00s + 12:00 - %z 2014 Oct 26 2:00s + 11:00 - %z # From Tim Parenti (2014-07-03): @@ -3278,14 +3464,14 @@ Zone Asia/Srednekolymsk 10:14:52 - LMT 1924 May 2 # UTC+12 since at least then, too. Zone Asia/Ust-Nera 9:32:54 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1981 Apr 1 - 11:00 Russia +11/+12 1991 Mar 31 2:00s - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 2011 Sep 13 0:00s # Decree 725? - 11:00 - +11 2014 Oct 26 2:00s - 10:00 - +10 + 8:00 - %z 1930 Jun 21 + 9:00 Russia %z 1981 Apr 1 + 11:00 Russia %z 1991 Mar 31 2:00s + 10:00 Russia %z 1992 Jan 19 2:00s + 11:00 Russia %z 2011 Mar 27 2:00s + 12:00 - %z 2011 Sep 13 0:00s # Decree 725? + 11:00 - %z 2014 Oct 26 2:00s + 10:00 - %z # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): @@ -3298,12 +3484,12 @@ Zone Asia/Ust-Nera 9:32:54 - LMT 1919 Dec 15 # The Zone name should be Asia/Petropavlovsk-Kamchatski or perhaps # Asia/Petropavlovsk-Kamchatsky, but these are too long. Zone Asia/Kamchatka 10:34:36 - LMT 1922 Nov 10 - 11:00 - +11 1930 Jun 21 - 12:00 Russia +12/+13 1991 Mar 31 2:00s - 11:00 Russia +11/+12 1992 Jan 19 2:00s - 12:00 Russia +12/+13 2010 Mar 28 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 + 11:00 - %z 1930 Jun 21 + 12:00 Russia %z 1991 Mar 31 2:00s + 11:00 Russia %z 1992 Jan 19 2:00s + 12:00 Russia %z 2010 Mar 28 2:00s + 11:00 Russia %z 2011 Mar 27 2:00s + 12:00 - %z # From Tim Parenti (2014-07-03): @@ -3311,13 +3497,13 @@ Zone Asia/Kamchatka 10:34:36 - LMT 1922 Nov 10 # 87 RU-CHU Chukotka Autonomous Okrug Zone Asia/Anadyr 11:49:56 - LMT 1924 May 2 - 12:00 - +12 1930 Jun 21 - 13:00 Russia +13/+14 1982 Apr 1 0:00s - 12:00 Russia +12/+13 1991 Mar 31 2:00s - 11:00 Russia +11/+12 1992 Jan 19 2:00s - 12:00 Russia +12/+13 2010 Mar 28 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 + 12:00 - %z 1930 Jun 21 + 13:00 Russia %z 1982 Apr 1 0:00s + 12:00 Russia %z 1991 Mar 31 2:00s + 11:00 Russia %z 1992 Jan 19 2:00s + 12:00 Russia %z 2010 Mar 28 2:00s + 11:00 Russia %z 2011 Mar 27 2:00s + 12:00 - %z # Bosnia & Herzegovina # Croatia @@ -3436,7 +3622,7 @@ Zone Africa/Ceuta -0:21:16 - LMT 1901 Jan 1 0:00u 1:00 - CET 1986 1:00 EU CE%sT Zone Atlantic/Canary -1:01:36 - LMT 1922 Mar # Las Palmas de Gran C. - -1:00 - -01 1946 Sep 30 1:00 + -1:00 - %z 1946 Sep 30 1:00 0:00 - WET 1980 Apr 6 0:00s 0:00 1:00 WEST 1980 Sep 28 1:00u 0:00 EU WE%sT @@ -3517,8 +3703,8 @@ Zone Atlantic/Canary -1:01:36 - LMT 1922 Mar # Las Palmas de Gran C. # but if no one is present after 11 at night, could be postponed until one # hour before the beginning of service. -# From Paul Eggert (2013-09-11): -# Round BMT to the nearest even second, 0:29:46. +# From Paul Eggert (2024-05-24): +# Express BMT as 0:29:45.500, approximately the same precision 7° 26' 22.50". # # We can find no reliable source for Shanks's assertion that all of Switzerland # except Geneva switched to Bern Mean Time at 00:00 on 1848-09-12. This book: @@ -3557,6 +3743,7 @@ Rule Swiss 1941 1942 - May Mon>=1 1:00 1:00 S Rule Swiss 1941 1942 - Oct Mon>=1 2:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Europe/Zurich 0:34:08 - LMT 1853 Jul 16 # See above comment. + #STDOFF 0:29:45.500 0:29:46 - BMT 1894 Jun # Bern Mean Time 1:00 Swiss CE%sT 1981 1:00 EU CE%sT @@ -3754,7 +3941,7 @@ Rule Turkey 1996 2006 - Oct lastSun 1:00s 0 - Zone Europe/Istanbul 1:55:52 - LMT 1880 1:56:56 - IMT 1910 Oct # Istanbul Mean Time? 2:00 Turkey EE%sT 1978 Jun 29 - 3:00 Turkey +03/+04 1984 Nov 1 2:00 + 3:00 Turkey %z 1984 Nov 1 2:00 2:00 Turkey EE%sT 2007 2:00 EU EE%sT 2011 Mar 27 1:00u 2:00 - EET 2011 Mar 28 1:00u @@ -3763,7 +3950,7 @@ Zone Europe/Istanbul 1:55:52 - LMT 1880 2:00 EU EE%sT 2015 Oct 25 1:00u 2:00 1:00 EEST 2015 Nov 8 1:00u 2:00 EU EE%sT 2016 Sep 7 - 3:00 - +03 + 3:00 - %z # Ukraine # diff --git a/make/data/tzdata/factory b/make/data/tzdata/factory index a05346a301d20..e5e7d88f5f6dd 100644 --- a/make/data/tzdata/factory +++ b/make/data/tzdata/factory @@ -31,5 +31,15 @@ # time zone abbreviation "-00", indicating that the actual time zone # is unknown. +# TZ="Factory" was added to TZDB in 1989, and in 2016 its abbreviation +# was changed to "-00" from a longish English-language error message. +# Around 2010, CLDR added "Etc/Unknown" for use with TZDB, to stand +# for an unknown or invalid time zone. These two notions differ: +# TZ="Factory" is a valid timezone, so tzalloc("Factory") succeeds, whereas +# TZ="Etc/Unknown" is invalid and tzalloc("Etc/Unknown") fails. +# Also, a downstream distributor could modify Factory to be a +# default timezone suitable for the devices it manufactures, +# whereas that cannot happen for Etc/Unknown. + # Zone NAME STDOFF RULES FORMAT Zone Factory 0 - -00 diff --git a/make/data/tzdata/leapseconds b/make/data/tzdata/leapseconds index 8e7df3de984fb..042a32b052c02 100644 --- a/make/data/tzdata/leapseconds +++ b/make/data/tzdata/leapseconds @@ -92,11 +92,11 @@ Leap 2016 Dec 31 23:59:60 + S # Any additional leap seconds will come after this. # This Expires line is commented out for now, # so that pre-2020a zic implementations do not reject this file. -#Expires 2024 Dec 28 00:00:00 +#Expires 2025 Dec 28 00:00:00 # POSIX timestamps for the data in this file: -#updated 1704708379 (2024-01-08 10:06:19 UTC) -#expires 1735344000 (2024-12-28 00:00:00 UTC) +#updated 1736208000 (2025-01-07 00:00:00 UTC) +#expires 1766880000 (2025-12-28 00:00:00 UTC) # Updated through IERS Bulletin C (https://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat) -# File expires on 28 December 2024 +# File expires on 28 December 2025 diff --git a/make/data/tzdata/northamerica b/make/data/tzdata/northamerica index a8b2ef3f7fa5f..0a54e63becc86 100644 --- a/make/data/tzdata/northamerica +++ b/make/data/tzdata/northamerica @@ -50,9 +50,12 @@ # in New York City (1869-10). His 1870 proposal was based on Washington, DC, # but in 1872-05 he moved the proposed origin to Greenwich. -# From Paul Eggert (2018-03-20): +# From Paul Eggert (2024-11-18): # Dowd's proposal left many details unresolved, such as where to draw -# lines between time zones. The key individual who made time zones +# lines between time zones. Sandford Fleming of the Canadian Pacific Railway +# argued for Dowd's proposal in 1876, and Cleveland Abbe of the American +# Meteorology Society published a report in 1879 recommending four US time +# zones based on GMT. However, the key individual who made time zones # work in the US was William Frederick Allen - railway engineer, # managing editor of the Travelers' Guide, and secretary of the # General Time Convention, a railway standardization group. Allen @@ -208,26 +211,6 @@ Rule US 1987 2006 - Apr Sun>=1 2:00 1:00 D Rule US 2007 max - Mar Sun>=8 2:00 1:00 D Rule US 2007 max - Nov Sun>=1 2:00 0 S -# From Arthur David Olson, 2005-12-19 -# We generate the files specified below to guard against old files with -# obsolete information being left in the time zone binary directory. -# We limit the list to names that have appeared in previous versions of -# this time zone package. -# We do these as separate Zones rather than as Links to avoid problems if -# a particular place changes whether it observes DST. -# We put these specifications here in the northamerica file both to -# increase the chances that they'll actually get compiled and to -# avoid the need to duplicate the US rules in another file. - -# Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone EST -5:00 - EST -Zone MST -7:00 - MST -Zone HST -10:00 - HST -Zone EST5EDT -5:00 US E%sT -Zone CST6CDT -6:00 US C%sT -Zone MST7MDT -7:00 US M%sT -Zone PST8PDT -8:00 US P%sT - # From U. S. Naval Observatory (1989-01-19): # USA EASTERN 5 H BEHIND UTC NEW YORK, WASHINGTON # USA EASTERN 4 H BEHIND UTC APR 3 - OCT 30 @@ -2396,6 +2379,81 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 # the researchers who prepared the Decrees page failed to find some of # the relevant documents. +# From Heitor David Pinto (2024-08-04): +# In 1931, the decree implementing DST specified that it would take +# effect on 30 April.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?cod_diario=192270&pagina=2&seccion=1 +# +# In 1981, the decree changing Campeche, Yucatán and Quintana Roo to UTC-5 +# specified that it would enter into force on 26 December 1981 at 2:00.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4705667&fecha=23/12/1981&cod_diario=202796 +# +# In 1982, the decree returning Campeche and Yucatán to UTC-6 specified that +# it would enter into force on 2 November 1982 at 2:00.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?cod_diario=205689&pagina=3&seccion=0 +# +# Quintana Roo changed to UTC-6 on 4 January 1983 at 0:00, and again +# to UTC-5 on 26 October 1997 at 2:00.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4787355&fecha=28/12/1982&cod_diario=206112 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?cod_diario=209559&pagina=15&seccion=0 +# +# Durango, Coahuila, Nuevo León and Tamaulipas were set to UTC-7 on 1 January +# 1922, and changed to UTC-6 on 10 June 1927. Then Durango, Coahuila and +# Nuevo León (but not Tamaulipas) returned to UTC-7 on 15 November 1930, +# observed DST in 1931, and changed again to UTC-6 on 1 April 1932.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4441846&fecha=29/12/1921&cod_diario=187468 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4541520&fecha=09/06/1927&cod_diario=193920 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4491963&fecha=15/11/1930&cod_diario=190835 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4418437&fecha=21/01/1932&cod_diario=185588 +# +# ... the ... 10 June 1927 ... decree only said 10 June 1927, without +# specifying a time, so I suppose that it should be considered at 0:00. +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4541520&fecha=09/06/1927&cod_diario=193920 +# +# In 1942, the decree changing Baja California, Baja California Sur, Sonora, +# Sinaloa and Nayarit to UTC-7 was published on 24 April, but it said that it +# would apply from 1 April, so it's unclear when the change actually +# occurred. The database currently shows 24 April 1942. +# https://www.dof.gob.mx/nota_to_imagen_fs.php?cod_diario=192203&pagina=2&seccion=1 +# +# Baja California Sur, Sonora, Sinaloa and Nayarit never used UTC-8. The ... +# 14 January 1949 ... change [to UTC-8] only occurred in Baja California. +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4515613&fecha=13/01/1949&cod_diario=192309 +# +# In 1945, the decree changing Baja California to UTC-8 specified that it +# would take effect on the third day from its publication. +# It was published on 12 November, so it would take effect on 15 November.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4555049&fecha=12/11/1945&cod_diario=194763 +# +# In 1948, the decree changing Baja California to UTC-7 specified that it +# would take effect on "this date". The decree was made on 13 March, +# but published on 5 April, so it's unclear when the change actually occurred. +# The database currently shows 5 April 1948. +# https://www.dof.gob.mx/nota_to_imagen_fs.php?cod_diario=188624&pagina=2&seccion=0 +# +# In 1949, the decree changing Baja California to UTC-8 was published on 13 +# January, but it said that it would apply from 1 January, so it's unclear when +# the change actually occurred. The database currently shows 14 January 1949. +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4515613&fecha=13/01/1949&cod_diario=192309 +# +# Baja California also observed UTC-7 from 1 May to 24 September 1950, +# from 29 April to 30 September 1951 at 2:00, +# and from 27 April to 28 September 1952 at 2:00.... +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4600403&fecha=29/04/1950&cod_diario=197505 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4623553&fecha=23/09/1950&cod_diario=198805 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4469444&fecha=27/04/1951&cod_diario=189317 +# https://www.dof.gob.mx/nota_to_imagen_fs.php?codnota=4533868&fecha=10/03/1952&cod_diario=193465 +# +# All changes in Baja California from 1948 to 1952 match those in California, +# on the same dates or with a difference of one day. +# So it may be easier to implement these changes as DST with rule CA +# during this whole period. +# +# From Paul Eggert (2024-08-18): +# For now, maintain the slightly-different history for Baja California, +# as we have no information on whether 1948/1952 clocks in Tijuana followed +# the decrees or followed San Diego. + # From Alan Perry (1996-02-15): # A guy from our Mexico subsidiary finally found the Presidential Decree # outlining the timezone changes in Mexico. @@ -2599,7 +2657,7 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 # http://puentelibre.mx/noticia/ciudad_juarez_cambio_horario_noviembre_2022/ # Rule NAME FROM TO - IN ON AT SAVE LETTER/S -Rule Mexico 1931 only - May 1 23:00 1:00 D +Rule Mexico 1931 only - Apr 30 0:00 1:00 D Rule Mexico 1931 only - Oct 1 0:00 0 S Rule Mexico 1939 only - Feb 5 0:00 1:00 D Rule Mexico 1939 only - Jun 25 0:00 0 S @@ -2618,14 +2676,16 @@ Rule Mexico 2002 2022 - Oct lastSun 2:00 0 S # Zone NAME STDOFF RULES FORMAT [UNTIL] # Quintana Roo; represented by Cancún Zone America/Cancun -5:47:04 - LMT 1922 Jan 1 6:00u - -6:00 - CST 1981 Dec 23 + -6:00 - CST 1981 Dec 26 2:00 + -5:00 - EST 1983 Jan 4 0:00 + -6:00 Mexico C%sT 1997 Oct 26 2:00 -5:00 Mexico E%sT 1998 Aug 2 2:00 -6:00 Mexico C%sT 2015 Feb 1 2:00 -5:00 - EST # Campeche, Yucatán; represented by Mérida Zone America/Merida -5:58:28 - LMT 1922 Jan 1 6:00u - -6:00 - CST 1981 Dec 23 - -5:00 - EST 1982 Dec 2 + -6:00 - CST 1981 Dec 26 2:00 + -5:00 - EST 1982 Nov 2 2:00 -6:00 Mexico C%sT # Coahuila, Nuevo León, Tamaulipas (near US border) # This includes the following municipios: @@ -2642,12 +2702,15 @@ Zone America/Matamoros -6:30:00 - LMT 1922 Jan 1 6:00u -6:00 US C%sT # Durango; Coahuila, Nuevo León, Tamaulipas (away from US border) Zone America/Monterrey -6:41:16 - LMT 1922 Jan 1 6:00u + -7:00 - MST 1927 Jun 10 + -6:00 - CST 1930 Nov 15 + -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1988 -6:00 US C%sT 1989 -6:00 Mexico C%sT # Central Mexico Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 Mexico C%sT 2001 Sep 30 2:00 @@ -2658,7 +2721,7 @@ Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 7:00u # Práxedis G Guerrero. # http://gaceta.diputados.gob.mx/PDF/65/2a022/nov/20221124-VII.pdf Zone America/Ciudad_Juarez -7:05:56 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1996 @@ -2673,7 +2736,7 @@ Zone America/Ciudad_Juarez -7:05:56 - LMT 1922 Jan 1 7:00u # Benavides. # http://gaceta.diputados.gob.mx/PDF/65/2a022/nov/20221124-VII.pdf Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1996 @@ -2685,7 +2748,7 @@ Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 7:00u -6:00 US C%sT # Chihuahua (away from US border) Zone America/Chihuahua -7:04:20 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1996 @@ -2695,23 +2758,21 @@ Zone America/Chihuahua -7:04:20 - LMT 1922 Jan 1 7:00u -6:00 - CST # Sonora Zone America/Hermosillo -7:23:52 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1942 Apr 24 - -7:00 - MST 1949 Jan 14 - -8:00 - PST 1970 + -7:00 - MST 1996 -7:00 Mexico M%sT 1999 -7:00 - MST # Baja California Sur, Nayarit (except Bahía de Banderas), Sinaloa Zone America/Mazatlan -7:05:40 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1942 Apr 24 - -7:00 - MST 1949 Jan 14 - -8:00 - PST 1970 + -7:00 - MST 1970 -7:00 Mexico M%sT # Bahía de Banderas @@ -2744,27 +2805,32 @@ Zone America/Mazatlan -7:05:40 - LMT 1922 Jan 1 7:00u # Use "Bahia_Banderas" to keep the name to fourteen characters. Zone America/Bahia_Banderas -7:01:00 - LMT 1922 Jan 1 7:00u - -7:00 - MST 1927 Jun 10 23:00 + -7:00 - MST 1927 Jun 10 -6:00 - CST 1930 Nov 15 -7:00 Mexico M%sT 1932 Apr 1 -6:00 - CST 1942 Apr 24 - -7:00 - MST 1949 Jan 14 - -8:00 - PST 1970 + -7:00 - MST 1970 -7:00 Mexico M%sT 2010 Apr 4 2:00 -6:00 Mexico C%sT # Baja California Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 7:00u -7:00 - MST 1924 - -8:00 - PST 1927 Jun 10 23:00 + -8:00 - PST 1927 Jun 10 -7:00 - MST 1930 Nov 15 -8:00 - PST 1931 Apr 1 -8:00 1:00 PDT 1931 Sep 30 -8:00 - PST 1942 Apr 24 -8:00 1:00 PWT 1945 Aug 14 23:00u - -8:00 1:00 PPT 1945 Nov 12 # Peace + -8:00 1:00 PPT 1945 Nov 15 # Peace -8:00 - PST 1948 Apr 5 -8:00 1:00 PDT 1949 Jan 14 + -8:00 - PST 1950 May 1 + -8:00 1:00 PDT 1950 Sep 24 + -8:00 - PST 1951 Apr 29 2:00 + -8:00 1:00 PDT 1951 Sep 30 2:00 + -8:00 - PST 1952 Apr 27 2:00 + -8:00 1:00 PDT 1952 Sep 28 2:00 -8:00 - PST 1954 -8:00 CA P%sT 1961 -8:00 - PST 1976 @@ -3573,8 +3639,8 @@ Zone America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12:00 # San Juan # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Miquelon -3:44:40 - LMT 1911 Jun 15 # St Pierre -4:00 - AST 1980 May - -3:00 - -03 1987 - -3:00 Canada -03/-02 + -3:00 - %z 1987 + -3:00 Canada %z # Turks and Caicos # diff --git a/make/data/tzdata/southamerica b/make/data/tzdata/southamerica index d77acc088570b..0a5859600e8ff 100644 --- a/make/data/tzdata/southamerica +++ b/make/data/tzdata/southamerica @@ -425,11 +425,11 @@ Rule Arg 2008 only - Oct Sun>=15 0:00 1:00 - Zone America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May # Córdoba Mean Time - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 Arg %z # # Córdoba (CB), Santa Fe (SF), Entre Ríos (ER), Corrientes (CN), Misiones (MN), # Chaco (CC), Formosa (FM), Santiago del Estero (SE) @@ -444,120 +444,120 @@ Zone America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 Zone America/Argentina/Cordoba -4:16:48 - LMT 1894 Oct 31 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 3 + -4:00 - %z 1991 Oct 20 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 Arg %z # # Salta (SA), La Pampa (LP), Neuquén (NQ), Rio Negro (RN) Zone America/Argentina/Salta -4:21:40 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 3 + -4:00 - %z 1991 Oct 20 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # Tucumán (TM) Zone America/Argentina/Tucuman -4:20:52 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 13 - -3:00 Arg -03/-02 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 3 + -4:00 - %z 1991 Oct 20 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 Jun 1 + -4:00 - %z 2004 Jun 13 + -3:00 Arg %z # # La Rioja (LR) Zone America/Argentina/La_Rioja -4:27:24 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 1 - -4:00 - -04 1991 May 7 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 1 + -4:00 - %z 1991 May 7 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 Jun 1 + -4:00 - %z 2004 Jun 20 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # San Juan (SJ) Zone America/Argentina/San_Juan -4:34:04 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 1 - -4:00 - -04 1991 May 7 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 May 31 - -4:00 - -04 2004 Jul 25 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 1 + -4:00 - %z 1991 May 7 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 May 31 + -4:00 - %z 2004 Jul 25 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # Jujuy (JY) Zone America/Argentina/Jujuy -4:21:12 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1990 Mar 4 - -4:00 - -04 1990 Oct 28 - -4:00 1:00 -03 1991 Mar 17 - -4:00 - -04 1991 Oct 6 - -3:00 1:00 -02 1992 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1990 Mar 4 + -4:00 - %z 1990 Oct 28 + -4:00 1:00 %z 1991 Mar 17 + -4:00 - %z 1991 Oct 6 + -3:00 1:00 %z 1992 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # Catamarca (CT), Chubut (CH) Zone America/Argentina/Catamarca -4:23:08 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1991 Mar 3 + -4:00 - %z 1991 Oct 20 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 Jun 1 + -4:00 - %z 2004 Jun 20 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # Mendoza (MZ) Zone America/Argentina/Mendoza -4:35:16 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1990 Mar 4 - -4:00 - -04 1990 Oct 15 - -4:00 1:00 -03 1991 Mar 1 - -4:00 - -04 1991 Oct 15 - -4:00 1:00 -03 1992 Mar 1 - -4:00 - -04 1992 Oct 18 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 May 23 - -4:00 - -04 2004 Sep 26 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1990 Mar 4 + -4:00 - %z 1990 Oct 15 + -4:00 1:00 %z 1991 Mar 1 + -4:00 - %z 1991 Oct 15 + -4:00 1:00 %z 1992 Mar 1 + -4:00 - %z 1992 Oct 18 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 May 23 + -4:00 - %z 2004 Sep 26 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # San Luis (SL) @@ -567,53 +567,53 @@ Rule SanLuis 2007 2008 - Oct Sun>=8 0:00 1:00 - Zone America/Argentina/San_Luis -4:25:24 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1990 - -3:00 1:00 -02 1990 Mar 14 - -4:00 - -04 1990 Oct 15 - -4:00 1:00 -03 1991 Mar 1 - -4:00 - -04 1991 Jun 1 - -3:00 - -03 1999 Oct 3 - -4:00 1:00 -03 2000 Mar 3 - -3:00 - -03 2004 May 31 - -4:00 - -04 2004 Jul 25 - -3:00 Arg -03/-02 2008 Jan 21 - -4:00 SanLuis -04/-03 2009 Oct 11 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1990 + -3:00 1:00 %z 1990 Mar 14 + -4:00 - %z 1990 Oct 15 + -4:00 1:00 %z 1991 Mar 1 + -4:00 - %z 1991 Jun 1 + -3:00 - %z 1999 Oct 3 + -4:00 1:00 %z 2000 Mar 3 + -3:00 - %z 2004 May 31 + -4:00 - %z 2004 Jul 25 + -3:00 Arg %z 2008 Jan 21 + -4:00 SanLuis %z 2009 Oct 11 + -3:00 - %z # # Santa Cruz (SC) Zone America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 Jun 1 + -4:00 - %z 2004 Jun 20 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # # Tierra del Fuego, Antártida e Islas del Atlántico Sur (TF) Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31 #STDOFF -4:16:48.25 -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 May 30 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 + -4:00 - %z 1930 Dec + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1999 Oct 3 + -4:00 Arg %z 2000 Mar 3 + -3:00 - %z 2004 May 30 + -4:00 - %z 2004 Jun 20 + -3:00 Arg %z 2008 Oct 18 + -3:00 - %z # Bolivia # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/La_Paz -4:32:36 - LMT 1890 -4:32:36 - CMT 1931 Oct 15 # Calamarca MT -4:32:36 1:00 BST 1932 Mar 21 # Bolivia ST - -4:00 - -04 + -4:00 - %z # Brazil @@ -984,12 +984,12 @@ Rule Brazil 2018 only - Nov Sun>=1 0:00 1:00 - # # Fernando de Noronha (administratively part of PE) Zone America/Noronha -2:09:40 - LMT 1914 - -2:00 Brazil -02/-01 1990 Sep 17 - -2:00 - -02 1999 Sep 30 - -2:00 Brazil -02/-01 2000 Oct 15 - -2:00 - -02 2001 Sep 13 - -2:00 Brazil -02/-01 2002 Oct 1 - -2:00 - -02 + -2:00 Brazil %z 1990 Sep 17 + -2:00 - %z 1999 Sep 30 + -2:00 Brazil %z 2000 Oct 15 + -2:00 - %z 2001 Sep 13 + -2:00 Brazil %z 2002 Oct 1 + -2:00 - %z # Other Atlantic islands have no permanent settlement. # These include Trindade and Martim Vaz (administratively part of ES), # Rocas Atoll (RN), and the St Peter and St Paul Archipelago (PE). @@ -1002,119 +1002,119 @@ Zone America/Noronha -2:09:40 - LMT 1914 # In the north a very small part from the river Javary (now Jari I guess, # the border with Amapá) to the Amazon, then to the Xingu. Zone America/Belem -3:13:56 - LMT 1914 - -3:00 Brazil -03/-02 1988 Sep 12 - -3:00 - -03 + -3:00 Brazil %z 1988 Sep 12 + -3:00 - %z # # west Pará (PA) # West Pará includes Altamira, Óbidos, Prainha, Oriximiná, and Santarém. Zone America/Santarem -3:38:48 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 2008 Jun 24 0:00 - -3:00 - -03 + -4:00 Brazil %z 1988 Sep 12 + -4:00 - %z 2008 Jun 24 0:00 + -3:00 - %z # # Maranhão (MA), Piauí (PI), Ceará (CE), Rio Grande do Norte (RN), # Paraíba (PB) Zone America/Fortaleza -2:34:00 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1999 Sep 30 - -3:00 Brazil -03/-02 2000 Oct 22 - -3:00 - -03 2001 Sep 13 - -3:00 Brazil -03/-02 2002 Oct 1 - -3:00 - -03 + -3:00 Brazil %z 1990 Sep 17 + -3:00 - %z 1999 Sep 30 + -3:00 Brazil %z 2000 Oct 22 + -3:00 - %z 2001 Sep 13 + -3:00 Brazil %z 2002 Oct 1 + -3:00 - %z # # Pernambuco (PE) (except Atlantic islands) Zone America/Recife -2:19:36 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1999 Sep 30 - -3:00 Brazil -03/-02 2000 Oct 15 - -3:00 - -03 2001 Sep 13 - -3:00 Brazil -03/-02 2002 Oct 1 - -3:00 - -03 + -3:00 Brazil %z 1990 Sep 17 + -3:00 - %z 1999 Sep 30 + -3:00 Brazil %z 2000 Oct 15 + -3:00 - %z 2001 Sep 13 + -3:00 Brazil %z 2002 Oct 1 + -3:00 - %z # # Tocantins (TO) Zone America/Araguaina -3:12:48 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1995 Sep 14 - -3:00 Brazil -03/-02 2003 Sep 24 - -3:00 - -03 2012 Oct 21 - -3:00 Brazil -03/-02 2013 Sep - -3:00 - -03 + -3:00 Brazil %z 1990 Sep 17 + -3:00 - %z 1995 Sep 14 + -3:00 Brazil %z 2003 Sep 24 + -3:00 - %z 2012 Oct 21 + -3:00 Brazil %z 2013 Sep + -3:00 - %z # # Alagoas (AL), Sergipe (SE) Zone America/Maceio -2:22:52 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1995 Oct 13 - -3:00 Brazil -03/-02 1996 Sep 4 - -3:00 - -03 1999 Sep 30 - -3:00 Brazil -03/-02 2000 Oct 22 - -3:00 - -03 2001 Sep 13 - -3:00 Brazil -03/-02 2002 Oct 1 - -3:00 - -03 + -3:00 Brazil %z 1990 Sep 17 + -3:00 - %z 1995 Oct 13 + -3:00 Brazil %z 1996 Sep 4 + -3:00 - %z 1999 Sep 30 + -3:00 Brazil %z 2000 Oct 22 + -3:00 - %z 2001 Sep 13 + -3:00 Brazil %z 2002 Oct 1 + -3:00 - %z # # Bahia (BA) # There are too many Salvadors elsewhere, so use America/Bahia instead # of America/Salvador. Zone America/Bahia -2:34:04 - LMT 1914 - -3:00 Brazil -03/-02 2003 Sep 24 - -3:00 - -03 2011 Oct 16 - -3:00 Brazil -03/-02 2012 Oct 21 - -3:00 - -03 + -3:00 Brazil %z 2003 Sep 24 + -3:00 - %z 2011 Oct 16 + -3:00 Brazil %z 2012 Oct 21 + -3:00 - %z # # Goiás (GO), Distrito Federal (DF), Minas Gerais (MG), # Espírito Santo (ES), Rio de Janeiro (RJ), São Paulo (SP), Paraná (PR), # Santa Catarina (SC), Rio Grande do Sul (RS) Zone America/Sao_Paulo -3:06:28 - LMT 1914 - -3:00 Brazil -03/-02 1963 Oct 23 0:00 - -3:00 1:00 -02 1964 - -3:00 Brazil -03/-02 + -3:00 Brazil %z 1963 Oct 23 0:00 + -3:00 1:00 %z 1964 + -3:00 Brazil %z # # Mato Grosso do Sul (MS) Zone America/Campo_Grande -3:38:28 - LMT 1914 - -4:00 Brazil -04/-03 + -4:00 Brazil %z # # Mato Grosso (MT) Zone America/Cuiaba -3:44:20 - LMT 1914 - -4:00 Brazil -04/-03 2003 Sep 24 - -4:00 - -04 2004 Oct 1 - -4:00 Brazil -04/-03 + -4:00 Brazil %z 2003 Sep 24 + -4:00 - %z 2004 Oct 1 + -4:00 Brazil %z # # Rondônia (RO) Zone America/Porto_Velho -4:15:36 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 + -4:00 Brazil %z 1988 Sep 12 + -4:00 - %z # # Roraima (RR) Zone America/Boa_Vista -4:02:40 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 1999 Sep 30 - -4:00 Brazil -04/-03 2000 Oct 15 - -4:00 - -04 + -4:00 Brazil %z 1988 Sep 12 + -4:00 - %z 1999 Sep 30 + -4:00 Brazil %z 2000 Oct 15 + -4:00 - %z # # east Amazonas (AM): Boca do Acre, Jutaí, Manaus, Floriano Peixoto # The great circle line from Tabatinga to Porto Acre divides # east from west Amazonas. Zone America/Manaus -4:00:04 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 1993 Sep 28 - -4:00 Brazil -04/-03 1994 Sep 22 - -4:00 - -04 + -4:00 Brazil %z 1988 Sep 12 + -4:00 - %z 1993 Sep 28 + -4:00 Brazil %z 1994 Sep 22 + -4:00 - %z # # west Amazonas (AM): Atalaia do Norte, Boca do Maoco, Benjamin Constant, # Eirunepé, Envira, Ipixuna Zone America/Eirunepe -4:39:28 - LMT 1914 - -5:00 Brazil -05/-04 1988 Sep 12 - -5:00 - -05 1993 Sep 28 - -5:00 Brazil -05/-04 1994 Sep 22 - -5:00 - -05 2008 Jun 24 0:00 - -4:00 - -04 2013 Nov 10 - -5:00 - -05 + -5:00 Brazil %z 1988 Sep 12 + -5:00 - %z 1993 Sep 28 + -5:00 Brazil %z 1994 Sep 22 + -5:00 - %z 2008 Jun 24 0:00 + -4:00 - %z 2013 Nov 10 + -5:00 - %z # # Acre (AC) Zone America/Rio_Branco -4:31:12 - LMT 1914 - -5:00 Brazil -05/-04 1988 Sep 12 - -5:00 - -05 2008 Jun 24 0:00 - -4:00 - -04 2013 Nov 10 - -5:00 - -05 + -5:00 Brazil %z 1988 Sep 12 + -5:00 - %z 2008 Jun 24 0:00 + -4:00 - %z 2013 Nov 10 + -5:00 - %z # Chile @@ -1382,36 +1382,36 @@ Rule Chile 2023 max - Sep Sun>=2 4:00u 1:00 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Santiago -4:42:45 - LMT 1890 -4:42:45 - SMT 1910 Jan 10 # Santiago Mean Time - -5:00 - -05 1916 Jul 1 + -5:00 - %z 1916 Jul 1 -4:42:45 - SMT 1918 Sep 10 - -4:00 - -04 1919 Jul 1 + -4:00 - %z 1919 Jul 1 -4:42:45 - SMT 1927 Sep 1 - -5:00 Chile -05/-04 1932 Sep 1 - -4:00 - -04 1942 Jun 1 - -5:00 - -05 1942 Aug 1 - -4:00 - -04 1946 Jul 14 24:00 - -4:00 1:00 -03 1946 Aug 28 24:00 # central CL - -5:00 1:00 -04 1947 Mar 31 24:00 - -5:00 - -05 1947 May 21 23:00 - -4:00 Chile -04/-03 + -5:00 Chile %z 1932 Sep 1 + -4:00 - %z 1942 Jun 1 + -5:00 - %z 1942 Aug 1 + -4:00 - %z 1946 Jul 14 24:00 + -4:00 1:00 %z 1946 Aug 28 24:00 # central CL + -5:00 1:00 %z 1947 Mar 31 24:00 + -5:00 - %z 1947 May 21 23:00 + -4:00 Chile %z Zone America/Punta_Arenas -4:43:40 - LMT 1890 -4:42:45 - SMT 1910 Jan 10 - -5:00 - -05 1916 Jul 1 + -5:00 - %z 1916 Jul 1 -4:42:45 - SMT 1918 Sep 10 - -4:00 - -04 1919 Jul 1 + -4:00 - %z 1919 Jul 1 -4:42:45 - SMT 1927 Sep 1 - -5:00 Chile -05/-04 1932 Sep 1 - -4:00 - -04 1942 Jun 1 - -5:00 - -05 1942 Aug 1 - -4:00 - -04 1946 Aug 28 24:00 - -5:00 1:00 -04 1947 Mar 31 24:00 - -5:00 - -05 1947 May 21 23:00 - -4:00 Chile -04/-03 2016 Dec 4 - -3:00 - -03 + -5:00 Chile %z 1932 Sep 1 + -4:00 - %z 1942 Jun 1 + -5:00 - %z 1942 Aug 1 + -4:00 - %z 1946 Aug 28 24:00 + -5:00 1:00 %z 1947 Mar 31 24:00 + -5:00 - %z 1947 May 21 23:00 + -4:00 Chile %z 2016 Dec 4 + -3:00 - %z Zone Pacific/Easter -7:17:28 - LMT 1890 -7:17:28 - EMT 1932 Sep # Easter Mean Time - -7:00 Chile -07/-06 1982 Mar 14 3:00u # Easter Time - -6:00 Chile -06/-05 + -7:00 Chile %z 1982 Mar 14 3:00u # Easter Time + -6:00 Chile %z # # Salas y Gómez Island is uninhabited. # Other Chilean locations, including Juan Fernández Is, Desventuradas Is, @@ -1431,10 +1431,10 @@ Zone Pacific/Easter -7:17:28 - LMT 1890 # # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Antarctica/Palmer 0 - -00 1965 - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1982 May - -4:00 Chile -04/-03 2016 Dec 4 - -3:00 - -03 + -4:00 Arg %z 1969 Oct 5 + -3:00 Arg %z 1982 May + -4:00 Chile %z 2016 Dec 4 + -3:00 - %z # Colombia @@ -1453,7 +1453,7 @@ Rule CO 1993 only - Feb 6 24:00 0 - #STDOFF -4:56:16.4 Zone America/Bogota -4:56:16 - LMT 1884 Mar 13 -4:56:16 - BMT 1914 Nov 23 # Bogotá Mean Time - -5:00 CO -05/-04 + -5:00 CO %z # Malpelo, Providencia, San Andres # no information; probably like America/Bogota @@ -1484,10 +1484,10 @@ Rule Ecuador 1993 only - Feb 5 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Guayaquil -5:19:20 - LMT 1890 -5:14:00 - QMT 1931 # Quito Mean Time - -5:00 Ecuador -05/-04 + -5:00 Ecuador %z Zone Pacific/Galapagos -5:58:24 - LMT 1931 # Puerto Baquerizo Moreno - -5:00 - -05 1986 - -6:00 Ecuador -06/-05 + -5:00 - %z 1986 + -6:00 Ecuador %z # Falklands @@ -1587,10 +1587,10 @@ Rule Falk 2001 2010 - Sep Sun>=1 2:00 1:00 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Atlantic/Stanley -3:51:24 - LMT 1890 -3:51:24 - SMT 1912 Mar 12 # Stanley Mean Time - -4:00 Falk -04/-03 1983 May - -3:00 Falk -03/-02 1985 Sep 15 - -4:00 Falk -04/-03 2010 Sep 5 2:00 - -3:00 - -03 + -4:00 Falk %z 1983 May + -3:00 Falk %z 1985 Sep 15 + -4:00 Falk %z 2010 Sep 5 2:00 + -3:00 - %z # French Guiana # For the 1911/1912 establishment of standard time in French possessions, see: @@ -1598,8 +1598,8 @@ Zone Atlantic/Stanley -3:51:24 - LMT 1890 # page 752, 18b. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Cayenne -3:29:20 - LMT 1911 Jul 1 - -4:00 - -04 1967 Oct - -3:00 - -03 + -4:00 - %z 1967 Oct + -3:00 - %z # Guyana @@ -1633,10 +1633,10 @@ Zone America/Cayenne -3:29:20 - LMT 1911 Jul 1 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Guyana -3:52:39 - LMT 1911 Aug 1 # Georgetown - -4:00 - -04 1915 Mar 1 - -3:45 - -0345 1975 Aug 1 - -3:00 - -03 1992 Mar 29 1:00 - -4:00 - -04 + -4:00 - %z 1915 Mar 1 + -3:45 - %z 1975 Aug 1 + -3:00 - %z 1992 Mar 29 1:00 + -4:00 - %z # Paraguay # @@ -1710,7 +1710,7 @@ Rule Para 2005 2009 - Mar Sun>=8 0:00 0 - # and that on the first Sunday of the month of October, it is to be set # forward 60 minutes, in all the territory of the Paraguayan Republic. # ... -Rule Para 2010 max - Oct Sun>=1 0:00 1:00 - +Rule Para 2010 2024 - Oct Sun>=1 0:00 1:00 - Rule Para 2010 2012 - Apr Sun>=8 0:00 0 - # # From Steffen Thorsen (2013-03-07): @@ -1729,14 +1729,35 @@ Rule Para 2010 2012 - Apr Sun>=8 0:00 0 - # https://www.abc.com.py/politica/2023/07/12/promulgacion-el-cambio-de-hora-sera-por-ley/ # From Carlos Raúl Perasso (2023-07-27): # http://silpy.congreso.gov.py/descarga/ley-144138 -Rule Para 2013 max - Mar Sun>=22 0:00 0 - +Rule Para 2013 2024 - Mar Sun>=22 0:00 0 - +# +# From Heitor David Pinto (2024-09-24): +# Today the Congress of Paraguay passed a bill to observe UTC-3 permanently.... +# The text of the bill says that it would enter into force on the first +# Sunday in October 2024, the same date currently scheduled to start DST.... +# https://silpy.congreso.gov.py/web/expediente/132531 +# (2024-10-14): +# The president approved the law on 11 October 2024, +# and it was officially published on 14 October 2024. +# https://www.gacetaoficial.gov.py/index/detalle_publicacion/89723 +# The text of the law says that it enters into force on the first +# Sunday in October 2024 (6 October 2024). But the constitution +# prohibits retroactive effect, and the civil code says that laws +# enter into force on the day after their publication or on the day +# that they specify, and it also says that they don't have retroactive +# effect. So I think that the time change on 6 October 2024 should +# still be considered as DST according to the previous law, and +# permanently UTC-3 from 15 October 2024 according to the new law.... +# https://www.constituteproject.org/constitution/Paraguay_2011 +# https://www.oas.org/dil/esp/codigo_civil_paraguay.pdf # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Asuncion -3:50:40 - LMT 1890 -3:50:40 - AMT 1931 Oct 10 # Asunción Mean Time - -4:00 - -04 1972 Oct - -3:00 - -03 1974 Apr - -4:00 Para -04/-03 + -4:00 - %z 1972 Oct + -3:00 - %z 1974 Apr + -4:00 Para %z 2024 Oct 15 + -3:00 - %z # Peru # @@ -1763,12 +1784,12 @@ Rule Peru 1994 only - Apr 1 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Lima -5:08:12 - LMT 1890 -5:08:36 - LMT 1908 Jul 28 # Lima Mean Time? - -5:00 Peru -05/-04 + -5:00 Peru %z # South Georgia # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Atlantic/South_Georgia -2:26:08 - LMT 1890 # Grytviken - -2:00 - -02 + -2:00 - %z # South Sandwich Is # uninhabited; scientific personnel have wintered @@ -1778,8 +1799,8 @@ Zone Atlantic/South_Georgia -2:26:08 - LMT 1890 # Grytviken Zone America/Paramaribo -3:40:40 - LMT 1911 -3:40:52 - PMT 1935 # Paramaribo Mean Time -3:40:36 - PMT 1945 Oct # The capital moved? - -3:30 - -0330 1984 Oct - -3:00 - -03 + -3:30 - %z 1984 Oct + -3:00 - %z # Uruguay # From Paul Eggert (1993-11-18): @@ -1994,15 +2015,15 @@ Rule Uruguay 2006 2014 - Oct Sun>=1 2:00 1:00 - # This Zone can be simplified once we assume zic %z. Zone America/Montevideo -3:44:51 - LMT 1908 Jun 10 -3:44:51 - MMT 1920 May 1 # Montevideo MT - -4:00 - -04 1923 Oct 1 - -3:30 Uruguay -0330/-03 1942 Dec 14 - -3:00 Uruguay -03/-0230 1960 - -3:00 Uruguay -03/-02 1968 - -3:00 Uruguay -03/-0230 1970 - -3:00 Uruguay -03/-02 1974 - -3:00 Uruguay -03/-0130 1974 Mar 10 - -3:00 Uruguay -03/-0230 1974 Dec 22 - -3:00 Uruguay -03/-02 + -4:00 - %z 1923 Oct 1 + -3:30 Uruguay %z 1942 Dec 14 + -3:00 Uruguay %z 1960 + -3:00 Uruguay %z 1968 + -3:00 Uruguay %z 1970 + -3:00 Uruguay %z 1974 + -3:00 Uruguay %z 1974 Mar 10 + -3:00 Uruguay %z 1974 Dec 22 + -3:00 Uruguay %z # Venezuela # @@ -2036,7 +2057,7 @@ Zone America/Montevideo -3:44:51 - LMT 1908 Jun 10 # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Caracas -4:27:44 - LMT 1890 -4:27:40 - CMT 1912 Feb 12 # Caracas Mean Time? - -4:30 - -0430 1965 Jan 1 0:00 - -4:00 - -04 2007 Dec 9 3:00 - -4:30 - -0430 2016 May 1 2:30 - -4:00 - -04 + -4:30 - %z 1965 Jan 1 0:00 + -4:00 - %z 2007 Dec 9 3:00 + -4:30 - %z 2016 May 1 2:30 + -4:00 - %z diff --git a/make/data/tzdata/zone.tab b/make/data/tzdata/zone.tab index 0a01e8777dd25..e7a4868c39d0e 100644 --- a/make/data/tzdata/zone.tab +++ b/make/data/tzdata/zone.tab @@ -287,8 +287,7 @@ MK +4159+02126 Europe/Skopje ML +1239-00800 Africa/Bamako MM +1647+09610 Asia/Yangon MN +4755+10653 Asia/Ulaanbaatar most of Mongolia -MN +4801+09139 Asia/Hovd Bayan-Olgiy, Govi-Altai, Hovd, Uvs, Zavkhan -MN +4804+11430 Asia/Choibalsan Dornod, Sukhbaatar +MN +4801+09139 Asia/Hovd Bayan-Olgii, Hovd, Uvs MO +221150+1133230 Asia/Macau MP +1512+14545 Pacific/Saipan MQ +1436-06105 America/Martinique @@ -334,7 +333,7 @@ PF -0900-13930 Pacific/Marquesas Marquesas Islands PF -2308-13457 Pacific/Gambier Gambier Islands PG -0930+14710 Pacific/Port_Moresby most of Papua New Guinea PG -0613+15534 Pacific/Bougainville Bougainville -PH +1435+12100 Asia/Manila +PH +143512+1205804 Asia/Manila PK +2452+06703 Asia/Karachi PL +5215+02100 Europe/Warsaw PM +4703-05620 America/Miquelon diff --git a/make/devkit/createJMHBundle.sh b/make/devkit/createJMHBundle.sh index b2b10769d1533..889b7f914a41b 100644 --- a/make/devkit/createJMHBundle.sh +++ b/make/devkit/createJMHBundle.sh @@ -44,7 +44,7 @@ rm -f * fetchJar() { url="${MAVEN_MIRROR}/$1/$2/$3/$2-$3.jar" if command -v curl > /dev/null; then - curl -O --fail $url + curl -OL --fail $url elif command -v wget > /dev/null; then wget $url else diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index 65edd047571c3..508be1090925b 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -87,6 +87,13 @@ ifeq ($(call check-jvm-feature, zero), true) DISABLED_WARNINGS_gcc += return-type switch clobbered endif +ifeq ($(DEBUG_LEVEL), fastdebug) + ifeq ($(call And, $(call isTargetOs, linux) $(call isTargetCpu, aarch64)), true) + # False positive warnings for atomic_linux_aarch64.hpp on GCC >= 13 + DISABLED_WARNINGS_gcc += stringop-overflow + endif +endif + DISABLED_WARNINGS_clang := tautological-compare \ undefined-var-template sometimes-uninitialized unknown-pragmas \ delete-non-virtual-dtor missing-braces char-subscripts \ @@ -151,6 +158,8 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \ arguments.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \ DISABLED_WARNINGS_gcc := $(DISABLED_WARNINGS_gcc), \ DISABLED_WARNINGS_clang := $(DISABLED_WARNINGS_clang), \ + DISABLED_WARNINGS_clang_notificationThread.cpp := bitwise-instead-of-logical, \ + DISABLED_WARNINGS_clang_serviceThread.cpp := bitwise-instead-of-logical, \ DISABLED_WARNINGS_xlc := $(DISABLED_WARNINGS_xlc), \ DISABLED_WARNINGS_microsoft := $(DISABLED_WARNINGS_microsoft), \ ASFLAGS := $(JVM_ASFLAGS), \ diff --git a/make/hotspot/lib/JvmFlags.gmk b/make/hotspot/lib/JvmFlags.gmk index 0c292ad866ca3..943e8dcbb8c80 100644 --- a/make/hotspot/lib/JvmFlags.gmk +++ b/make/hotspot/lib/JvmFlags.gmk @@ -67,10 +67,12 @@ JVM_CFLAGS_TARGET_DEFINES += \ # ifeq ($(DEBUG_LEVEL), release) + # release builds disable uses of assert macro from . + JVM_CFLAGS_DEBUGLEVEL := -DNDEBUG # For hotspot, release builds differ internally between "optimized" and "product" # in that "optimize" does not define PRODUCT. ifneq ($(HOTSPOT_DEBUG_LEVEL), optimized) - JVM_CFLAGS_DEBUGLEVEL := -DPRODUCT + JVM_CFLAGS_DEBUGLEVEL += -DPRODUCT endif else ifeq ($(DEBUG_LEVEL), fastdebug) JVM_CFLAGS_DEBUGLEVEL := -DASSERT diff --git a/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java b/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java index 39aa5e35d6e75..b7a97f50ba20c 100644 --- a/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java +++ b/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -273,7 +273,7 @@ private void outputFile(Path dstFile, String version, // link version-region-rules out.writeShort(builtZones.size()); for (Map.Entry entry : builtZones.entrySet()) { - int regionIndex = Arrays.binarySearch(regionArray, entry.getKey()); + int regionIndex = findRegionIndex(regionArray, entry.getKey()); int rulesIndex = rulesList.indexOf(entry.getValue()); out.writeShort(regionIndex); out.writeShort(rulesIndex); @@ -281,8 +281,8 @@ private void outputFile(Path dstFile, String version, // alias-region out.writeShort(links.size()); for (Map.Entry entry : links.entrySet()) { - int aliasIndex = Arrays.binarySearch(regionArray, entry.getKey()); - int regionIndex = Arrays.binarySearch(regionArray, entry.getValue()); + int aliasIndex = findRegionIndex(regionArray, entry.getKey()); + int regionIndex = findRegionIndex(regionArray, entry.getValue()); out.writeShort(aliasIndex); out.writeShort(regionIndex); } @@ -294,6 +294,14 @@ private void outputFile(Path dstFile, String version, } } + private static int findRegionIndex(String[] regionArray, String region) { + int index = Arrays.binarySearch(regionArray, region); + if (index < 0) { + throw new IllegalArgumentException("Unknown region: " + region); + } + return index; + } + /** Whether to output verbose messages. */ private boolean verbose; diff --git a/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesProvider.java b/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesProvider.java index d82e58e34206a..ecca3c69c065f 100644 --- a/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesProvider.java +++ b/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -164,7 +164,8 @@ private void load(List files) throws IOException { } continue; } - if (line.startsWith("Zone")) { // parse Zone line + int token0len = tokens.length > 0 ? tokens[0].length() : line.length(); + if (line.regionMatches(true, 0, "Zone", 0, token0len)) { // parse Zone line String name = tokens[1]; if (excludedZones.contains(name)){ continue; @@ -182,13 +183,13 @@ private void load(List files) throws IOException { if (zLine.parse(tokens, 2)) { openZone = null; } - } else if (line.startsWith("Rule")) { // parse Rule line + } else if (line.regionMatches(true, 0, "Rule", 0, token0len)) { // parse Rule line String name = tokens[1]; if (!rules.containsKey(name)) { rules.put(name, new ArrayList(10)); } rules.get(name).add(new RuleLine().parse(tokens)); - } else if (line.startsWith("Link")) { // parse link line + } else if (line.regionMatches(true, 0, "Link", 0, token0len)) { // parse link line if (tokens.length >= 3) { String realId = tokens[1]; String aliasId = tokens[2]; @@ -304,7 +305,7 @@ private void parse(String[] tokens, int off) { month = parseMonth(tokens[off++]); if (off < tokens.length) { String dayRule = tokens[off++]; - if (dayRule.startsWith("last")) { + if (dayRule.regionMatches(true, 0, "last", 0, 4)) { dayOfMonth = -1; dayOfWeek = parseDayOfWeek(dayRule.substring(4)); adjustForwards = false; @@ -355,42 +356,45 @@ private void parse(String[] tokens, int off) { } int parseYear(String year, int defaultYear) { - switch (year.toLowerCase()) { - case "min": return 1900; - case "max": return Year.MAX_VALUE; - case "only": return defaultYear; - } + int len = year.length(); + + if (year.regionMatches(true, 0, "minimum", 0, len)) return 1900; + if (year.regionMatches(true, 0, "maximum", 0, len)) return Year.MAX_VALUE; + if (year.regionMatches(true, 0, "only", 0, len)) return defaultYear; + return Integer.parseInt(year); } Month parseMonth(String mon) { - switch (mon) { - case "Jan": return Month.JANUARY; - case "Feb": return Month.FEBRUARY; - case "Mar": return Month.MARCH; - case "Apr": return Month.APRIL; - case "May": return Month.MAY; - case "Jun": return Month.JUNE; - case "Jul": return Month.JULY; - case "Aug": return Month.AUGUST; - case "Sep": return Month.SEPTEMBER; - case "Oct": return Month.OCTOBER; - case "Nov": return Month.NOVEMBER; - case "Dec": return Month.DECEMBER; - } + int len = mon.length(); + + if (mon.regionMatches(true, 0, "January", 0, len)) return Month.JANUARY; + if (mon.regionMatches(true, 0, "February", 0, len)) return Month.FEBRUARY; + if (mon.regionMatches(true, 0, "March", 0, len)) return Month.MARCH; + if (mon.regionMatches(true, 0, "April", 0, len)) return Month.APRIL; + if (mon.regionMatches(true, 0, "May", 0, len)) return Month.MAY; + if (mon.regionMatches(true, 0, "June", 0, len)) return Month.JUNE; + if (mon.regionMatches(true, 0, "July", 0, len)) return Month.JULY; + if (mon.regionMatches(true, 0, "August", 0, len)) return Month.AUGUST; + if (mon.regionMatches(true, 0, "September", 0, len)) return Month.SEPTEMBER; + if (mon.regionMatches(true, 0, "October", 0, len)) return Month.OCTOBER; + if (mon.regionMatches(true, 0, "November", 0, len)) return Month.NOVEMBER; + if (mon.regionMatches(true, 0, "December", 0, len)) return Month.DECEMBER; + throw new IllegalArgumentException("Unknown month: " + mon); } DayOfWeek parseDayOfWeek(String dow) { - switch (dow) { - case "Mon": return DayOfWeek.MONDAY; - case "Tue": return DayOfWeek.TUESDAY; - case "Wed": return DayOfWeek.WEDNESDAY; - case "Thu": return DayOfWeek.THURSDAY; - case "Fri": return DayOfWeek.FRIDAY; - case "Sat": return DayOfWeek.SATURDAY; - case "Sun": return DayOfWeek.SUNDAY; - } + int len = dow.length(); + + if (dow.regionMatches(true, 0, "Monday", 0, len)) return DayOfWeek.MONDAY; + if (dow.regionMatches(true, 0, "Tuesday", 0, len)) return DayOfWeek.TUESDAY; + if (dow.regionMatches(true, 0, "Wednesday", 0, len)) return DayOfWeek.WEDNESDAY; + if (dow.regionMatches(true, 0, "Thursday", 0, len)) return DayOfWeek.THURSDAY; + if (dow.regionMatches(true, 0, "Friday", 0, len)) return DayOfWeek.FRIDAY; + if (dow.regionMatches(true, 0, "Saturday", 0, len)) return DayOfWeek.SATURDAY; + if (dow.regionMatches(true, 0, "Sunday", 0, len)) return DayOfWeek.SUNDAY; + throw new IllegalArgumentException("Unknown day-of-week: " + dow); } diff --git a/make/modules/java.base/Launcher.gmk b/make/modules/java.base/Launcher.gmk index 700ddefda49e8..1ab3846c14377 100644 --- a/make/modules/java.base/Launcher.gmk +++ b/make/modules/java.base/Launcher.gmk @@ -78,7 +78,8 @@ ifeq ($(call isTargetOs, macosx aix linux), true) NAME := jspawnhelper, \ SRC := $(TOPDIR)/src/$(MODULE)/unix/native/jspawnhelper, \ OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKEXE) -I$(TOPDIR)/src/$(MODULE)/unix/native/libjava, \ + CFLAGS := $(CFLAGS_JDKEXE) $(VERSION_CFLAGS) \ + -I$(TOPDIR)/src/$(MODULE)/unix/native/libjava, \ EXTRA_OBJECT_FILES := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjava/childproc.o, \ LDFLAGS := $(LDFLAGS_JDKEXE), \ OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_libs/$(MODULE), \ diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk index bb09d8cf8a2ee..c1db028fff810 100644 --- a/make/modules/java.base/lib/CoreLibraries.gmk +++ b/make/modules/java.base/lib/CoreLibraries.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -92,6 +92,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \ CFLAGS := $(CFLAGS_JDKLIB) \ $(LIBJAVA_CFLAGS), \ jdk_util.c_CFLAGS := $(VERSION_CFLAGS), \ + ProcessImpl_md.c_CFLAGS := $(VERSION_CFLAGS), \ EXTRA_HEADER_DIRS := libfdlibm, \ WARNINGS_AS_ERRORS_xlc := false, \ DISABLED_WARNINGS_gcc := unused-result unused-function, \ @@ -136,7 +137,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBZIP, \ $(LIBZ_CFLAGS), \ CFLAGS_unix := $(BUILD_LIBZIP_MMAP) -UDEBUG, \ DISABLED_WARNINGS_gcc := unused-function implicit-fallthrough, \ - DISABLED_WARNINGS_clang := format-nonliteral, \ + DISABLED_WARNINGS_clang := format-nonliteral deprecated-non-prototype, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ LIBS_unix := -ljvm -ljava $(LIBZ_LIBS), \ @@ -205,7 +206,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJLI, \ OPTIMIZATION := HIGH, \ CFLAGS := $(CFLAGS_JDKLIB) $(LIBJLI_CFLAGS), \ DISABLED_WARNINGS_gcc := unused-function implicit-fallthrough, \ - DISABLED_WARNINGS_clang := sometimes-uninitialized format-nonliteral, \ + DISABLED_WARNINGS_clang := sometimes-uninitialized format-nonliteral deprecated-non-prototype, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ LIBS_unix := $(LIBZ_LIBS), \ diff --git a/make/modules/java.desktop/Java.gmk b/make/modules/java.desktop/Java.gmk index 4b1c14a1133ff..b7cf961a84768 100644 --- a/make/modules/java.desktop/Java.gmk +++ b/make/modules/java.desktop/Java.gmk @@ -67,6 +67,7 @@ EXCLUDE_FILES += \ ifeq ($(call isTargetOs, macosx), true) # exclude all X11 on Mac. EXCLUDES += \ + sun/awt/screencast \ sun/awt/X11 \ sun/java2d/x11 \ sun/java2d/jules \ diff --git a/make/modules/java.desktop/lib/Awt2dLibraries.gmk b/make/modules/java.desktop/lib/Awt2dLibraries.gmk index 3bbff03638be8..bf6a987149af4 100644 --- a/make/modules/java.desktop/lib/Awt2dLibraries.gmk +++ b/make/modules/java.desktop/lib/Awt2dLibraries.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -146,7 +146,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \ DISABLED_WARNINGS_gcc := sign-compare unused-result maybe-uninitialized \ format-nonliteral parentheses unused-value unused-function, \ DISABLED_WARNINGS_clang := logical-op-parentheses extern-initializer \ - sign-compare format-nonliteral, \ + sign-compare format-nonliteral deprecated-non-prototype, \ DISABLED_WARNINGS_microsoft := 4244 4267 4996, \ LDFLAGS := $(LDFLAGS_JDKLIB) $(call SET_SHARED_LIBRARY_ORIGIN), \ LDFLAGS_macosx := -L$(INSTALL_LIBRARIES_HERE), \ @@ -194,6 +194,9 @@ ifeq ($(call isTargetOs, windows macosx), false) LIBAWT_XAWT_EXCLUDES := medialib debug + LIBPIPEWIRE_HEADER_DIRS := \ + $(TOPDIR)/src/$(MODULE)/unix/native/libpipewire/include + LIBAWT_XAWT_EXTRA_HEADER_DIRS := \ $(LIBAWT_DEFAULT_HEADER_DIRS) \ libawt_xawt/awt \ @@ -203,7 +206,7 @@ ifeq ($(call isTargetOs, windows macosx), false) common/font \ common/java2d/opengl \ common/java2d/x11 \ - # + $(LIBPIPEWIRE_HEADER_DIRS) LIBAWT_XAWT_CFLAGS += -DXAWT -DXAWT_HACK \ $(FONTCONFIG_CFLAGS) \ @@ -456,7 +459,10 @@ else endif # hb-ft.cc is not presently needed, and requires freetype 2.4.2 or later. - LIBFONTMANAGER_EXCLUDE_FILES += libharfbuzz/hb-ft.cc + # hb-subset and hb-style APIs are not needed, excluded to cut on compilation time. + LIBFONTMANAGER_EXCLUDE_FILES += hb-ft.cc hb-subset-cff-common.cc \ + hb-subset-cff1.cc hb-subset-cff2.cc hb-subset-input.cc hb-subset-plan.cc \ + hb-subset.cc hb-subset-instancer-solver.cc gsubgpos-context.cc hb-style.cc # list of disabled warnings and the compilers for which it was specifically added. # array-bounds -> GCC 12 on Alpine Linux @@ -467,9 +473,12 @@ else array-bounds parentheses # noexcept-type required for GCC 7 builds. Not required for GCC 8+. # expansion-to-defined required for GCC 9 builds. Not required for GCC 10+. + # maybe-uninitialized required for GCC 8 builds. Not required for GCC 9+. + # calloc-transposed-args required for GCC 14 builds. (fixed upstream in Harfbuzz 032c931e1c0cfb20f18e5acb8ba005775242bd92) HARFBUZZ_DISABLED_WARNINGS_CXX_gcc := reorder delete-non-virtual-dtor strict-overflow \ maybe-uninitialized class-memaccess unused-result extra use-after-free noexcept-type \ - expansion-to-defined dangling-reference + expansion-to-defined dangling-reference maybe-uninitialized \ + calloc-transposed-args HARFBUZZ_DISABLED_WARNINGS_clang := unused-value incompatible-pointer-types \ tautological-constant-out-of-range-compare int-to-pointer-cast \ undef missing-field-initializers range-loop-analysis \ @@ -764,7 +773,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) maybe-uninitialized shift-negative-value implicit-fallthrough \ unused-function, \ DISABLED_WARNINGS_clang := incompatible-pointer-types sign-compare \ - deprecated-declarations null-pointer-subtraction $(LIBZ_DISABLED_WARNINGS_CLANG), \ + deprecated-declarations null-pointer-subtraction deprecated-non-prototype $(LIBZ_DISABLED_WARNINGS_CLANG), \ DISABLED_WARNINGS_microsoft := 4018 4244 4267, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ @@ -825,6 +834,7 @@ ifeq ($(call isTargetOs, macosx), true) incompatible-pointer-types parentheses-equality extra-tokens \ sign-compare semicolon-before-method-body format-nonliteral undef \ pointer-arith, \ + DISABLED_WARNINGS_clang_MTLRenderer.m := gnu-folding-constant, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN) \ -L$(INSTALL_LIBRARIES_HERE), \ diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk index 92af1a8acc9b6..cd03747d98d8c 100644 --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.gmk @@ -873,7 +873,7 @@ BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exesigtest := -ljvm ifeq ($(call isTargetOs, windows), true) BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT - BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libnativeStack.c + BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libnativeStack.c exeGetCreatedJavaVMs.c BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit := jvm.lib else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libbootclssearch_agent += -lpthread @@ -1511,6 +1511,7 @@ else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libterminatedThread += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit += -ljvm BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread + BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeGetCreatedJavaVMs := -ljvm -lpthread endif # This evaluation is expensive and should only be done if this target was diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 1fea866cc1d29..e755d4cee8819 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -505,7 +505,7 @@ class Address { if (size == 0) // It's a byte i->f(_ext.shift() >= 0, 12); else { - assert(_ext.shift() <= 0 || _ext.shift() == (int)size, "bad shift"); + guarantee(_ext.shift() <= 0 || _ext.shift() == (int)size, "bad shift"); i->f(_ext.shift() > 0, 12); } i->f(0b10, 11, 10); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index a35b8b844817b..7abb2205414fd 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -1050,6 +1050,110 @@ void MacroAssembler::lookup_interface_method(Register recv_klass, } } +// Look up the method for a megamorphic invokeinterface call in a single pass over itable: +// - check recv_klass (actual object class) is a subtype of resolved_klass from CompiledICHolder +// - find a holder_klass (class that implements the method) vtable offset and get the method from vtable by index +// The target method is determined by . +// The receiver klass is in recv_klass. +// On success, the result will be in method_result, and execution falls through. +// On failure, execution transfers to the given label. +void MacroAssembler::lookup_interface_method_stub(Register recv_klass, + Register holder_klass, + Register resolved_klass, + Register method_result, + Register temp_itbl_klass, + Register scan_temp, + int itable_index, + Label& L_no_such_interface) { + // 'method_result' is only used as output register at the very end of this method. + // Until then we can reuse it as 'holder_offset'. + Register holder_offset = method_result; + assert_different_registers(resolved_klass, recv_klass, holder_klass, temp_itbl_klass, scan_temp, holder_offset); + + int vtable_start_offset = in_bytes(Klass::vtable_start_offset()); + int itable_offset_entry_size = itableOffsetEntry::size() * wordSize; + int ioffset = itableOffsetEntry::interface_offset_in_bytes(); + int ooffset = itableOffsetEntry::offset_offset_in_bytes(); + + Label L_loop_search_resolved_entry, L_resolved_found, L_holder_found; + + ldrw(scan_temp, Address(recv_klass, Klass::vtable_length_offset())); + add(recv_klass, recv_klass, vtable_start_offset + ioffset); + // itableOffsetEntry[] itable = recv_klass + Klass::vtable_start_offset() + sizeof(vtableEntry) * recv_klass->_vtable_len; + // temp_itbl_klass = itable[0]._interface; + int vtblEntrySize = vtableEntry::size_in_bytes(); + assert(vtblEntrySize == wordSize, "ldr lsl shift amount must be 3"); + ldr(temp_itbl_klass, Address(recv_klass, scan_temp, Address::lsl(exact_log2(vtblEntrySize)))); + mov(holder_offset, zr); + // scan_temp = &(itable[0]._interface) + lea(scan_temp, Address(recv_klass, scan_temp, Address::lsl(exact_log2(vtblEntrySize)))); + + // Initial checks: + // - if (holder_klass != resolved_klass), go to "scan for resolved" + // - if (itable[0] == holder_klass), shortcut to "holder found" + // - if (itable[0] == 0), no such interface + cmp(resolved_klass, holder_klass); + br(Assembler::NE, L_loop_search_resolved_entry); + cmp(holder_klass, temp_itbl_klass); + br(Assembler::EQ, L_holder_found); + cbz(temp_itbl_klass, L_no_such_interface); + + // Loop: Look for holder_klass record in itable + // do { + // temp_itbl_klass = *(scan_temp += itable_offset_entry_size); + // if (temp_itbl_klass == holder_klass) { + // goto L_holder_found; // Found! + // } + // } while (temp_itbl_klass != 0); + // goto L_no_such_interface // Not found. + Label L_search_holder; + bind(L_search_holder); + ldr(temp_itbl_klass, Address(pre(scan_temp, itable_offset_entry_size))); + cmp(holder_klass, temp_itbl_klass); + br(Assembler::EQ, L_holder_found); + cbnz(temp_itbl_klass, L_search_holder); + + b(L_no_such_interface); + + // Loop: Look for resolved_class record in itable + // while (true) { + // temp_itbl_klass = *(scan_temp += itable_offset_entry_size); + // if (temp_itbl_klass == 0) { + // goto L_no_such_interface; + // } + // if (temp_itbl_klass == resolved_klass) { + // goto L_resolved_found; // Found! + // } + // if (temp_itbl_klass == holder_klass) { + // holder_offset = scan_temp; + // } + // } + // + Label L_loop_search_resolved; + bind(L_loop_search_resolved); + ldr(temp_itbl_klass, Address(pre(scan_temp, itable_offset_entry_size))); + bind(L_loop_search_resolved_entry); + cbz(temp_itbl_klass, L_no_such_interface); + cmp(resolved_klass, temp_itbl_klass); + br(Assembler::EQ, L_resolved_found); + cmp(holder_klass, temp_itbl_klass); + br(Assembler::NE, L_loop_search_resolved); + mov(holder_offset, scan_temp); + b(L_loop_search_resolved); + + // See if we already have a holder klass. If not, go and scan for it. + bind(L_resolved_found); + cbz(holder_offset, L_search_holder); + mov(scan_temp, holder_offset); + + // Finally, scan_temp contains holder_klass vtable offset + bind(L_holder_found); + ldrw(method_result, Address(scan_temp, ooffset - ioffset)); + add(recv_klass, recv_klass, itable_index * wordSize + itableMethodEntry::method_offset_in_bytes() + - vtable_start_offset - ioffset); // substract offsets to restore the original value of recv_klass + ldr(method_result, Address(recv_klass, method_result, Address::uxtw(0))); +} + // virtual method calling void MacroAssembler::lookup_virtual_method(Register recv_klass, RegisterOrConstant vtable_index, diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 3f6ebb41f2ac2..9b1c0a935394e 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -918,6 +918,15 @@ class MacroAssembler: public Assembler { Label& no_such_interface, bool return_method = true); + void lookup_interface_method_stub(Register recv_klass, + Register holder_klass, + Register resolved_klass, + Register method_result, + Register temp_reg, + Register temp_reg2, + int itable_index, + Label& L_no_such_interface); + // virtual method calling // n.b. x86 allows RegisterOrConstant for vtable_index void lookup_virtual_method(Register recv_klass, diff --git a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp index 415d3774b9c8a..264acada7b4e5 100644 --- a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,6 +27,7 @@ #include "asm/macroAssembler.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/vmClasses.hpp" +#include "compiler/disassembler.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "memory/allocation.inline.hpp" @@ -36,7 +37,7 @@ #include "runtime/frame.inline.hpp" #include "runtime/stubRoutines.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp index d5a5503213c65..66b2769aca5db 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp @@ -28,7 +28,6 @@ #include "code/codeCache.hpp" #include "code/compiledIC.hpp" #include "gc/shared/collectedHeap.hpp" -#include "memory/resourceArea.hpp" #include "nativeInst_aarch64.hpp" #include "oops/oop.inline.hpp" #include "runtime/handles.hpp" @@ -189,8 +188,6 @@ void NativeCall::set_destination_mt_safe(address dest, bool assert_lock) { CompiledICLocker::is_safe(addr_at(0)), "concurrent code patching"); - ResourceMark rm; - int code_size = NativeInstruction::instruction_size; address addr_call = addr_at(0); bool reachable = Assembler::reachable_from_branch_at(addr_call, dest); assert(NativeCall::is_call_at(addr_call), "unexpected code at call site"); diff --git a/src/hotspot/cpu/aarch64/register_aarch64.hpp b/src/hotspot/cpu/aarch64/register_aarch64.hpp index 5a152d62777d8..459e24639fd83 100644 --- a/src/hotspot/cpu/aarch64/register_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/register_aarch64.hpp @@ -140,7 +140,13 @@ class FloatRegisterImpl: public AbstractRegisterImpl { max_slots_per_register = 8, save_slots_per_register = 2, slots_per_neon_register = 4, - extra_save_slots_per_neon_register = slots_per_neon_register - save_slots_per_register + extra_save_slots_per_neon_register = slots_per_neon_register - save_slots_per_register, + neon_vl = 16, + // VLmax: The maximum sve vector length is determined by the hardware + // sve_vl_min <= VLmax <= sve_vl_max. + sve_vl_min = 16, + // Maximum supported vector length across all CPUs + sve_vl_max = 256 }; // construction diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index e20cffd57670b..807ffebe5b665 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "classfile/javaClasses.hpp" +#include "compiler/disassembler.hpp" #include "compiler/compiler_globals.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "interpreter/bytecodeHistogram.hpp" @@ -66,7 +67,7 @@ // Max size with JVMTI int TemplateInterpreter::InterpreterCodeSize = 200 * 1024; -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> //----------------------------------------------------------------------------- @@ -1919,13 +1920,21 @@ void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, address& vep) { assert(t->is_valid() && t->tos_in() == vtos, "illegal template"); Label L; - aep = __ pc(); __ push_ptr(); __ b(L); - fep = __ pc(); __ push_f(); __ b(L); - dep = __ pc(); __ push_d(); __ b(L); - lep = __ pc(); __ push_l(); __ b(L); - bep = cep = sep = - iep = __ pc(); __ push_i(); - vep = __ pc(); + aep = __ pc(); // atos entry point + __ push_ptr(); + __ b(L); + fep = __ pc(); // ftos entry point + __ push_f(); + __ b(L); + dep = __ pc(); // dtos entry point + __ push_d(); + __ b(L); + lep = __ pc(); // ltos entry point + __ push_l(); + __ b(L); + bep = cep = sep = iep = __ pc(); // [bcsi]tos entry point + __ push_i(); + vep = __ pc(); // vtos entry point __ bind(L); generate_and_dispatch(t); } diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 4666b42b933b9..ec577c261d34a 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/tlab_globals.hpp" @@ -45,7 +46,7 @@ #include "runtime/synchronizer.hpp" #include "utilities/powerOfTwo.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> // Address computation: local variables diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 039e9c17b46c1..8e3dbfd5db779 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -24,6 +24,7 @@ */ #include "precompiled.hpp" +#include "register_aarch64.hpp" #include "runtime/arguments.hpp" #include "runtime/globals_extension.hpp" #include "runtime/java.hpp" @@ -212,10 +213,9 @@ void VM_Version::initialize() { } } - // Neoverse N1, N2 and V1 - if (_cpu == CPU_ARM && ((_model == 0xd0c || _model2 == 0xd0c) - || (_model == 0xd49 || _model2 == 0xd49) - || (_model == 0xd40 || _model2 == 0xd40))) { + // Neoverse N1, N2, V1, V2 + if (_cpu == CPU_ARM && (model_is(0xd0c) || model_is(0xd49) || + model_is(0xd40) || model_is(0xd4f))) { if (FLAG_IS_DEFAULT(UseSIMDForMemoryOps)) { FLAG_SET_DEFAULT(UseSIMDForMemoryOps, true); } @@ -238,8 +238,8 @@ void VM_Version::initialize() { if (_cpu == CPU_ARM && (_model == 0xd07 || _model2 == 0xd07)) _features |= CPU_STXR_PREFETCH; char buf[512]; - sprintf(buf, "0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision); - if (_model2) sprintf(buf+strlen(buf), "(0x%03x)", _model2); + int buf_used_len = os::snprintf_checked(buf, sizeof(buf), "0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision); + if (_model2) os::snprintf_checked(buf + buf_used_len, sizeof(buf) - buf_used_len, "(0x%03x)", _model2); #define ADD_FEATURE_IF_SUPPORTED(id, name, bit) if (_features & CPU_##id) strcat(buf, ", " name); CPU_FEATURE_FLAGS(ADD_FEATURE_IF_SUPPORTED) #undef ADD_FEATURE_IF_SUPPORTED @@ -401,14 +401,27 @@ void VM_Version::initialize() { if (FLAG_IS_DEFAULT(UseSVE)) { FLAG_SET_DEFAULT(UseSVE, (_features & CPU_SVE2) ? 2 : 1); } - if (UseSVE > 0) { - _initial_sve_vector_length = get_current_sve_vector_length(); - } } else if (UseSVE > 0) { warning("UseSVE specified, but not supported on current CPU. Disabling SVE."); FLAG_SET_DEFAULT(UseSVE, 0); } + if (UseSVE > 0) { + int vl = get_current_sve_vector_length(); + if (vl < 0) { + warning("Unable to get SVE vector length on this system. " + "Disabling SVE. Specify -XX:UseSVE=0 to shun this warning."); + FLAG_SET_DEFAULT(UseSVE, 0); + } else if ((vl == 0) || ((vl % FloatRegisterImpl::sve_vl_min) != 0) || !is_power_of_2(vl)) { + warning("Detected SVE vector length (%d) should be a power of two and a multiple of %d. " + "Disabling SVE. Specify -XX:UseSVE=0 to shun this warning.", + vl, FloatRegisterImpl::sve_vl_min); + FLAG_SET_DEFAULT(UseSVE, 0); + } else { + _initial_sve_vector_length = vl; + } + } + // This machine allows unaligned memory accesses if (FLAG_IS_DEFAULT(UseUnalignedAccesses)) { FLAG_SET_DEFAULT(UseUnalignedAccesses, true); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 3f186f56e75bb..46c77e48b8f1a 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -142,6 +142,10 @@ class VM_Version : public Abstract_VM_Version { static int cpu_variant() { return _variant; } static int cpu_revision() { return _revision; } + static bool model_is(int cpu_model) { + return _model == cpu_model || _model2 == cpu_model; + } + static bool is_zva_enabled() { return 0 <= _zva_length; } static int zva_length() { assert(is_zva_enabled(), "ZVA not available"); diff --git a/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp index acef8d21abc80..11aba1d339bdb 100644 --- a/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp @@ -175,7 +175,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { // so all registers except arguments are free at this point. const Register recv_klass_reg = r10; const Register holder_klass_reg = r16; // declaring interface klass (DECC) - const Register resolved_klass_reg = rmethod; // resolved interface klass (REFC) + const Register resolved_klass_reg = r17; // resolved interface klass (REFC) const Register temp_reg = r11; const Register temp_reg2 = r15; const Register icholder_reg = rscratch2; @@ -192,28 +192,13 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { __ load_klass(recv_klass_reg, j_rarg0); // Receiver subtype check against REFC. - __ lookup_interface_method(// inputs: rec. class, interface - recv_klass_reg, resolved_klass_reg, noreg, - // outputs: scan temp. reg1, scan temp. reg2 - temp_reg2, temp_reg, - L_no_such_interface, - /*return_method=*/false); - - const ptrdiff_t typecheckSize = __ pc() - start_pc; - start_pc = __ pc(); - // Get selected method from declaring class and itable index - __ lookup_interface_method(// inputs: rec. class, interface, itable index - recv_klass_reg, holder_klass_reg, itable_index, - // outputs: method, scan temp. reg - rmethod, temp_reg, - L_no_such_interface); - - const ptrdiff_t lookupSize = __ pc() - start_pc; + __ lookup_interface_method_stub(recv_klass_reg, holder_klass_reg, resolved_klass_reg, rmethod, + temp_reg, temp_reg2, itable_index, L_no_such_interface); // Reduce "estimate" such that "padding" does not drop below 8. - const ptrdiff_t estimate = 124; - const ptrdiff_t codesize = typecheckSize + lookupSize; + const ptrdiff_t estimate = 144; + const ptrdiff_t codesize = __ pc() - start_pc; slop_delta = (int)(estimate - codesize); slop_bytes += slop_delta; assert(slop_delta >= 0, "itable #%d: Code size estimate (%d) for lookup_interface_method too small, required: %d", itable_index, (int)estimate, (int)codesize); diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index d9ade024b911b..f40595ca62ff2 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -243,7 +243,7 @@ uint MachConstantBaseNode::size(PhaseRegAlloc*) const { #ifndef PRODUCT void MachConstantBaseNode::format(PhaseRegAlloc* ra_, outputStream* st) const { char reg[128]; - ra_->dump_register(this, reg); + ra_->dump_register(this, reg, sizeof(reg)); st->print("MOV_SLOW &constanttable,%s\t! constant table base", reg); } #endif diff --git a/src/hotspot/cpu/ppc/assembler_ppc.cpp b/src/hotspot/cpu/ppc/assembler_ppc.cpp index 6a6be870991b8..026db4a87de07 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2015 SAP SE. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,9 +79,9 @@ int Assembler::branch_destination(int inst, int pos) { // Low-level andi-one-instruction-macro. void Assembler::andi(Register a, Register s, const long ui16) { - if (is_power_of_2(((jlong) ui16)+1)) { + if (is_power_of_2(((unsigned long) ui16)+1)) { // pow2minus1 - clrldi(a, s, 64 - log2i_exact((((jlong) ui16)+1))); + clrldi(a, s, 64 - log2i_exact((((unsigned long) ui16)+1))); } else if (is_power_of_2((jlong) ui16)) { // pow2 rlwinm(a, s, 0, 31 - log2i_exact((jlong) ui16), 31 - log2i_exact((jlong) ui16)); diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index 0d0c31177458b..188fb068a91d1 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2012, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1919,7 +1919,7 @@ class Assembler : public AbstractAssembler { // More convenient version. int condition_register_bit(ConditionRegister cr, Condition c) { - return 4 * (int)(intptr_t)cr + c; + return 4 * cr.encoding() + c; } void crand( ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc); void crnand(ConditionRegister crdst, Condition cdst, ConditionRegister crsrc, Condition csrc); diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 02f59278c16ca..d48f07e47ab40 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2012, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -133,9 +133,20 @@ void LIR_Assembler::osr_entry() { // copied into place by code emitted in the IR. Register OSR_buf = osrBufferPointer()->as_register(); - { assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); - int monitor_offset = BytesPerWord * method()->max_locals() + - (2 * BytesPerWord) * (number_of_locks - 1); + { + assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); + + const int locals_space = BytesPerWord * method()->max_locals(); + int monitor_offset = locals_space + (2 * BytesPerWord) * (number_of_locks - 1); + bool use_OSR_bias = false; + + if (!Assembler::is_simm16(monitor_offset + BytesPerWord) && number_of_locks > 0) { + // Offsets too large for ld instructions. Use bias. + __ add_const_optimized(OSR_buf, OSR_buf, locals_space); + monitor_offset -= locals_space; + use_OSR_bias = true; + } + // SharedRuntime::OSR_migration_begin() packs BasicObjectLocks in // the OSR buffer using 2 word entries: first the lock and then // the oop. @@ -161,6 +172,11 @@ void LIR_Assembler::osr_entry() { __ ld(R0, slot_offset + 1*BytesPerWord, OSR_buf); __ std(R0, mo.disp(), mo.base()); } + + if (use_OSR_bias) { + // Restore. + __ sub_const_optimized(OSR_buf, OSR_buf, locals_space); + } } } @@ -589,7 +605,7 @@ void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { case Bytecodes::_f2i: { bool dst_in_memory = !VM_Version::has_mtfprd(); FloatRegister rsrc = (code == Bytecodes::_d2i) ? src->as_double_reg() : src->as_float_reg(); - Address addr = dst_in_memory ? frame_map()->address_for_slot(dst->double_stack_ix()) : NULL; + Address addr = dst_in_memory ? frame_map()->address_for_slot(dst->double_stack_ix()) : Address(); Label L; // Result must be 0 if value is NaN; test by comparing value to itself. __ fcmpu(CCR0, rsrc, rsrc); @@ -613,7 +629,7 @@ void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { case Bytecodes::_f2l: { bool dst_in_memory = !VM_Version::has_mtfprd(); FloatRegister rsrc = (code == Bytecodes::_d2l) ? src->as_double_reg() : src->as_float_reg(); - Address addr = dst_in_memory ? frame_map()->address_for_slot(dst->double_stack_ix()) : NULL; + Address addr = dst_in_memory ? frame_map()->address_for_slot(dst->double_stack_ix()) : Address(); Label L; // Result must be 0 if value is NaN; test by comparing value to itself. __ fcmpu(CCR0, rsrc, rsrc); diff --git a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp index 0a5ab12d3a46d..aa8c4075a695a 100644 --- a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp @@ -582,7 +582,7 @@ inline bool can_handle_logic_op_as_uimm(ValueType *type, Bytecodes::Code bc) { is_power_of_2(int_or_long_const) || is_power_of_2(-int_or_long_const))) return true; if (bc == Bytecodes::_land && - (is_power_of_2(int_or_long_const+1) || + (is_power_of_2((unsigned long)int_or_long_const+1) || (Assembler::is_uimm(int_or_long_const, 32) && is_power_of_2(int_or_long_const)) || (int_or_long_const != min_jlong && is_power_of_2(-int_or_long_const)))) return true; diff --git a/src/hotspot/cpu/ppc/c2_init_ppc.cpp b/src/hotspot/cpu/ppc/c2_init_ppc.cpp index d15f880e41375..e517cdf7de101 100644 --- a/src/hotspot/cpu/ppc/c2_init_ppc.cpp +++ b/src/hotspot/cpu/ppc/c2_init_ppc.cpp @@ -34,21 +34,5 @@ // Processor dependent initialization of C2 compiler for ppc. void Compile::pd_compiler2_init() { - - // Power7 and later. - if (PowerArchitecturePPC64 > 6) { - if (FLAG_IS_DEFAULT(UsePopCountInstruction)) { - FLAG_SET_ERGO(UsePopCountInstruction, true); - } - } - - if (!VM_Version::has_isel() && FLAG_IS_DEFAULT(ConditionalMoveLimit)) { - FLAG_SET_ERGO(ConditionalMoveLimit, 0); - } - - if (OptimizeFill) { - warning("OptimizeFill is not supported on this CPU."); - FLAG_SET_DEFAULT(OptimizeFill, false); - } - + guarantee(CodeEntryAlignment >= InteriorEntryAlignment, ""); } diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp index 17fc8e5078eb1..4deb75e275f3f 100644 --- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp @@ -540,15 +540,15 @@ class ZSetupArguments { if (_ref != R4_ARG2) { // Calculate address first as the address' base register might clash with R4_ARG2 - __ add(R4_ARG2, (intptr_t) _ref_addr.disp(), _ref_addr.base()); + __ addi(R4_ARG2, _ref_addr.base(), _ref_addr.disp()); __ mr_if_needed(R3_ARG1, _ref); } else if (_ref_addr.base() != R3_ARG1) { __ mr(R3_ARG1, _ref); - __ add(R4_ARG2, (intptr_t) _ref_addr.disp(), _ref_addr.base()); // Cloberring _ref + __ addi(R4_ARG2, _ref_addr.base(), _ref_addr.disp()); // Clobbering _ref } else { // Arguments are provided in inverse order (i.e. _ref == R4_ARG2, _ref_addr == R3_ARG1) __ mr(R0, _ref); - __ add(R4_ARG2, (intptr_t) _ref_addr.disp(), _ref_addr.base()); + __ addi(R4_ARG2, _ref_addr.base(), _ref_addr.disp()); __ mr(R3_ARG1, R0); } } diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index b474fe8905b53..6950b304d084c 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2012, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1779,7 +1779,7 @@ void InterpreterMacroAssembler::profile_arguments_type(Register callee, if (MethodData::profile_arguments()) { Label done; int off_to_args = in_bytes(TypeEntriesAtCall::args_data_offset()); - add(R28_mdx, off_to_args, R28_mdx); + addi(R28_mdx, R28_mdx, off_to_args); for (int i = 0; i < TypeProfileArgsLimit; i++) { if (i > 0 || MethodData::profile_return()) { diff --git a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp index 28b2ea9da45f4..ff8ba4fc84e96 100644 --- a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp +++ b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2012, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -300,7 +300,7 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* } Register R19_member = R19_method; // MemberName ptr; incoming method ptr is dead now __ ld(R19_member, RegisterOrConstant((intptr_t)8), R15_argbase); - __ add(R15_argbase, Interpreter::stackElementSize, R15_argbase); + __ addi(R15_argbase, R15_argbase, Interpreter::stackElementSize); generate_method_handle_dispatch(_masm, iid, tmp_recv, R19_member, not_for_compiler_entry); } diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index d26e3c883e715..fab403825e77d 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -1946,7 +1946,7 @@ uint MachNopNode::size(PhaseRegAlloc *ra_) const { void BoxLockNode::format(PhaseRegAlloc *ra_, outputStream *st) const { int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); char reg_str[128]; - ra_->dump_register(this, reg_str); + ra_->dump_register(this, reg_str, sizeof(reg_str)); st->print("ADDI %s, SP, %d \t// box node", reg_str, offset); } #endif @@ -3510,6 +3510,7 @@ encode %{ call->_in_rms = _in_rms; call->_nesting = _nesting; call->_override_symbolic_info = _override_symbolic_info; + call->_arg_escape = _arg_escape; // New call needs all inputs of old call. // Req... diff --git a/src/hotspot/cpu/ppc/register_definitions_ppc.cpp b/src/hotspot/cpu/ppc/register_definitions_ppc.cpp deleted file mode 100644 index c0bfd7db95af8..0000000000000 --- a/src/hotspot/cpu/ppc/register_definitions_ppc.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2015 SAP SE. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -// Make sure the defines don't screw up the declarations later on in this file. -#define DONT_USE_REGISTER_DEFINES - -#include "asm/register.hpp" - -REGISTER_DEFINITION(Register, noreg); - -REGISTER_DEFINITION(FloatRegister, fnoreg); - -REGISTER_DEFINITION(VectorRegister, vnoreg); - -REGISTER_DEFINITION(VectorSRegister, vsnoreg); diff --git a/src/hotspot/cpu/ppc/register_ppc.cpp b/src/hotspot/cpu/ppc/register_ppc.cpp index e6b4f74c0c8e4..e84f89373adb0 100644 --- a/src/hotspot/cpu/ppc/register_ppc.cpp +++ b/src/hotspot/cpu/ppc/register_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2018 SAP SE. All rights reserved. + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,8 +26,7 @@ #include "precompiled.hpp" #include "register_ppc.hpp" - -const char* RegisterImpl::name() const { +const char* Register::name() const { const char* names[number_of_registers] = { "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", @@ -37,14 +36,14 @@ const char* RegisterImpl::name() const { return is_valid() ? names[encoding()] : "noreg"; } -const char* ConditionRegisterImpl::name() const { +const char* ConditionRegister::name() const { const char* names[number_of_registers] = { "CR0", "CR1", "CR2", "CR3", "CR4", "CR5", "CR6", "CR7" }; return is_valid() ? names[encoding()] : "cnoreg"; } -const char* FloatRegisterImpl::name() const { +const char* FloatRegister::name() const { const char* names[number_of_registers] = { "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", @@ -54,14 +53,14 @@ const char* FloatRegisterImpl::name() const { return is_valid() ? names[encoding()] : "fnoreg"; } -const char* SpecialRegisterImpl::name() const { +const char* SpecialRegister::name() const { const char* names[number_of_registers] = { "SR_XER", "SR_LR", "SR_CTR", "SR_VRSAVE", "SR_SPEFSCR", "SR_PPR" }; return is_valid() ? names[encoding()] : "snoreg"; } -const char* VectorRegisterImpl::name() const { +const char* VectorRegister::name() const { const char* names[number_of_registers] = { "VR0", "VR1", "VR2", "VR3", "VR4", "VR5", "VR6", "VR7", "VR8", "VR9", "VR10", "VR11", "VR12", "VR13", "VR14", "VR15", @@ -71,7 +70,7 @@ const char* VectorRegisterImpl::name() const { return is_valid() ? names[encoding()] : "vnoreg"; } -const char* VectorSRegisterImpl::name() const { +const char* VectorSRegister::name() const { const char* names[number_of_registers] = { "VSR0", "VSR1", "VSR2", "VSR3", "VSR4", "VSR5", "VSR6", "VSR7", "VSR8", "VSR9", "VSR10", "VSR11", "VSR12", "VSR13", "VSR14", "VSR15", @@ -86,19 +85,19 @@ const char* VectorSRegisterImpl::name() const { } // Method to convert a FloatRegister to a Vector-Scalar Register (VectorSRegister) -VectorSRegister FloatRegisterImpl::to_vsr() const { - if (this == fnoreg) { return vsnoreg; } +VectorSRegister FloatRegister::to_vsr() const { + if (*this == fnoreg) { return vsnoreg; } return as_VectorSRegister(encoding()); } // Method to convert a VectorRegister to a Vector-Scalar Register (VectorSRegister) -VectorSRegister VectorRegisterImpl::to_vsr() const { - if (this == vnoreg) { return vsnoreg; } +VectorSRegister VectorRegister::to_vsr() const { + if (*this == vnoreg) { return vsnoreg; } return as_VectorSRegister(encoding() + 32); } // Method to convert a VectorSRegister to a Vector Register (VectorRegister) -VectorRegister VectorSRegisterImpl::to_vr() const { - if (this == vsnoreg) { return vnoreg; } +VectorRegister VectorSRegister::to_vr() const { + if (*this == vsnoreg) { return vnoreg; } return as_VectorRegister(encoding() - 32); } diff --git a/src/hotspot/cpu/ppc/register_ppc.hpp b/src/hotspot/cpu/ppc/register_ppc.hpp index cf91cbbc16ecd..3d062e124af22 100644 --- a/src/hotspot/cpu/ppc/register_ppc.hpp +++ b/src/hotspot/cpu/ppc/register_ppc.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2022 SAP SE. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ #include "asm/register.hpp" // forward declaration -class Address; class VMRegImpl; typedef VMRegImpl* VMReg; @@ -75,207 +74,145 @@ typedef VMRegImpl* VMReg; // vs0-31 Alias for f0-f31 (64 bit, see above) // vs32-63 Alias for v0-31 (128 bit, see above) -// Use Register as shortcut -class RegisterImpl; -typedef RegisterImpl* Register; - -inline Register as_Register(int encoding) { - assert(encoding >= -1 && encoding < 32, "bad register encoding"); - return (Register)(intptr_t)encoding; -} // The implementation of integer registers for the Power architecture -class RegisterImpl: public AbstractRegisterImpl { +class Register { + int _encoding; public: enum { number_of_registers = 32 }; + constexpr Register(int encoding = -1) : _encoding(encoding) {} + bool operator==(const Register rhs) const { return _encoding == rhs._encoding; } + bool operator!=(const Register rhs) const { return _encoding != rhs._encoding; } + const Register* operator->() const { return this; } + // general construction - inline friend Register as_Register(int encoding); + inline constexpr friend Register as_Register(int encoding); // accessors - int encoding() const { assert(is_valid(), "invalid register"); return value(); } - inline VMReg as_VMReg(); - Register successor() const { return as_Register(encoding() + 1); } + constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; } + inline VMReg as_VMReg() const; + Register successor() const { return Register(encoding() + 1); } // testers - bool is_valid() const { return ( 0 <= (value()&0x7F) && (value()&0x7F) < number_of_registers); } - bool is_volatile() const { return ( 0 <= (value()&0x7F) && (value()&0x7F) <= 13 ); } - bool is_nonvolatile() const { return (14 <= (value()&0x7F) && (value()&0x7F) <= 31 ); } + constexpr bool is_valid() const { return ( 0 <= _encoding && _encoding < number_of_registers); } + constexpr bool is_volatile() const { return ( 0 <= _encoding && _encoding <= 13 ); } + constexpr bool is_nonvolatile() const { return (14 <= _encoding && _encoding <= 31 ); } const char* name() const; }; -// The integer registers of the PPC architecture -CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1)); - -CONSTANT_REGISTER_DECLARATION(Register, R0, (0)); -CONSTANT_REGISTER_DECLARATION(Register, R1, (1)); -CONSTANT_REGISTER_DECLARATION(Register, R2, (2)); -CONSTANT_REGISTER_DECLARATION(Register, R3, (3)); -CONSTANT_REGISTER_DECLARATION(Register, R4, (4)); -CONSTANT_REGISTER_DECLARATION(Register, R5, (5)); -CONSTANT_REGISTER_DECLARATION(Register, R6, (6)); -CONSTANT_REGISTER_DECLARATION(Register, R7, (7)); -CONSTANT_REGISTER_DECLARATION(Register, R8, (8)); -CONSTANT_REGISTER_DECLARATION(Register, R9, (9)); -CONSTANT_REGISTER_DECLARATION(Register, R10, (10)); -CONSTANT_REGISTER_DECLARATION(Register, R11, (11)); -CONSTANT_REGISTER_DECLARATION(Register, R12, (12)); -CONSTANT_REGISTER_DECLARATION(Register, R13, (13)); -CONSTANT_REGISTER_DECLARATION(Register, R14, (14)); -CONSTANT_REGISTER_DECLARATION(Register, R15, (15)); -CONSTANT_REGISTER_DECLARATION(Register, R16, (16)); -CONSTANT_REGISTER_DECLARATION(Register, R17, (17)); -CONSTANT_REGISTER_DECLARATION(Register, R18, (18)); -CONSTANT_REGISTER_DECLARATION(Register, R19, (19)); -CONSTANT_REGISTER_DECLARATION(Register, R20, (20)); -CONSTANT_REGISTER_DECLARATION(Register, R21, (21)); -CONSTANT_REGISTER_DECLARATION(Register, R22, (22)); -CONSTANT_REGISTER_DECLARATION(Register, R23, (23)); -CONSTANT_REGISTER_DECLARATION(Register, R24, (24)); -CONSTANT_REGISTER_DECLARATION(Register, R25, (25)); -CONSTANT_REGISTER_DECLARATION(Register, R26, (26)); -CONSTANT_REGISTER_DECLARATION(Register, R27, (27)); -CONSTANT_REGISTER_DECLARATION(Register, R28, (28)); -CONSTANT_REGISTER_DECLARATION(Register, R29, (29)); -CONSTANT_REGISTER_DECLARATION(Register, R30, (30)); -CONSTANT_REGISTER_DECLARATION(Register, R31, (31)); - +inline constexpr Register as_Register(int encoding) { + assert(encoding >= -1 && encoding < 32, "bad register encoding"); + return Register(encoding); +} -// -// Because Power has many registers, #define'ing values for them is -// beneficial in code size and is worth the cost of some of the -// dangers of defines. If a particular file has a problem with these -// defines then it's possible to turn them off in that file by -// defining DONT_USE_REGISTER_DEFINES. Register_definition_ppc.cpp -// does that so that it's able to provide real definitions of these -// registers for use in debuggers and such. -// +// The integer registers of the PPC architecture +constexpr Register noreg = as_Register(-1); + +constexpr Register R0 = as_Register( 0); +constexpr Register R1 = as_Register( 1); +constexpr Register R2 = as_Register( 2); +constexpr Register R3 = as_Register( 3); +constexpr Register R4 = as_Register( 4); +constexpr Register R5 = as_Register( 5); +constexpr Register R6 = as_Register( 6); +constexpr Register R7 = as_Register( 7); +constexpr Register R8 = as_Register( 8); +constexpr Register R9 = as_Register( 9); +constexpr Register R10 = as_Register(10); +constexpr Register R11 = as_Register(11); +constexpr Register R12 = as_Register(12); +constexpr Register R13 = as_Register(13); +constexpr Register R14 = as_Register(14); +constexpr Register R15 = as_Register(15); +constexpr Register R16 = as_Register(16); +constexpr Register R17 = as_Register(17); +constexpr Register R18 = as_Register(18); +constexpr Register R19 = as_Register(19); +constexpr Register R20 = as_Register(20); +constexpr Register R21 = as_Register(21); +constexpr Register R22 = as_Register(22); +constexpr Register R23 = as_Register(23); +constexpr Register R24 = as_Register(24); +constexpr Register R25 = as_Register(25); +constexpr Register R26 = as_Register(26); +constexpr Register R27 = as_Register(27); +constexpr Register R28 = as_Register(28); +constexpr Register R29 = as_Register(29); +constexpr Register R30 = as_Register(30); +constexpr Register R31 = as_Register(31); -#ifndef DONT_USE_REGISTER_DEFINES -#define noreg ((Register)(noreg_RegisterEnumValue)) - -#define R0 ((Register)(R0_RegisterEnumValue)) -#define R1 ((Register)(R1_RegisterEnumValue)) -#define R2 ((Register)(R2_RegisterEnumValue)) -#define R3 ((Register)(R3_RegisterEnumValue)) -#define R4 ((Register)(R4_RegisterEnumValue)) -#define R5 ((Register)(R5_RegisterEnumValue)) -#define R6 ((Register)(R6_RegisterEnumValue)) -#define R7 ((Register)(R7_RegisterEnumValue)) -#define R8 ((Register)(R8_RegisterEnumValue)) -#define R9 ((Register)(R9_RegisterEnumValue)) -#define R10 ((Register)(R10_RegisterEnumValue)) -#define R11 ((Register)(R11_RegisterEnumValue)) -#define R12 ((Register)(R12_RegisterEnumValue)) -#define R13 ((Register)(R13_RegisterEnumValue)) -#define R14 ((Register)(R14_RegisterEnumValue)) -#define R15 ((Register)(R15_RegisterEnumValue)) -#define R16 ((Register)(R16_RegisterEnumValue)) -#define R17 ((Register)(R17_RegisterEnumValue)) -#define R18 ((Register)(R18_RegisterEnumValue)) -#define R19 ((Register)(R19_RegisterEnumValue)) -#define R20 ((Register)(R20_RegisterEnumValue)) -#define R21 ((Register)(R21_RegisterEnumValue)) -#define R22 ((Register)(R22_RegisterEnumValue)) -#define R23 ((Register)(R23_RegisterEnumValue)) -#define R24 ((Register)(R24_RegisterEnumValue)) -#define R25 ((Register)(R25_RegisterEnumValue)) -#define R26 ((Register)(R26_RegisterEnumValue)) -#define R27 ((Register)(R27_RegisterEnumValue)) -#define R28 ((Register)(R28_RegisterEnumValue)) -#define R29 ((Register)(R29_RegisterEnumValue)) -#define R30 ((Register)(R30_RegisterEnumValue)) -#define R31 ((Register)(R31_RegisterEnumValue)) -#endif - -// Use ConditionRegister as shortcut -class ConditionRegisterImpl; -typedef ConditionRegisterImpl* ConditionRegister; - -inline ConditionRegister as_ConditionRegister(int encoding) { - assert(encoding >= 0 && encoding < 8, "bad condition register encoding"); - return (ConditionRegister)(intptr_t)encoding; -} // The implementation of condition register(s) for the PPC architecture -class ConditionRegisterImpl: public AbstractRegisterImpl { +class ConditionRegister { + int _encoding; public: enum { number_of_registers = 8 }; + constexpr ConditionRegister(int encoding = -1) : _encoding(encoding) {} + bool operator==(const ConditionRegister rhs) const { return _encoding == rhs._encoding; } + bool operator!=(const ConditionRegister rhs) const { return _encoding != rhs._encoding; } + const ConditionRegister* operator->() const { return this; } + // construction. - inline friend ConditionRegister as_ConditionRegister(int encoding); + inline constexpr friend ConditionRegister as_ConditionRegister(int encoding); // accessors - int encoding() const { assert(is_valid(), "invalid register"); return value(); } - inline VMReg as_VMReg(); + constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; } + inline VMReg as_VMReg() const; // testers - bool is_valid() const { return (0 <= value() && value() < number_of_registers); } - bool is_nonvolatile() const { return (2 <= (value()&0x7F) && (value()&0x7F) <= 4 ); } + constexpr bool is_valid() const { return (0 <= _encoding && _encoding < number_of_registers); } + constexpr bool is_nonvolatile() const { return (2 <= _encoding && _encoding <= 4 ); } const char* name() const; }; -// The (parts of the) condition register(s) of the PPC architecture -// sys/ioctl.h on AIX defines CR0-CR3, so I name these CCR. -CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR0, (0)); -CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR1, (1)); -CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR2, (2)); -CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR3, (3)); -CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR4, (4)); -CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR5, (5)); -CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR6, (6)); -CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR7, (7)); - -#ifndef DONT_USE_REGISTER_DEFINES - -#define CCR0 ((ConditionRegister)(CCR0_ConditionRegisterEnumValue)) -#define CCR1 ((ConditionRegister)(CCR1_ConditionRegisterEnumValue)) -#define CCR2 ((ConditionRegister)(CCR2_ConditionRegisterEnumValue)) -#define CCR3 ((ConditionRegister)(CCR3_ConditionRegisterEnumValue)) -#define CCR4 ((ConditionRegister)(CCR4_ConditionRegisterEnumValue)) -#define CCR5 ((ConditionRegister)(CCR5_ConditionRegisterEnumValue)) -#define CCR6 ((ConditionRegister)(CCR6_ConditionRegisterEnumValue)) -#define CCR7 ((ConditionRegister)(CCR7_ConditionRegisterEnumValue)) - -#endif // DONT_USE_REGISTER_DEFINES - -// Forward declaration -// Use VectorSRegister as a shortcut. -class VectorSRegisterImpl; -typedef VectorSRegisterImpl* VectorSRegister; - -// Use FloatRegister as shortcut -class FloatRegisterImpl; -typedef FloatRegisterImpl* FloatRegister; - -inline FloatRegister as_FloatRegister(int encoding) { - assert(encoding >= -1 && encoding < 32, "bad float register encoding"); - return (FloatRegister)(intptr_t)encoding; +inline constexpr ConditionRegister as_ConditionRegister(int encoding) { + assert(encoding >= 0 && encoding < 8, "bad condition register encoding"); + return ConditionRegister(encoding); } +constexpr ConditionRegister CCR0 = as_ConditionRegister(0); +constexpr ConditionRegister CCR1 = as_ConditionRegister(1); +constexpr ConditionRegister CCR2 = as_ConditionRegister(2); +constexpr ConditionRegister CCR3 = as_ConditionRegister(3); +constexpr ConditionRegister CCR4 = as_ConditionRegister(4); +constexpr ConditionRegister CCR5 = as_ConditionRegister(5); +constexpr ConditionRegister CCR6 = as_ConditionRegister(6); +constexpr ConditionRegister CCR7 = as_ConditionRegister(7); + + +class VectorSRegister; + // The implementation of float registers for the PPC architecture -class FloatRegisterImpl: public AbstractRegisterImpl { +class FloatRegister { + int _encoding; public: enum { number_of_registers = 32 }; + constexpr FloatRegister(int encoding = -1) : _encoding(encoding) {} + bool operator==(const FloatRegister rhs) const { return _encoding == rhs._encoding; } + bool operator!=(const FloatRegister rhs) const { return _encoding != rhs._encoding; } + const FloatRegister* operator->() const { return this; } + // construction - inline friend FloatRegister as_FloatRegister(int encoding); + inline constexpr friend FloatRegister as_FloatRegister(int encoding); // accessors - int encoding() const { assert(is_valid(), "invalid register"); return value(); } - inline VMReg as_VMReg(); - FloatRegister successor() const { return as_FloatRegister(encoding() + 1); } + constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; } + inline VMReg as_VMReg() const; + FloatRegister successor() const { return FloatRegister(encoding() + 1); } // testers - bool is_valid() const { return (0 <= value() && value() < number_of_registers); } + constexpr bool is_valid() const { return (0 <= _encoding && _encoding < number_of_registers); } const char* name() const; @@ -283,147 +220,108 @@ class FloatRegisterImpl: public AbstractRegisterImpl { VectorSRegister to_vsr() const; }; -// The float registers of the PPC architecture -CONSTANT_REGISTER_DECLARATION(FloatRegister, fnoreg, (-1)); - -CONSTANT_REGISTER_DECLARATION(FloatRegister, F0, ( 0)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F1, ( 1)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F2, ( 2)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F3, ( 3)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F4, ( 4)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F5, ( 5)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F6, ( 6)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F7, ( 7)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F8, ( 8)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F9, ( 9)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F10, (10)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F11, (11)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F12, (12)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F13, (13)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F14, (14)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F15, (15)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F16, (16)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F17, (17)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F18, (18)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F19, (19)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F20, (20)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F21, (21)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F22, (22)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F23, (23)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F24, (24)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F25, (25)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F26, (26)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F27, (27)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F28, (28)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F29, (29)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F30, (30)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, F31, (31)); - -#ifndef DONT_USE_REGISTER_DEFINES -#define fnoreg ((FloatRegister)(fnoreg_FloatRegisterEnumValue)) -#define F0 ((FloatRegister)( F0_FloatRegisterEnumValue)) -#define F1 ((FloatRegister)( F1_FloatRegisterEnumValue)) -#define F2 ((FloatRegister)( F2_FloatRegisterEnumValue)) -#define F3 ((FloatRegister)( F3_FloatRegisterEnumValue)) -#define F4 ((FloatRegister)( F4_FloatRegisterEnumValue)) -#define F5 ((FloatRegister)( F5_FloatRegisterEnumValue)) -#define F6 ((FloatRegister)( F6_FloatRegisterEnumValue)) -#define F7 ((FloatRegister)( F7_FloatRegisterEnumValue)) -#define F8 ((FloatRegister)( F8_FloatRegisterEnumValue)) -#define F9 ((FloatRegister)( F9_FloatRegisterEnumValue)) -#define F10 ((FloatRegister)( F10_FloatRegisterEnumValue)) -#define F11 ((FloatRegister)( F11_FloatRegisterEnumValue)) -#define F12 ((FloatRegister)( F12_FloatRegisterEnumValue)) -#define F13 ((FloatRegister)( F13_FloatRegisterEnumValue)) -#define F14 ((FloatRegister)( F14_FloatRegisterEnumValue)) -#define F15 ((FloatRegister)( F15_FloatRegisterEnumValue)) -#define F16 ((FloatRegister)( F16_FloatRegisterEnumValue)) -#define F17 ((FloatRegister)( F17_FloatRegisterEnumValue)) -#define F18 ((FloatRegister)( F18_FloatRegisterEnumValue)) -#define F19 ((FloatRegister)( F19_FloatRegisterEnumValue)) -#define F20 ((FloatRegister)( F20_FloatRegisterEnumValue)) -#define F21 ((FloatRegister)( F21_FloatRegisterEnumValue)) -#define F22 ((FloatRegister)( F22_FloatRegisterEnumValue)) -#define F23 ((FloatRegister)( F23_FloatRegisterEnumValue)) -#define F24 ((FloatRegister)( F24_FloatRegisterEnumValue)) -#define F25 ((FloatRegister)( F25_FloatRegisterEnumValue)) -#define F26 ((FloatRegister)( F26_FloatRegisterEnumValue)) -#define F27 ((FloatRegister)( F27_FloatRegisterEnumValue)) -#define F28 ((FloatRegister)( F28_FloatRegisterEnumValue)) -#define F29 ((FloatRegister)( F29_FloatRegisterEnumValue)) -#define F30 ((FloatRegister)( F30_FloatRegisterEnumValue)) -#define F31 ((FloatRegister)( F31_FloatRegisterEnumValue)) -#endif // DONT_USE_REGISTER_DEFINES - -// Use SpecialRegister as shortcut -class SpecialRegisterImpl; -typedef SpecialRegisterImpl* SpecialRegister; - -inline SpecialRegister as_SpecialRegister(int encoding) { - return (SpecialRegister)(intptr_t)encoding; +inline constexpr FloatRegister as_FloatRegister(int encoding) { + assert(encoding >= -1 && encoding < 32, "bad float register encoding"); + return FloatRegister(encoding); } +// The float registers of the PPC architecture +constexpr FloatRegister fnoreg = as_FloatRegister(-1); + +constexpr FloatRegister F0 = as_FloatRegister( 0); +constexpr FloatRegister F1 = as_FloatRegister( 1); +constexpr FloatRegister F2 = as_FloatRegister( 2); +constexpr FloatRegister F3 = as_FloatRegister( 3); +constexpr FloatRegister F4 = as_FloatRegister( 4); +constexpr FloatRegister F5 = as_FloatRegister( 5); +constexpr FloatRegister F6 = as_FloatRegister( 6); +constexpr FloatRegister F7 = as_FloatRegister( 7); +constexpr FloatRegister F8 = as_FloatRegister( 8); +constexpr FloatRegister F9 = as_FloatRegister( 9); +constexpr FloatRegister F10 = as_FloatRegister(10); +constexpr FloatRegister F11 = as_FloatRegister(11); +constexpr FloatRegister F12 = as_FloatRegister(12); +constexpr FloatRegister F13 = as_FloatRegister(13); +constexpr FloatRegister F14 = as_FloatRegister(14); +constexpr FloatRegister F15 = as_FloatRegister(15); +constexpr FloatRegister F16 = as_FloatRegister(16); +constexpr FloatRegister F17 = as_FloatRegister(17); +constexpr FloatRegister F18 = as_FloatRegister(18); +constexpr FloatRegister F19 = as_FloatRegister(19); +constexpr FloatRegister F20 = as_FloatRegister(20); +constexpr FloatRegister F21 = as_FloatRegister(21); +constexpr FloatRegister F22 = as_FloatRegister(22); +constexpr FloatRegister F23 = as_FloatRegister(23); +constexpr FloatRegister F24 = as_FloatRegister(24); +constexpr FloatRegister F25 = as_FloatRegister(25); +constexpr FloatRegister F26 = as_FloatRegister(26); +constexpr FloatRegister F27 = as_FloatRegister(27); +constexpr FloatRegister F28 = as_FloatRegister(28); +constexpr FloatRegister F29 = as_FloatRegister(29); +constexpr FloatRegister F30 = as_FloatRegister(30); +constexpr FloatRegister F31 = as_FloatRegister(31); + + // The implementation of special registers for the Power architecture (LR, CTR and friends) -class SpecialRegisterImpl: public AbstractRegisterImpl { +class SpecialRegister { + int _encoding; public: enum { number_of_registers = 6 }; + constexpr SpecialRegister(int encoding = -1) : _encoding(encoding) {} + bool operator==(const SpecialRegister rhs) const { return _encoding == rhs._encoding; } + bool operator!=(const SpecialRegister rhs) const { return _encoding != rhs._encoding; } + const SpecialRegister* operator->() const { return this; } + // construction - inline friend SpecialRegister as_SpecialRegister(int encoding); + inline constexpr friend SpecialRegister as_SpecialRegister(int encoding); // accessors - int encoding() const { assert(is_valid(), "invalid register"); return value(); } - inline VMReg as_VMReg(); + constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; } + inline VMReg as_VMReg() const; // testers - bool is_valid() const { return 0 <= value() && value() < number_of_registers; } + constexpr bool is_valid() const { return (0 <= _encoding && _encoding < number_of_registers); } const char* name() const; }; -// The special registers of the PPC architecture -CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_XER, (0)); -CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_LR, (1)); -CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_CTR, (2)); -CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_VRSAVE, (3)); -CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_SPEFSCR, (4)); -CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_PPR, (5)); - -#ifndef DONT_USE_REGISTER_DEFINES -#define SR_XER ((SpecialRegister)(SR_XER_SpecialRegisterEnumValue)) -#define SR_LR ((SpecialRegister)(SR_LR_SpecialRegisterEnumValue)) -#define SR_CTR ((SpecialRegister)(SR_CTR_SpecialRegisterEnumValue)) -#define SR_VRSAVE ((SpecialRegister)(SR_VRSAVE_SpecialRegisterEnumValue)) -#define SR_SPEFSCR ((SpecialRegister)(SR_SPEFSCR_SpecialRegisterEnumValue)) -#define SR_PPR ((SpecialRegister)(SR_PPR_SpecialRegisterEnumValue)) -#endif // DONT_USE_REGISTER_DEFINES - - -// Use VectorRegister as shortcut -class VectorRegisterImpl; -typedef VectorRegisterImpl* VectorRegister; - -inline VectorRegister as_VectorRegister(int encoding) { - return (VectorRegister)(intptr_t)encoding; +inline constexpr SpecialRegister as_SpecialRegister(int encoding) { + return SpecialRegister(encoding); } +// The special registers of the PPC architecture +constexpr SpecialRegister SR_XER = as_SpecialRegister(0); +constexpr SpecialRegister SR_LR = as_SpecialRegister(1); +constexpr SpecialRegister SR_CTR = as_SpecialRegister(2); +constexpr SpecialRegister SR_VRSAVE = as_SpecialRegister(3); +constexpr SpecialRegister SR_SPEFSCR = as_SpecialRegister(4); +constexpr SpecialRegister SR_PPR = as_SpecialRegister(5); + + // The implementation of vector registers for the Power architecture -class VectorRegisterImpl: public AbstractRegisterImpl { +class VectorRegister { + int _encoding; public: enum { number_of_registers = 32 }; + constexpr VectorRegister(int encoding = -1) : _encoding(encoding) {} + bool operator==(const VectorRegister rhs) const { return _encoding == rhs._encoding; } + bool operator!=(const VectorRegister rhs) const { return _encoding != rhs._encoding; } + const VectorRegister* operator->() const { return this; } + // construction - inline friend VectorRegister as_VectorRegister(int encoding); + inline constexpr friend VectorRegister as_VectorRegister(int encoding); // accessors - int encoding() const { assert(is_valid(), "invalid register"); return value(); } + constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; } // testers - bool is_valid() const { return 0 <= value() && value() < number_of_registers; } + constexpr bool is_valid() const { return (0 <= _encoding && _encoding < number_of_registers); } const char* name() const; @@ -431,100 +329,69 @@ class VectorRegisterImpl: public AbstractRegisterImpl { VectorSRegister to_vsr() const; }; +inline constexpr VectorRegister as_VectorRegister(int encoding) { + return VectorRegister(encoding); +} + // The Vector registers of the Power architecture +constexpr VectorRegister vnoreg = as_VectorRegister(-1); + +constexpr VectorRegister VR0 = as_VectorRegister( 0); +constexpr VectorRegister VR1 = as_VectorRegister( 1); +constexpr VectorRegister VR2 = as_VectorRegister( 2); +constexpr VectorRegister VR3 = as_VectorRegister( 3); +constexpr VectorRegister VR4 = as_VectorRegister( 4); +constexpr VectorRegister VR5 = as_VectorRegister( 5); +constexpr VectorRegister VR6 = as_VectorRegister( 6); +constexpr VectorRegister VR7 = as_VectorRegister( 7); +constexpr VectorRegister VR8 = as_VectorRegister( 8); +constexpr VectorRegister VR9 = as_VectorRegister( 9); +constexpr VectorRegister VR10 = as_VectorRegister(10); +constexpr VectorRegister VR11 = as_VectorRegister(11); +constexpr VectorRegister VR12 = as_VectorRegister(12); +constexpr VectorRegister VR13 = as_VectorRegister(13); +constexpr VectorRegister VR14 = as_VectorRegister(14); +constexpr VectorRegister VR15 = as_VectorRegister(15); +constexpr VectorRegister VR16 = as_VectorRegister(16); +constexpr VectorRegister VR17 = as_VectorRegister(17); +constexpr VectorRegister VR18 = as_VectorRegister(18); +constexpr VectorRegister VR19 = as_VectorRegister(19); +constexpr VectorRegister VR20 = as_VectorRegister(20); +constexpr VectorRegister VR21 = as_VectorRegister(21); +constexpr VectorRegister VR22 = as_VectorRegister(22); +constexpr VectorRegister VR23 = as_VectorRegister(23); +constexpr VectorRegister VR24 = as_VectorRegister(24); +constexpr VectorRegister VR25 = as_VectorRegister(25); +constexpr VectorRegister VR26 = as_VectorRegister(26); +constexpr VectorRegister VR27 = as_VectorRegister(27); +constexpr VectorRegister VR28 = as_VectorRegister(28); +constexpr VectorRegister VR29 = as_VectorRegister(29); +constexpr VectorRegister VR30 = as_VectorRegister(30); +constexpr VectorRegister VR31 = as_VectorRegister(31); -CONSTANT_REGISTER_DECLARATION(VectorRegister, vnoreg, (-1)); - -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR0, ( 0)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR1, ( 1)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR2, ( 2)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR3, ( 3)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR4, ( 4)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR5, ( 5)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR6, ( 6)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR7, ( 7)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR8, ( 8)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR9, ( 9)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR10, (10)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR11, (11)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR12, (12)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR13, (13)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR14, (14)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR15, (15)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR16, (16)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR17, (17)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR18, (18)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR19, (19)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR20, (20)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR21, (21)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR22, (22)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR23, (23)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR24, (24)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR25, (25)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR26, (26)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR27, (27)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR28, (28)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR29, (29)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR30, (30)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, VR31, (31)); - -#ifndef DONT_USE_REGISTER_DEFINES -#define vnoreg ((VectorRegister)(vnoreg_VectorRegisterEnumValue)) -#define VR0 ((VectorRegister)( VR0_VectorRegisterEnumValue)) -#define VR1 ((VectorRegister)( VR1_VectorRegisterEnumValue)) -#define VR2 ((VectorRegister)( VR2_VectorRegisterEnumValue)) -#define VR3 ((VectorRegister)( VR3_VectorRegisterEnumValue)) -#define VR4 ((VectorRegister)( VR4_VectorRegisterEnumValue)) -#define VR5 ((VectorRegister)( VR5_VectorRegisterEnumValue)) -#define VR6 ((VectorRegister)( VR6_VectorRegisterEnumValue)) -#define VR7 ((VectorRegister)( VR7_VectorRegisterEnumValue)) -#define VR8 ((VectorRegister)( VR8_VectorRegisterEnumValue)) -#define VR9 ((VectorRegister)( VR9_VectorRegisterEnumValue)) -#define VR10 ((VectorRegister)( VR10_VectorRegisterEnumValue)) -#define VR11 ((VectorRegister)( VR11_VectorRegisterEnumValue)) -#define VR12 ((VectorRegister)( VR12_VectorRegisterEnumValue)) -#define VR13 ((VectorRegister)( VR13_VectorRegisterEnumValue)) -#define VR14 ((VectorRegister)( VR14_VectorRegisterEnumValue)) -#define VR15 ((VectorRegister)( VR15_VectorRegisterEnumValue)) -#define VR16 ((VectorRegister)( VR16_VectorRegisterEnumValue)) -#define VR17 ((VectorRegister)( VR17_VectorRegisterEnumValue)) -#define VR18 ((VectorRegister)( VR18_VectorRegisterEnumValue)) -#define VR19 ((VectorRegister)( VR19_VectorRegisterEnumValue)) -#define VR20 ((VectorRegister)( VR20_VectorRegisterEnumValue)) -#define VR21 ((VectorRegister)( VR21_VectorRegisterEnumValue)) -#define VR22 ((VectorRegister)( VR22_VectorRegisterEnumValue)) -#define VR23 ((VectorRegister)( VR23_VectorRegisterEnumValue)) -#define VR24 ((VectorRegister)( VR24_VectorRegisterEnumValue)) -#define VR25 ((VectorRegister)( VR25_VectorRegisterEnumValue)) -#define VR26 ((VectorRegister)( VR26_VectorRegisterEnumValue)) -#define VR27 ((VectorRegister)( VR27_VectorRegisterEnumValue)) -#define VR28 ((VectorRegister)( VR28_VectorRegisterEnumValue)) -#define VR29 ((VectorRegister)( VR29_VectorRegisterEnumValue)) -#define VR30 ((VectorRegister)( VR30_VectorRegisterEnumValue)) -#define VR31 ((VectorRegister)( VR31_VectorRegisterEnumValue)) -#endif // DONT_USE_REGISTER_DEFINES - - -inline VectorSRegister as_VectorSRegister(int encoding) { - return (VectorSRegister)(intptr_t)encoding; -} // The implementation of Vector-Scalar (VSX) registers on POWER architecture. -class VectorSRegisterImpl: public AbstractRegisterImpl { +class VectorSRegister { + int _encoding; public: enum { number_of_registers = 64 }; + constexpr VectorSRegister(int encoding = -1) : _encoding(encoding) {} + bool operator==(const VectorSRegister rhs) const { return _encoding == rhs._encoding; } + bool operator!=(const VectorSRegister rhs) const { return _encoding != rhs._encoding; } + const VectorSRegister* operator->() const { return this; } + // construction - inline friend VectorSRegister as_VectorSRegister(int encoding); + inline constexpr friend VectorSRegister as_VectorSRegister(int encoding); // accessors - int encoding() const { assert(is_valid(), "invalid register"); return value(); } - inline VMReg as_VMReg(); + constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; } + inline VMReg as_VMReg() const; // testers - bool is_valid() const { return 0 <= value() && value() < number_of_registers; } + constexpr bool is_valid() const { return (0 <= _encoding && _encoding < number_of_registers); } const char* name() const; @@ -532,145 +399,77 @@ class VectorSRegisterImpl: public AbstractRegisterImpl { VectorRegister to_vr() const; }; -// The Vector-Scalar (VSX) registers of the POWER architecture. +inline constexpr VectorSRegister as_VectorSRegister(int encoding) { + return VectorSRegister(encoding); +} -CONSTANT_REGISTER_DECLARATION(VectorSRegister, vsnoreg, (-1)); - -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR0, ( 0)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR1, ( 1)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR2, ( 2)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR3, ( 3)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR4, ( 4)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR5, ( 5)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR6, ( 6)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR7, ( 7)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR8, ( 8)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR9, ( 9)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR10, (10)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR11, (11)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR12, (12)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR13, (13)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR14, (14)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR15, (15)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR16, (16)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR17, (17)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR18, (18)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR19, (19)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR20, (20)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR21, (21)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR22, (22)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR23, (23)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR24, (24)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR25, (25)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR26, (26)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR27, (27)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR28, (28)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR29, (29)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR30, (30)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR31, (31)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR32, (32)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR33, (33)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR34, (34)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR35, (35)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR36, (36)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR37, (37)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR38, (38)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR39, (39)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR40, (40)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR41, (41)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR42, (42)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR43, (43)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR44, (44)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR45, (45)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR46, (46)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR47, (47)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR48, (48)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR49, (49)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR50, (50)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR51, (51)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR52, (52)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR53, (53)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR54, (54)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR55, (55)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR56, (56)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR57, (57)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR58, (58)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR59, (59)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR60, (60)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR61, (61)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR62, (62)); -CONSTANT_REGISTER_DECLARATION(VectorSRegister, VSR63, (63)); - -#ifndef DONT_USE_REGISTER_DEFINES -#define vsnoreg ((VectorSRegister)(vsnoreg_VectorSRegisterEnumValue)) -#define VSR0 ((VectorSRegister)( VSR0_VectorSRegisterEnumValue)) -#define VSR1 ((VectorSRegister)( VSR1_VectorSRegisterEnumValue)) -#define VSR2 ((VectorSRegister)( VSR2_VectorSRegisterEnumValue)) -#define VSR3 ((VectorSRegister)( VSR3_VectorSRegisterEnumValue)) -#define VSR4 ((VectorSRegister)( VSR4_VectorSRegisterEnumValue)) -#define VSR5 ((VectorSRegister)( VSR5_VectorSRegisterEnumValue)) -#define VSR6 ((VectorSRegister)( VSR6_VectorSRegisterEnumValue)) -#define VSR7 ((VectorSRegister)( VSR7_VectorSRegisterEnumValue)) -#define VSR8 ((VectorSRegister)( VSR8_VectorSRegisterEnumValue)) -#define VSR9 ((VectorSRegister)( VSR9_VectorSRegisterEnumValue)) -#define VSR10 ((VectorSRegister)( VSR10_VectorSRegisterEnumValue)) -#define VSR11 ((VectorSRegister)( VSR11_VectorSRegisterEnumValue)) -#define VSR12 ((VectorSRegister)( VSR12_VectorSRegisterEnumValue)) -#define VSR13 ((VectorSRegister)( VSR13_VectorSRegisterEnumValue)) -#define VSR14 ((VectorSRegister)( VSR14_VectorSRegisterEnumValue)) -#define VSR15 ((VectorSRegister)( VSR15_VectorSRegisterEnumValue)) -#define VSR16 ((VectorSRegister)( VSR16_VectorSRegisterEnumValue)) -#define VSR17 ((VectorSRegister)( VSR17_VectorSRegisterEnumValue)) -#define VSR18 ((VectorSRegister)( VSR18_VectorSRegisterEnumValue)) -#define VSR19 ((VectorSRegister)( VSR19_VectorSRegisterEnumValue)) -#define VSR20 ((VectorSRegister)( VSR20_VectorSRegisterEnumValue)) -#define VSR21 ((VectorSRegister)( VSR21_VectorSRegisterEnumValue)) -#define VSR22 ((VectorSRegister)( VSR22_VectorSRegisterEnumValue)) -#define VSR23 ((VectorSRegister)( VSR23_VectorSRegisterEnumValue)) -#define VSR24 ((VectorSRegister)( VSR24_VectorSRegisterEnumValue)) -#define VSR25 ((VectorSRegister)( VSR25_VectorSRegisterEnumValue)) -#define VSR26 ((VectorSRegister)( VSR26_VectorSRegisterEnumValue)) -#define VSR27 ((VectorSRegister)( VSR27_VectorSRegisterEnumValue)) -#define VSR28 ((VectorSRegister)( VSR28_VectorSRegisterEnumValue)) -#define VSR29 ((VectorSRegister)( VSR29_VectorSRegisterEnumValue)) -#define VSR30 ((VectorSRegister)( VSR30_VectorSRegisterEnumValue)) -#define VSR31 ((VectorSRegister)( VSR31_VectorSRegisterEnumValue)) -#define VSR32 ((VectorSRegister)( VSR32_VectorSRegisterEnumValue)) -#define VSR33 ((VectorSRegister)( VSR33_VectorSRegisterEnumValue)) -#define VSR34 ((VectorSRegister)( VSR34_VectorSRegisterEnumValue)) -#define VSR35 ((VectorSRegister)( VSR35_VectorSRegisterEnumValue)) -#define VSR36 ((VectorSRegister)( VSR36_VectorSRegisterEnumValue)) -#define VSR37 ((VectorSRegister)( VSR37_VectorSRegisterEnumValue)) -#define VSR38 ((VectorSRegister)( VSR38_VectorSRegisterEnumValue)) -#define VSR39 ((VectorSRegister)( VSR39_VectorSRegisterEnumValue)) -#define VSR40 ((VectorSRegister)( VSR40_VectorSRegisterEnumValue)) -#define VSR41 ((VectorSRegister)( VSR41_VectorSRegisterEnumValue)) -#define VSR42 ((VectorSRegister)( VSR42_VectorSRegisterEnumValue)) -#define VSR43 ((VectorSRegister)( VSR43_VectorSRegisterEnumValue)) -#define VSR44 ((VectorSRegister)( VSR44_VectorSRegisterEnumValue)) -#define VSR45 ((VectorSRegister)( VSR45_VectorSRegisterEnumValue)) -#define VSR46 ((VectorSRegister)( VSR46_VectorSRegisterEnumValue)) -#define VSR47 ((VectorSRegister)( VSR47_VectorSRegisterEnumValue)) -#define VSR48 ((VectorSRegister)( VSR48_VectorSRegisterEnumValue)) -#define VSR49 ((VectorSRegister)( VSR49_VectorSRegisterEnumValue)) -#define VSR50 ((VectorSRegister)( VSR50_VectorSRegisterEnumValue)) -#define VSR51 ((VectorSRegister)( VSR51_VectorSRegisterEnumValue)) -#define VSR52 ((VectorSRegister)( VSR52_VectorSRegisterEnumValue)) -#define VSR53 ((VectorSRegister)( VSR53_VectorSRegisterEnumValue)) -#define VSR54 ((VectorSRegister)( VSR54_VectorSRegisterEnumValue)) -#define VSR55 ((VectorSRegister)( VSR55_VectorSRegisterEnumValue)) -#define VSR56 ((VectorSRegister)( VSR56_VectorSRegisterEnumValue)) -#define VSR57 ((VectorSRegister)( VSR57_VectorSRegisterEnumValue)) -#define VSR58 ((VectorSRegister)( VSR58_VectorSRegisterEnumValue)) -#define VSR59 ((VectorSRegister)( VSR59_VectorSRegisterEnumValue)) -#define VSR60 ((VectorSRegister)( VSR60_VectorSRegisterEnumValue)) -#define VSR61 ((VectorSRegister)( VSR61_VectorSRegisterEnumValue)) -#define VSR62 ((VectorSRegister)( VSR62_VectorSRegisterEnumValue)) -#define VSR63 ((VectorSRegister)( VSR63_VectorSRegisterEnumValue)) -#endif // DONT_USE_REGISTER_DEFINES - -// Maximum number of incoming arguments that can be passed in i registers. -const int PPC_ARGS_IN_REGS_NUM = 8; +// The Vector-Scalar (VSX) registers of the POWER architecture. +constexpr VectorSRegister vsnoreg = as_VectorSRegister(-1); + +constexpr VectorSRegister VSR0 = as_VectorSRegister( 0); +constexpr VectorSRegister VSR1 = as_VectorSRegister( 1); +constexpr VectorSRegister VSR2 = as_VectorSRegister( 2); +constexpr VectorSRegister VSR3 = as_VectorSRegister( 3); +constexpr VectorSRegister VSR4 = as_VectorSRegister( 4); +constexpr VectorSRegister VSR5 = as_VectorSRegister( 5); +constexpr VectorSRegister VSR6 = as_VectorSRegister( 6); +constexpr VectorSRegister VSR7 = as_VectorSRegister( 7); +constexpr VectorSRegister VSR8 = as_VectorSRegister( 8); +constexpr VectorSRegister VSR9 = as_VectorSRegister( 9); +constexpr VectorSRegister VSR10 = as_VectorSRegister(10); +constexpr VectorSRegister VSR11 = as_VectorSRegister(11); +constexpr VectorSRegister VSR12 = as_VectorSRegister(12); +constexpr VectorSRegister VSR13 = as_VectorSRegister(13); +constexpr VectorSRegister VSR14 = as_VectorSRegister(14); +constexpr VectorSRegister VSR15 = as_VectorSRegister(15); +constexpr VectorSRegister VSR16 = as_VectorSRegister(16); +constexpr VectorSRegister VSR17 = as_VectorSRegister(17); +constexpr VectorSRegister VSR18 = as_VectorSRegister(18); +constexpr VectorSRegister VSR19 = as_VectorSRegister(19); +constexpr VectorSRegister VSR20 = as_VectorSRegister(20); +constexpr VectorSRegister VSR21 = as_VectorSRegister(21); +constexpr VectorSRegister VSR22 = as_VectorSRegister(22); +constexpr VectorSRegister VSR23 = as_VectorSRegister(23); +constexpr VectorSRegister VSR24 = as_VectorSRegister(24); +constexpr VectorSRegister VSR25 = as_VectorSRegister(25); +constexpr VectorSRegister VSR26 = as_VectorSRegister(26); +constexpr VectorSRegister VSR27 = as_VectorSRegister(27); +constexpr VectorSRegister VSR28 = as_VectorSRegister(28); +constexpr VectorSRegister VSR29 = as_VectorSRegister(29); +constexpr VectorSRegister VSR30 = as_VectorSRegister(30); +constexpr VectorSRegister VSR31 = as_VectorSRegister(31); +constexpr VectorSRegister VSR32 = as_VectorSRegister(32); +constexpr VectorSRegister VSR33 = as_VectorSRegister(33); +constexpr VectorSRegister VSR34 = as_VectorSRegister(34); +constexpr VectorSRegister VSR35 = as_VectorSRegister(35); +constexpr VectorSRegister VSR36 = as_VectorSRegister(36); +constexpr VectorSRegister VSR37 = as_VectorSRegister(37); +constexpr VectorSRegister VSR38 = as_VectorSRegister(38); +constexpr VectorSRegister VSR39 = as_VectorSRegister(39); +constexpr VectorSRegister VSR40 = as_VectorSRegister(40); +constexpr VectorSRegister VSR41 = as_VectorSRegister(41); +constexpr VectorSRegister VSR42 = as_VectorSRegister(42); +constexpr VectorSRegister VSR43 = as_VectorSRegister(43); +constexpr VectorSRegister VSR44 = as_VectorSRegister(44); +constexpr VectorSRegister VSR45 = as_VectorSRegister(45); +constexpr VectorSRegister VSR46 = as_VectorSRegister(46); +constexpr VectorSRegister VSR47 = as_VectorSRegister(47); +constexpr VectorSRegister VSR48 = as_VectorSRegister(48); +constexpr VectorSRegister VSR49 = as_VectorSRegister(49); +constexpr VectorSRegister VSR50 = as_VectorSRegister(50); +constexpr VectorSRegister VSR51 = as_VectorSRegister(51); +constexpr VectorSRegister VSR52 = as_VectorSRegister(52); +constexpr VectorSRegister VSR53 = as_VectorSRegister(53); +constexpr VectorSRegister VSR54 = as_VectorSRegister(54); +constexpr VectorSRegister VSR55 = as_VectorSRegister(55); +constexpr VectorSRegister VSR56 = as_VectorSRegister(56); +constexpr VectorSRegister VSR57 = as_VectorSRegister(57); +constexpr VectorSRegister VSR58 = as_VectorSRegister(58); +constexpr VectorSRegister VSR59 = as_VectorSRegister(59); +constexpr VectorSRegister VSR60 = as_VectorSRegister(60); +constexpr VectorSRegister VSR61 = as_VectorSRegister(61); +constexpr VectorSRegister VSR62 = as_VectorSRegister(62); +constexpr VectorSRegister VSR63 = as_VectorSRegister(63); // Need to know the total number of registers of all sorts for SharedInfo. @@ -678,11 +477,11 @@ const int PPC_ARGS_IN_REGS_NUM = 8; class ConcreteRegisterImpl : public AbstractRegisterImpl { public: enum { - max_gpr = RegisterImpl::number_of_registers * 2, - max_fpr = max_gpr + FloatRegisterImpl::number_of_registers * 2, - max_vsr = max_fpr + VectorSRegisterImpl::number_of_registers, - max_cnd = max_vsr + ConditionRegisterImpl::number_of_registers, - max_spr = max_cnd + SpecialRegisterImpl::number_of_registers, + max_gpr = Register::number_of_registers * 2, + max_fpr = max_gpr + FloatRegister::number_of_registers * 2, + max_vsr = max_fpr + VectorSRegister::number_of_registers, + max_cnd = max_vsr + ConditionRegister::number_of_registers, + max_spr = max_cnd + SpecialRegister::number_of_registers, // This number must be large enough to cover REG_COUNT (defined by c2) registers. // There is no requirement that any ordering here matches any ordering c2 gives // it's optoregs. @@ -691,135 +490,69 @@ class ConcreteRegisterImpl : public AbstractRegisterImpl { }; // Common register declarations used in assembler code. -REGISTER_DECLARATION(Register, R0_SCRATCH, R0); // volatile -REGISTER_DECLARATION(Register, R1_SP, R1); // non-volatile -REGISTER_DECLARATION(Register, R2_TOC, R2); // volatile -REGISTER_DECLARATION(Register, R3_RET, R3); // volatile -REGISTER_DECLARATION(Register, R3_ARG1, R3); // volatile -REGISTER_DECLARATION(Register, R4_ARG2, R4); // volatile -REGISTER_DECLARATION(Register, R5_ARG3, R5); // volatile -REGISTER_DECLARATION(Register, R6_ARG4, R6); // volatile -REGISTER_DECLARATION(Register, R7_ARG5, R7); // volatile -REGISTER_DECLARATION(Register, R8_ARG6, R8); // volatile -REGISTER_DECLARATION(Register, R9_ARG7, R9); // volatile -REGISTER_DECLARATION(Register, R10_ARG8, R10); // volatile -REGISTER_DECLARATION(FloatRegister, F0_SCRATCH, F0); // volatile -REGISTER_DECLARATION(FloatRegister, F1_RET, F1); // volatile -REGISTER_DECLARATION(FloatRegister, F1_ARG1, F1); // volatile -REGISTER_DECLARATION(FloatRegister, F2_ARG2, F2); // volatile -REGISTER_DECLARATION(FloatRegister, F3_ARG3, F3); // volatile -REGISTER_DECLARATION(FloatRegister, F4_ARG4, F4); // volatile -REGISTER_DECLARATION(FloatRegister, F5_ARG5, F5); // volatile -REGISTER_DECLARATION(FloatRegister, F6_ARG6, F6); // volatile -REGISTER_DECLARATION(FloatRegister, F7_ARG7, F7); // volatile -REGISTER_DECLARATION(FloatRegister, F8_ARG8, F8); // volatile -REGISTER_DECLARATION(FloatRegister, F9_ARG9, F9); // volatile -REGISTER_DECLARATION(FloatRegister, F10_ARG10, F10); // volatile -REGISTER_DECLARATION(FloatRegister, F11_ARG11, F11); // volatile -REGISTER_DECLARATION(FloatRegister, F12_ARG12, F12); // volatile -REGISTER_DECLARATION(FloatRegister, F13_ARG13, F13); // volatile - -#ifndef DONT_USE_REGISTER_DEFINES -#define R0_SCRATCH AS_REGISTER(Register, R0) -#define R1_SP AS_REGISTER(Register, R1) -#define R2_TOC AS_REGISTER(Register, R2) -#define R3_RET AS_REGISTER(Register, R3) -#define R3_ARG1 AS_REGISTER(Register, R3) -#define R4_ARG2 AS_REGISTER(Register, R4) -#define R5_ARG3 AS_REGISTER(Register, R5) -#define R6_ARG4 AS_REGISTER(Register, R6) -#define R7_ARG5 AS_REGISTER(Register, R7) -#define R8_ARG6 AS_REGISTER(Register, R8) -#define R9_ARG7 AS_REGISTER(Register, R9) -#define R10_ARG8 AS_REGISTER(Register, R10) -#define F0_SCRATCH AS_REGISTER(FloatRegister, F0) -#define F1_RET AS_REGISTER(FloatRegister, F1) -#define F1_ARG1 AS_REGISTER(FloatRegister, F1) -#define F2_ARG2 AS_REGISTER(FloatRegister, F2) -#define F3_ARG3 AS_REGISTER(FloatRegister, F3) -#define F4_ARG4 AS_REGISTER(FloatRegister, F4) -#define F5_ARG5 AS_REGISTER(FloatRegister, F5) -#define F6_ARG6 AS_REGISTER(FloatRegister, F6) -#define F7_ARG7 AS_REGISTER(FloatRegister, F7) -#define F8_ARG8 AS_REGISTER(FloatRegister, F8) -#define F9_ARG9 AS_REGISTER(FloatRegister, F9) -#define F10_ARG10 AS_REGISTER(FloatRegister, F10) -#define F11_ARG11 AS_REGISTER(FloatRegister, F11) -#define F12_ARG12 AS_REGISTER(FloatRegister, F12) -#define F13_ARG13 AS_REGISTER(FloatRegister, F13) -#endif +constexpr Register R0_SCRATCH = R0; // volatile +constexpr Register R1_SP = R1; // non-volatile +constexpr Register R2_TOC = R2; // volatile +constexpr Register R3_RET = R3; // volatile +constexpr Register R3_ARG1 = R3; // volatile +constexpr Register R4_ARG2 = R4; // volatile +constexpr Register R5_ARG3 = R5; // volatile +constexpr Register R6_ARG4 = R6; // volatile +constexpr Register R7_ARG5 = R7; // volatile +constexpr Register R8_ARG6 = R8; // volatile +constexpr Register R9_ARG7 = R9; // volatile +constexpr Register R10_ARG8 = R10; // volatile +constexpr FloatRegister F0_SCRATCH = F0; // volatile +constexpr FloatRegister F1_RET = F1; // volatile +constexpr FloatRegister F1_ARG1 = F1; // volatile +constexpr FloatRegister F2_ARG2 = F2; // volatile +constexpr FloatRegister F3_ARG3 = F3; // volatile +constexpr FloatRegister F4_ARG4 = F4; // volatile +constexpr FloatRegister F5_ARG5 = F5; // volatile +constexpr FloatRegister F6_ARG6 = F6; // volatile +constexpr FloatRegister F7_ARG7 = F7; // volatile +constexpr FloatRegister F8_ARG8 = F8; // volatile +constexpr FloatRegister F9_ARG9 = F9; // volatile +constexpr FloatRegister F10_ARG10 = F10; // volatile +constexpr FloatRegister F11_ARG11 = F11; // volatile +constexpr FloatRegister F12_ARG12 = F12; // volatile +constexpr FloatRegister F13_ARG13 = F13; // volatile // Register declarations to be used in frame manager assembly code. // Use only non-volatile registers in order to keep values across C-calls. -REGISTER_DECLARATION(Register, R14_bcp, R14); -REGISTER_DECLARATION(Register, R15_esp, R15); -REGISTER_DECLARATION(FloatRegister, F15_ftos, F15); -REGISTER_DECLARATION(Register, R16_thread, R16); // address of current thread -REGISTER_DECLARATION(Register, R17_tos, R17); // address of Java tos (prepushed). -REGISTER_DECLARATION(Register, R18_locals, R18); // address of first param slot (receiver). -REGISTER_DECLARATION(Register, R19_method, R19); // address of current method -#ifndef DONT_USE_REGISTER_DEFINES -#define R14_bcp AS_REGISTER(Register, R14) -#define R15_esp AS_REGISTER(Register, R15) -#define F15_ftos AS_REGISTER(FloatRegister, F15) -#define R16_thread AS_REGISTER(Register, R16) -#define R17_tos AS_REGISTER(Register, R17) -#define R18_locals AS_REGISTER(Register, R18) -#define R19_method AS_REGISTER(Register, R19) -#define R21_sender_SP AS_REGISTER(Register, R21) -#define R23_method_handle AS_REGISTER(Register, R23) -#endif +constexpr Register R14_bcp = R14; +constexpr Register R15_esp = R15; +constexpr FloatRegister F15_ftos = F15; +constexpr Register R16_thread = R16; // address of current thread +constexpr Register R17_tos = R17; // address of Java tos (prepushed). +constexpr Register R18_locals = R18; // address of first param slot (receiver). +constexpr Register R19_method = R19; // address of current method // Temporary registers to be used within frame manager. We can use // the non-volatiles because the call stub has saved them. // Use only non-volatile registers in order to keep values across C-calls. -REGISTER_DECLARATION(Register, R21_tmp1, R21); -REGISTER_DECLARATION(Register, R22_tmp2, R22); -REGISTER_DECLARATION(Register, R23_tmp3, R23); -REGISTER_DECLARATION(Register, R24_tmp4, R24); -REGISTER_DECLARATION(Register, R25_tmp5, R25); -REGISTER_DECLARATION(Register, R26_tmp6, R26); -REGISTER_DECLARATION(Register, R27_tmp7, R27); -REGISTER_DECLARATION(Register, R28_tmp8, R28); -REGISTER_DECLARATION(Register, R29_tmp9, R29); -REGISTER_DECLARATION(Register, R24_dispatch_addr, R24); -REGISTER_DECLARATION(Register, R25_templateTableBase, R25); -REGISTER_DECLARATION(Register, R26_monitor, R26); -REGISTER_DECLARATION(Register, R27_constPoolCache, R27); -REGISTER_DECLARATION(Register, R28_mdx, R28); - -REGISTER_DECLARATION(Register, R19_inline_cache_reg, R19); -REGISTER_DECLARATION(Register, R29_TOC, R29); - -#ifndef DONT_USE_REGISTER_DEFINES -#define R21_tmp1 AS_REGISTER(Register, R21) -#define R22_tmp2 AS_REGISTER(Register, R22) -#define R23_tmp3 AS_REGISTER(Register, R23) -#define R24_tmp4 AS_REGISTER(Register, R24) -#define R25_tmp5 AS_REGISTER(Register, R25) -#define R26_tmp6 AS_REGISTER(Register, R26) -#define R27_tmp7 AS_REGISTER(Register, R27) -#define R28_tmp8 AS_REGISTER(Register, R28) -#define R29_tmp9 AS_REGISTER(Register, R29) -// Lmonitors : monitor pointer -// LcpoolCache: constant pool cache -// mdx: method data index -#define R24_dispatch_addr AS_REGISTER(Register, R24) -#define R25_templateTableBase AS_REGISTER(Register, R25) -#define R26_monitor AS_REGISTER(Register, R26) -#define R27_constPoolCache AS_REGISTER(Register, R27) -#define R28_mdx AS_REGISTER(Register, R28) - -#define R19_inline_cache_reg AS_REGISTER(Register, R19) -#define R29_TOC AS_REGISTER(Register, R29) -#endif +constexpr Register R21_tmp1 = R21; +constexpr Register R22_tmp2 = R22; +constexpr Register R23_tmp3 = R23; +constexpr Register R24_tmp4 = R24; +constexpr Register R25_tmp5 = R25; +constexpr Register R26_tmp6 = R26; +constexpr Register R27_tmp7 = R27; +constexpr Register R28_tmp8 = R28; +constexpr Register R29_tmp9 = R29; +constexpr Register R24_dispatch_addr = R24; +constexpr Register R25_templateTableBase = R25; +constexpr Register R26_monitor = R26; +constexpr Register R27_constPoolCache = R27; +constexpr Register R28_mdx = R28; + +constexpr Register R19_inline_cache_reg = R19; +constexpr Register R21_sender_SP = R21; +constexpr Register R23_method_handle = R23; +constexpr Register R29_TOC = R29; // Scratch registers are volatile. -REGISTER_DECLARATION(Register, R11_scratch1, R11); -REGISTER_DECLARATION(Register, R12_scratch2, R12); -#ifndef DONT_USE_REGISTER_DEFINES -#define R11_scratch1 AS_REGISTER(Register, R11) -#define R12_scratch2 AS_REGISTER(Register, R12) -#endif +constexpr Register R11_scratch1 = R11; +constexpr Register R12_scratch2 = R12; #endif // CPU_PPC_REGISTER_PPC_HPP diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index b53a4ba3ab3c6..ab972771ae32c 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2012, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -322,7 +322,7 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble break; } case RegisterSaver::special_reg: { - if (reg_num == SR_CTR_SpecialRegisterEnumValue) { + if (reg_num == SR_CTR.encoding()) { __ mfctr(R30); __ std(R30, offset, R1_SP); } else { @@ -401,7 +401,7 @@ void RegisterSaver::restore_live_registers_and_pop_frame(MacroAssembler* masm, break; } case RegisterSaver::special_reg: { - if (reg_num == SR_CTR_SpecialRegisterEnumValue) { + if (reg_num == SR_CTR.encoding()) { if (restore_ctr) { // Nothing to do here if ctr already contains the next address. __ ld(R31, offset, R1_SP); __ mtctr(R31); @@ -581,7 +581,7 @@ static int reg2offset(VMReg r) { // as framesizes are fixed. // VMRegImpl::stack0 refers to the first slot 0(sp). // and VMRegImpl::stack0+1 refers to the memory word 4-bytes higher. Register -// up to RegisterImpl::number_of_registers) are the 64-bit +// up to Register::number_of_registers) are the 64-bit // integer registers. // Note: the INPUTS in sig_bt are in units of Java argument words, which are @@ -2015,12 +2015,12 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // out is the index of the outgoing C arguments #ifdef ASSERT - bool reg_destroyed[RegisterImpl::number_of_registers]; - bool freg_destroyed[FloatRegisterImpl::number_of_registers]; - for (int r = 0 ; r < RegisterImpl::number_of_registers ; r++) { + bool reg_destroyed[Register::number_of_registers]; + bool freg_destroyed[FloatRegister::number_of_registers]; + for (int r = 0 ; r < Register::number_of_registers ; r++) { reg_destroyed[r] = false; } - for (int f = 0 ; f < FloatRegisterImpl::number_of_registers ; f++) { + for (int f = 0 ; f < FloatRegister::number_of_registers ; f++) { freg_destroyed[f] = false; } #endif // ASSERT diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index 912be97c96b43..48a61702cf452 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -2133,7 +2133,7 @@ class StubGenerator: public StubCodeGenerator { __ check_klass_subtype_fast_path(sub_klass, super_klass, temp, R0, &L_success, &L_miss, NULL, super_check_offset); - __ check_klass_subtype_slow_path(sub_klass, super_klass, temp, R0, &L_success, NULL); + __ check_klass_subtype_slow_path(sub_klass, super_klass, temp, R0, &L_success); // Fall through on failure! __ bind(L_miss); @@ -3537,7 +3537,7 @@ class StubGenerator: public StubCodeGenerator { // passing the link register's value directly doesn't work. // Since we have to save the link register on the stack anyway, we calculate the corresponding stack address // and pass that one instead. - __ add(R3_ARG1, _abi0(lr), R1_SP); + __ addi(R3_ARG1, R1_SP, _abi0(lr)); __ save_LR_CR(R0); __ push_frame_reg_args(nbytes_save, R0); diff --git a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp index 548c0c8d2f304..04f6b99011181 100644 --- a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2015, 2021 SAP SE. All rights reserved. + * Copyright (c) 2015, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1010,7 +1010,7 @@ void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call, Regist if (native_call) { __ li(R14_bcp, 0); // Must initialize. } else { - __ add(R14_bcp, in_bytes(ConstMethod::codes_offset()), Rconst_method); + __ addi(R14_bcp, Rconst_method, in_bytes(ConstMethod::codes_offset())); } // Resize parent frame. diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 13c13fe843d15..5d33604489ec8 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, 2021 SAP SE. All rights reserved. + * Copyright (c) 2013, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index 43b655cf9d653..8354dcd95a18f 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -117,6 +117,17 @@ void VM_Version::initialize() { FLAG_SET_ERGO(TrapBasedRangeChecks, false); } + // Power7 and later. + if (PowerArchitecturePPC64 > 6) { + if (FLAG_IS_DEFAULT(UsePopCountInstruction)) { + FLAG_SET_ERGO(UsePopCountInstruction, true); + } + } + + if (!VM_Version::has_isel() && FLAG_IS_DEFAULT(ConditionalMoveLimit)) { + FLAG_SET_ERGO(ConditionalMoveLimit, 0); + } + if (PowerArchitecturePPC64 >= 8) { if (FLAG_IS_DEFAULT(SuperwordUseVSX)) { FLAG_SET_ERGO(SuperwordUseVSX, true); @@ -176,6 +187,17 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseByteReverseInstructions, false); } } + + if (OptimizeFill) { + warning("OptimizeFill is not supported on this CPU."); + FLAG_SET_DEFAULT(OptimizeFill, false); + } + + if (OptoScheduling) { + // The OptoScheduling information is not maintained in ppd.ad. + warning("OptoScheduling is not supported on this CPU."); + FLAG_SET_DEFAULT(OptoScheduling, false); + } #endif // Create and print feature-string. diff --git a/src/hotspot/cpu/ppc/vmreg_ppc.cpp b/src/hotspot/cpu/ppc/vmreg_ppc.cpp index 9e4c0325f90c0..4b9194fdf6b74 100644 --- a/src/hotspot/cpu/ppc/vmreg_ppc.cpp +++ b/src/hotspot/cpu/ppc/vmreg_ppc.cpp @@ -33,7 +33,7 @@ void VMRegImpl::set_regName() { for (i = 0; i < ConcreteRegisterImpl::max_gpr; ) { regName[i++] = reg->name(); regName[i++] = reg->name(); - if (reg->encoding() < RegisterImpl::number_of_registers-1) + if (reg->encoding() < Register::number_of_registers-1) reg = reg->successor(); } @@ -41,7 +41,7 @@ void VMRegImpl::set_regName() { for ( ; i < ConcreteRegisterImpl::max_fpr; ) { regName[i++] = freg->name(); regName[i++] = freg->name(); - if (reg->encoding() < FloatRegisterImpl::number_of_registers-1) + if (reg->encoding() < FloatRegister::number_of_registers-1) freg = freg->successor(); } diff --git a/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp b/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp index eb2f5310c4234..2d7fc31dfb9a0 100644 --- a/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2018 SAP SE. All rights reserved. + * Copyright (c) 2012, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,28 +26,30 @@ #ifndef CPU_PPC_VMREG_PPC_INLINE_HPP #define CPU_PPC_VMREG_PPC_INLINE_HPP -inline VMReg RegisterImpl::as_VMReg() { - if (this == noreg) return VMRegImpl::Bad(); +#include "code/vmreg.hpp" +#include "register_ppc.hpp" + +inline VMReg Register::as_VMReg() const { + if (*this == noreg) return VMRegImpl::Bad(); // Two halfs, multiply by 2. return VMRegImpl::as_VMReg(encoding() << 1); } -inline VMReg FloatRegisterImpl::as_VMReg() { +inline VMReg FloatRegister::as_VMReg() const { // Two halfs, multiply by 2. return VMRegImpl::as_VMReg((encoding() << 1) + ConcreteRegisterImpl::max_gpr); } -inline VMReg VectorSRegisterImpl::as_VMReg() { +inline VMReg VectorSRegister::as_VMReg() const { return VMRegImpl::as_VMReg((encoding()) + ConcreteRegisterImpl::max_fpr); } -inline VMReg ConditionRegisterImpl::as_VMReg() { +inline VMReg ConditionRegister::as_VMReg() const { return VMRegImpl::as_VMReg((encoding()) + ConcreteRegisterImpl::max_vsr); } -inline VMReg SpecialRegisterImpl::as_VMReg() { +inline VMReg SpecialRegister::as_VMReg() const { return VMRegImpl::as_VMReg((encoding()) + ConcreteRegisterImpl::max_cnd); } - #endif // CPU_PPC_VMREG_PPC_INLINE_HPP diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index cb9375be738bd..2d18fa5c2c8ab 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -265,7 +265,6 @@ void InterpreterMacroAssembler::get_cache_and_index_and_bytecode_at_bcp(Register la(bytecode, Address(cache, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset())); - membar(MacroAssembler::AnyAny); lwu(bytecode, bytecode); membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); const int shift_count = (1 + byte_no) * BitsPerByte; diff --git a/src/hotspot/cpu/riscv/interpreterRT_riscv.cpp b/src/hotspot/cpu/riscv/interpreterRT_riscv.cpp index d93530d856401..fbfe7e128d5b8 100644 --- a/src/hotspot/cpu/riscv/interpreterRT_riscv.cpp +++ b/src/hotspot/cpu/riscv/interpreterRT_riscv.cpp @@ -74,6 +74,16 @@ InterpreterRuntime::SignatureHandlerGenerator::SignatureHandlerGenerator( _stack_offset = 0; } +// The C ABI specifies: +// "integer scalars narrower than XLEN bits are widened according to the sign +// of their type up to 32 bits, then sign-extended to XLEN bits." +// Applies for both passed in register and stack. +// +// Java uses 32-bit stack slots; jint, jshort, jchar, jbyte uses one slot. +// Native uses 64-bit stack slots for all integer scalar types. +// +// lw loads the Java stack slot, sign-extends and +// sd store this widened integer into a 64 bit native stack slot. void InterpreterRuntime::SignatureHandlerGenerator::pass_int() { const Address src(from(), Interpreter::local_offset_in_bytes(offset())); @@ -82,7 +92,7 @@ void InterpreterRuntime::SignatureHandlerGenerator::pass_int() { __ lw(reg, src); } else { __ lw(x10, src); - __ sw(x10, Address(to(), next_stack_offset())); + __ sd(x10, Address(to(), next_stack_offset())); } } diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 874a7a124fdbc..6dda3924eb257 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -696,37 +696,9 @@ void MacroAssembler::li32(Register Rd, int32_t imm) { upper = (int32_t)upper; // lui Rd, imm[31:12] + imm[11] lui(Rd, upper); - // use addiw to distinguish li32 to li64 addiw(Rd, Rd, lower); } -void MacroAssembler::li64(Register Rd, int64_t imm) { - // Load upper 32 bits. upper = imm[63:32], but if imm[31] == 1 or - // (imm[31:20] == 0x7ff && imm[19] == 1), upper = imm[63:32] + 1. - int64_t lower = imm & 0xffffffff; - lower -= ((lower << 44) >> 44); - int64_t tmp_imm = ((uint64_t)(imm & 0xffffffff00000000)) + (uint64_t)lower; - int32_t upper = (tmp_imm - (int32_t)lower) >> 32; - - // Load upper 32 bits - int64_t up = upper, lo = upper; - lo = (lo << 52) >> 52; - up -= lo; - up = (int32_t)up; - lui(Rd, up); - addi(Rd, Rd, lo); - - // Load the rest 32 bits. - slli(Rd, Rd, 12); - addi(Rd, Rd, (int32_t)lower >> 20); - slli(Rd, Rd, 12); - lower = ((int32_t)imm << 12) >> 20; - addi(Rd, Rd, lower); - slli(Rd, Rd, 8); - lower = imm & 0xff; - addi(Rd, Rd, lower); -} - void MacroAssembler::li(Register Rd, int64_t imm) { // int64_t is in range 0x8000 0000 0000 0000 ~ 0x7fff ffff ffff ffff // li -> c.li @@ -1315,27 +1287,6 @@ static int patch_addr_in_movptr(address branch, address target) { return MOVPTR_INSTRUCTIONS_NUM * NativeInstruction::instruction_size; } -static int patch_imm_in_li64(address branch, address target) { - const int LI64_INSTRUCTIONS_NUM = 8; // lui + addi + slli + addi + slli + addi + slli + addi - int64_t lower = (intptr_t)target & 0xffffffff; - lower = lower - ((lower << 44) >> 44); - int64_t tmp_imm = ((uint64_t)((intptr_t)target & 0xffffffff00000000)) + (uint64_t)lower; - int32_t upper = (tmp_imm - (int32_t)lower) >> 32; - int64_t tmp_upper = upper, tmp_lower = upper; - tmp_lower = (tmp_lower << 52) >> 52; - tmp_upper -= tmp_lower; - tmp_upper >>= 12; - // Load upper 32 bits. Upper = target[63:32], but if target[31] = 1 or (target[31:20] == 0x7ff && target[19] == 1), - // upper = target[63:32] + 1. - Assembler::patch(branch + 0, 31, 12, tmp_upper & 0xfffff); // Lui. - Assembler::patch(branch + 4, 31, 20, tmp_lower & 0xfff); // Addi. - // Load the rest 32 bits. - Assembler::patch(branch + 12, 31, 20, ((int32_t)lower >> 20) & 0xfff); // Addi. - Assembler::patch(branch + 20, 31, 20, (((intptr_t)target << 44) >> 52) & 0xfff); // Addi. - Assembler::patch(branch + 28, 31, 20, (intptr_t)target & 0xff); // Addi. - return LI64_INSTRUCTIONS_NUM * NativeInstruction::instruction_size; -} - static int patch_imm_in_li32(address branch, int32_t target) { const int LI32_INSTRUCTIONS_NUM = 2; // lui + addiw int64_t upper = (intptr_t)target; @@ -1390,16 +1341,6 @@ static address get_target_of_movptr(address insn_addr) { return (address) target_address; } -static address get_target_of_li64(address insn_addr) { - assert_cond(insn_addr != NULL); - intptr_t target_address = (((int64_t)Assembler::sextract(Assembler::ld_instr(insn_addr), 31, 12)) & 0xfffff) << 44; // Lui. - target_address += ((int64_t)Assembler::sextract(Assembler::ld_instr(insn_addr + 4), 31, 20)) << 32; // Addi. - target_address += ((int64_t)Assembler::sextract(Assembler::ld_instr(insn_addr + 12), 31, 20)) << 20; // Addi. - target_address += ((int64_t)Assembler::sextract(Assembler::ld_instr(insn_addr + 20), 31, 20)) << 8; // Addi. - target_address += ((int64_t)Assembler::sextract(Assembler::ld_instr(insn_addr + 28), 31, 20)); // Addi. - return (address)target_address; -} - static address get_target_of_li32(address insn_addr) { assert_cond(insn_addr != NULL); intptr_t target_address = (((int64_t)Assembler::sextract(Assembler::ld_instr(insn_addr), 31, 12)) & 0xfffff) << 12; // Lui. @@ -1420,8 +1361,6 @@ int MacroAssembler::pd_patch_instruction_size(address branch, address target) { return patch_offset_in_pc_relative(branch, offset); } else if (NativeInstruction::is_movptr_at(branch)) { // movptr return patch_addr_in_movptr(branch, target); - } else if (NativeInstruction::is_li64_at(branch)) { // li64 - return patch_imm_in_li64(branch, target); } else if (NativeInstruction::is_li32_at(branch)) { // li32 int64_t imm = (intptr_t)target; return patch_imm_in_li32(branch, (int32_t)imm); @@ -1447,8 +1386,6 @@ address MacroAssembler::target_addr_for_insn(address insn_addr) { offset = get_offset_of_pc_relative(insn_addr); } else if (NativeInstruction::is_movptr_at(insn_addr)) { // movptr return get_target_of_movptr(insn_addr); - } else if (NativeInstruction::is_li64_at(insn_addr)) { // li64 - return get_target_of_li64(insn_addr); } else if (NativeInstruction::is_li32_at(insn_addr)) { // li32 return get_target_of_li32(insn_addr); } else { diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index be1c69c0dc2dc..7cdb2dda1faf9 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -679,7 +679,6 @@ class MacroAssembler: public Assembler { void la(Register Rd, const Address &adr); void li32(Register Rd, int32_t imm); - void li64(Register Rd, int64_t imm); void li (Register Rd, int64_t imm); // optimized load immediate // mv diff --git a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp index bd3530e2df482..bcd345435dd10 100644 --- a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp +++ b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -28,6 +28,7 @@ #include "asm/macroAssembler.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/vmClasses.hpp" +#include "compiler/disassembler.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "memory/allocation.inline.hpp" @@ -37,7 +38,7 @@ #include "runtime/frame.inline.hpp" #include "runtime/stubRoutines.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp index c1f834799a0ad..57300d74e911e 100644 --- a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp @@ -103,18 +103,6 @@ bool NativeInstruction::is_li32_at(address instr) { check_li32_data_dependency(instr); } -bool NativeInstruction::is_li64_at(address instr) { - return is_lui_at(instr) && // lui - is_addi_at(instr + instruction_size) && // addi - is_slli_shift_at(instr + instruction_size * 2, 12) && // Slli Rd, Rs, 12 - is_addi_at(instr + instruction_size * 3) && // addi - is_slli_shift_at(instr + instruction_size * 4, 12) && // Slli Rd, Rs, 12 - is_addi_at(instr + instruction_size * 5) && // addi - is_slli_shift_at(instr + instruction_size * 6, 8) && // Slli Rd, Rs, 8 - is_addi_at(instr + instruction_size * 7) && // addi - check_li64_data_dependency(instr); -} - void NativeCall::verify() { assert(NativeCall::is_call_at((address)this), "unexpected code at call site"); } diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp index eee60479b1d0d..b0826ba5dbcd1 100644 --- a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp @@ -114,40 +114,6 @@ class NativeInstruction { extract_rs1(last_instr) == extract_rd(slli2); } - // the instruction sequence of li64 is as below: - // lui - // addi - // slli - // addi - // slli - // addi - // slli - // addi - static bool check_li64_data_dependency(address instr) { - address lui = instr; - address addi1 = lui + instruction_size; - address slli1 = addi1 + instruction_size; - address addi2 = slli1 + instruction_size; - address slli2 = addi2 + instruction_size; - address addi3 = slli2 + instruction_size; - address slli3 = addi3 + instruction_size; - address addi4 = slli3 + instruction_size; - return extract_rs1(addi1) == extract_rd(lui) && - extract_rs1(addi1) == extract_rd(addi1) && - extract_rs1(slli1) == extract_rd(addi1) && - extract_rs1(slli1) == extract_rd(slli1) && - extract_rs1(addi2) == extract_rd(slli1) && - extract_rs1(addi2) == extract_rd(addi2) && - extract_rs1(slli2) == extract_rd(addi2) && - extract_rs1(slli2) == extract_rd(slli2) && - extract_rs1(addi3) == extract_rd(slli2) && - extract_rs1(addi3) == extract_rd(addi3) && - extract_rs1(slli3) == extract_rd(addi3) && - extract_rs1(slli3) == extract_rd(slli3) && - extract_rs1(addi4) == extract_rd(slli3) && - extract_rs1(addi4) == extract_rd(addi4); - } - // the instruction sequence of li32 is as below: // lui // addiw @@ -182,7 +148,6 @@ class NativeInstruction { static bool is_movptr_at(address instr); static bool is_li32_at(address instr); - static bool is_li64_at(address instr); static bool is_pc_relative_at(address branch); static bool is_load_pc_relative_at(address branch); diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 86b0e0a36f72f..3e8a276c47e4a 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -84,8 +84,8 @@ reg_def R0 ( NS, NS, Op_RegI, 0, x0->as_VMReg() ); // zr reg_def R0_H ( NS, NS, Op_RegI, 0, x0->as_VMReg()->next() ); reg_def R1 ( NS, SOC, Op_RegI, 1, x1->as_VMReg() ); // ra reg_def R1_H ( NS, SOC, Op_RegI, 1, x1->as_VMReg()->next() ); -reg_def R2 ( NS, SOE, Op_RegI, 2, x2->as_VMReg() ); // sp -reg_def R2_H ( NS, SOE, Op_RegI, 2, x2->as_VMReg()->next() ); +reg_def R2 ( NS, NS, Op_RegI, 2, x2->as_VMReg() ); // sp +reg_def R2_H ( NS, NS, Op_RegI, 2, x2->as_VMReg()->next() ); reg_def R3 ( NS, NS, Op_RegI, 3, x3->as_VMReg() ); // gp reg_def R3_H ( NS, NS, Op_RegI, 3, x3->as_VMReg()->next() ); reg_def R4 ( NS, NS, Op_RegI, 4, x4->as_VMReg() ); // tp diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 92d9f8b4b1459..d5a1637489bfd 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -758,15 +758,21 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, return stk_args; } -// On 64 bit we will store integer like items to the stack as -// 64 bits items (riscv64 abi) even though java would only store -// 32bits for a parameter. On 32bit it will simply be 32 bits -// So this routine will do 32->32 on 32bit and 32->64 on 64bit +// The C ABI specifies: +// "integer scalars narrower than XLEN bits are widened according to the sign +// of their type up to 32 bits, then sign-extended to XLEN bits." +// Applies for both passed in register and stack. +// +// Java uses 32-bit stack slots; jint, jshort, jchar, jbyte uses one slot. +// Native uses 64-bit stack slots for all integer scalar types. +// +// lw loads the Java stack slot, sign-extends and +// sd store this widened integer into a 64 bit native stack slot. static void move32_64(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { if (src.first()->is_stack()) { if (dst.first()->is_stack()) { // stack to stack - __ ld(t0, Address(fp, reg2offset_in(src.first()))); + __ lw(t0, Address(fp, reg2offset_in(src.first()))); __ sd(t0, Address(sp, reg2offset_out(dst.first()))); } else { // stack to reg diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp index 0471a66422c77..cd9acf343f313 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -27,6 +27,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "classfile/javaClasses.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/bytecodeTracer.hpp" @@ -66,7 +67,7 @@ // Max size with JVMTI int TemplateInterpreter::InterpreterCodeSize = 256 * 1024; -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> //----------------------------------------------------------------------------- @@ -1691,13 +1692,21 @@ void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, address& vep) { assert(t != NULL && t->is_valid() && t->tos_in() == vtos, "illegal template"); Label L; - aep = __ pc(); __ push_ptr(); __ j(L); - fep = __ pc(); __ push_f(); __ j(L); - dep = __ pc(); __ push_d(); __ j(L); - lep = __ pc(); __ push_l(); __ j(L); - bep = cep = sep = - iep = __ pc(); __ push_i(); - vep = __ pc(); + aep = __ pc(); // atos entry point + __ push_ptr(); + __ j(L); + fep = __ pc(); // ftos entry point + __ push_f(); + __ j(L); + dep = __ pc(); // dtos entry point + __ push_d(); + __ j(L); + lep = __ pc(); // ltos entry point + __ push_l(); + __ j(L); + bep = cep = sep = iep = __ pc(); // [bcsi]tos entry point + __ push_i(); + vep = __ pc(); // vtos entry point __ bind(L); generate_and_dispatch(t); } diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index a35eb1ad18269..db847a0e7bc3b 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/tlab_globals.hpp" @@ -46,7 +47,7 @@ #include "runtime/synchronizer.hpp" #include "utilities/powerOfTwo.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> // Address computation: local variables @@ -317,7 +318,6 @@ void TemplateTable::ldc(bool wide) // get type __ addi(x13, x11, tags_offset); __ add(x13, x10, x13); - __ membar(MacroAssembler::AnyAny); __ lbu(x13, Address(x13, 0)); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); @@ -3491,7 +3491,6 @@ void TemplateTable::_new() { const int tags_offset = Array::base_offset_in_bytes(); __ add(t0, x10, x13); __ la(t0, Address(t0, tags_offset)); - __ membar(MacroAssembler::AnyAny); __ lbu(t0, t0); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); __ sub(t1, t0, (u1)JVM_CONSTANT_Class); @@ -3638,7 +3637,6 @@ void TemplateTable::checkcast() // See if bytecode has already been quicked __ add(t0, x13, Array::base_offset_in_bytes()); __ add(x11, t0, x9); - __ membar(MacroAssembler::AnyAny); __ lbu(x11, x11); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); __ sub(t0, x11, (u1)JVM_CONSTANT_Class); @@ -3694,7 +3692,6 @@ void TemplateTable::instanceof() { // See if bytecode has already been quicked __ add(t0, x13, Array::base_offset_in_bytes()); __ add(x11, t0, x9); - __ membar(MacroAssembler::AnyAny); __ lbu(x11, x11); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); __ sub(t0, x11, (u1)JVM_CONSTANT_Class); diff --git a/src/hotspot/cpu/s390/assembler_s390.hpp b/src/hotspot/cpu/s390/assembler_s390.hpp index 1d64ceebeb03b..d7fa3b0c24ba7 100644 --- a/src/hotspot/cpu/s390/assembler_s390.hpp +++ b/src/hotspot/cpu/s390/assembler_s390.hpp @@ -189,11 +189,6 @@ class Address { _index(noreg), _disp(0) {} - Address(Register base, Register index, intptr_t disp = 0) : - _base(base), - _index(index), - _disp(disp) {} - Address(Register base, intptr_t disp = 0) : _base(base), _index(noreg), diff --git a/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp b/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp index 329c163f313e6..40b96c3ee5197 100644 --- a/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp +++ b/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2018 SAP SE. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -445,6 +445,7 @@ void ArrayCopyStub::emit_code(LIR_Assembler* ce) { "must be aligned"); ce->emit_static_call_stub(); + CHECK_BAILOUT(); // Prepend each BRASL with a nop. __ relocate(relocInfo::static_call_type); diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index eaf83d8b311a6..39894a28af9f7 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -133,9 +133,19 @@ void LIR_Assembler::osr_entry() { // copied into place by code emitted in the IR. Register OSR_buf = osrBufferPointer()->as_register(); - { assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); - int monitor_offset = BytesPerWord * method()->max_locals() + - (2 * BytesPerWord) * (number_of_locks - 1); + { + assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); + + const int locals_space = BytesPerWord * method() -> max_locals(); + int monitor_offset = locals_space + (2 * BytesPerWord) * (number_of_locks - 1); + bool large_offset = !Immediate::is_simm20(monitor_offset + BytesPerWord) && number_of_locks > 0; + + if (large_offset) { + // z_lg can only handle displacement upto 20bit signed binary integer + __ z_algfi(OSR_buf, locals_space); + monitor_offset -= locals_space; + } + // SharedRuntime::OSR_migration_begin() packs BasicObjectLocks in // the OSR buffer using 2 word entries: first the lock and then // the oop. @@ -149,6 +159,10 @@ void LIR_Assembler::osr_entry() { __ z_lg(Z_R1_scratch, slot_offset + 1*BytesPerWord, OSR_buf); __ z_stg(Z_R1_scratch, frame_map()->address_for_monitor_object(i)); } + + if (large_offset) { + __ z_slgfi(OSR_buf, locals_space); + } } } @@ -869,13 +883,13 @@ void LIR_Assembler::stack2stack(LIR_Opr src, LIR_Opr dest, BasicType type) { // 4-byte accesses only! Don't use it to access 8 bytes! Address LIR_Assembler::as_Address_hi(LIR_Address* addr) { ShouldNotCallThis(); - return 0; // unused + return Address(); // unused } // 4-byte accesses only! Don't use it to access 8 bytes! Address LIR_Assembler::as_Address_lo(LIR_Address* addr) { ShouldNotCallThis(); - return 0; // unused + return Address(); // unused } void LIR_Assembler::mem2reg(LIR_Opr src_opr, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, diff --git a/src/hotspot/cpu/s390/interpreterRT_s390.cpp b/src/hotspot/cpu/s390/interpreterRT_s390.cpp index 6d88d71b86252..33b152654061c 100644 --- a/src/hotspot/cpu/s390/interpreterRT_s390.cpp +++ b/src/hotspot/cpu/s390/interpreterRT_s390.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016 SAP SE. All rights reserved. + * Copyright (c) 2016, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,7 +73,7 @@ InterpreterRuntime::SignatureHandlerGenerator::SignatureHandlerGenerator( void InterpreterRuntime::SignatureHandlerGenerator::pass_int() { int int_arg_nr = jni_offset() - _fp_arg_nr; Register r = (int_arg_nr < 5 /*max_int_register_arguments*/) ? - as_Register(int_arg_nr) + Z_ARG1->encoding() : Z_R0; + as_Register(int_arg_nr + Z_ARG1->encoding()) : Z_R0; __ z_lgf(r, locals_j_arg_at(offset())); if (DEBUG_ONLY(true ||) int_arg_nr >= 5) { @@ -84,7 +84,7 @@ void InterpreterRuntime::SignatureHandlerGenerator::pass_int() { void InterpreterRuntime::SignatureHandlerGenerator::pass_long() { int int_arg_nr = jni_offset() - _fp_arg_nr; Register r = (int_arg_nr < 5 /*max_int_register_arguments*/) ? - as_Register(int_arg_nr) + Z_ARG1->encoding() : Z_R0; + as_Register(int_arg_nr + Z_ARG1->encoding()) : Z_R0; __ z_lg(r, locals_j_arg_at(offset() + 1)); // Long resides in upper slot. if (DEBUG_ONLY(true ||) int_arg_nr >= 5) { @@ -115,7 +115,7 @@ void InterpreterRuntime::SignatureHandlerGenerator::pass_double() { void InterpreterRuntime::SignatureHandlerGenerator::pass_object() { int int_arg_nr = jni_offset() - _fp_arg_nr; Register r = (int_arg_nr < 5 /*max_int_register_arguments*/) ? - as_Register(int_arg_nr) + Z_ARG1->encoding() : Z_R0; + as_Register(int_arg_nr + Z_ARG1->encoding()) : Z_R0; // The handle for a receiver will never be null. bool do_NULL_check = offset() != 0 || is_static(); diff --git a/src/hotspot/cpu/s390/methodHandles_s390.cpp b/src/hotspot/cpu/s390/methodHandles_s390.cpp index 1bae203f75a8a..9553cee3be258 100644 --- a/src/hotspot/cpu/s390/methodHandles_s390.cpp +++ b/src/hotspot/cpu/s390/methodHandles_s390.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2017 SAP SE. All rights reserved. + * Copyright (c) 2016, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -634,7 +634,7 @@ void MethodHandles::trace_method_handle(MacroAssembler* _masm, const char* adapt if (!log_is_enabled(Info, methodhandles)) { return; } // If arg registers are contiguous, we can use STMG/LMG. - assert((Z_ARG5->encoding() - Z_ARG1->encoding() + 1) == RegisterImpl::number_of_arg_registers, "Oops"); + assert((Z_ARG5->encoding() - Z_ARG1->encoding() + 1) == Register::number_of_arg_registers, "Oops"); BLOCK_COMMENT("trace_method_handle {"); diff --git a/src/hotspot/cpu/s390/register_definitions_s390.cpp b/src/hotspot/cpu/s390/register_definitions_s390.cpp deleted file mode 100644 index 2378d513799fe..0000000000000 --- a/src/hotspot/cpu/s390/register_definitions_s390.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2017 SAP SE. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -// Make sure the defines don't screw up the declarations later on in this file. -#define DONT_USE_REGISTER_DEFINES - -#include "precompiled.hpp" -#include "asm/assembler.hpp" -#include "asm/register.hpp" -#include "register_s390.hpp" -#include "interp_masm_s390.hpp" - -REGISTER_DEFINITION(Register, noreg); - -REGISTER_DEFINITION(FloatRegister, fnoreg); - -REGISTER_DEFINITION(VectorRegister, vnoreg); diff --git a/src/hotspot/cpu/s390/register_s390.cpp b/src/hotspot/cpu/s390/register_s390.cpp index 853b564247077..f055a1c013441 100644 --- a/src/hotspot/cpu/s390/register_s390.cpp +++ b/src/hotspot/cpu/s390/register_s390.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2017 SAP SE. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,11 +27,11 @@ #include "register_s390.hpp" -const int ConcreteRegisterImpl::max_gpr = RegisterImpl::number_of_registers * 2; +const int ConcreteRegisterImpl::max_gpr = Register::number_of_registers * 2; const int ConcreteRegisterImpl::max_fpr = ConcreteRegisterImpl::max_gpr + - FloatRegisterImpl::number_of_registers * 2; + FloatRegister::number_of_registers * 2; -const char* RegisterImpl::name() const { +const char* Register::name() const { const char* names[number_of_registers] = { "Z_R0", "Z_R1", "Z_R2", "Z_R3", "Z_R4", "Z_R5", "Z_R6", "Z_R7", "Z_R8", "Z_R9", "Z_R10", "Z_R11", "Z_R12", "Z_R13", "Z_R14", "Z_R15" @@ -39,15 +39,15 @@ const char* RegisterImpl::name() const { return is_valid() ? names[encoding()] : "noreg"; } -const char* FloatRegisterImpl::name() const { +const char* FloatRegister::name() const { const char* names[number_of_registers] = { - "Z_F0", "Z_F1", "Z_F2", "Z_F3", "Z_F4", "Z_F5", "Z_F6", "Z_F7", "Z_F8", "Z_F9", - "Z_F10", "Z_F11", "Z_F12", "Z_F13", "Z_F14", "Z_F15" + "Z_F0", "Z_F1", "Z_F2", "Z_F3", "Z_F4", "Z_F5", "Z_F6", "Z_F7", + "Z_F8", "Z_F9", "Z_F10", "Z_F11", "Z_F12", "Z_F13", "Z_F14", "Z_F15" }; return is_valid() ? names[encoding()] : "fnoreg"; } -const char* VectorRegisterImpl::name() const { +const char* VectorRegister::name() const { const char* names[number_of_registers] = { "Z_V0", "Z_V1", "Z_V2", "Z_V3", "Z_V4", "Z_V5", "Z_V6", "Z_V7", "Z_V8", "Z_V9", "Z_V10", "Z_V11", "Z_V12", "Z_V13", "Z_V14", "Z_V15", diff --git a/src/hotspot/cpu/s390/register_s390.hpp b/src/hotspot/cpu/s390/register_s390.hpp index 59da743016f40..931e899257e92 100644 --- a/src/hotspot/cpu/s390/register_s390.hpp +++ b/src/hotspot/cpu/s390/register_s390.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2019 SAP SE. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,9 +29,10 @@ #include "asm/register.hpp" #include "runtime/vm_version.hpp" -class Address; -class VMRegImpl; +#define NOREG_ENCODING -1 +// forward declaration +class VMRegImpl; typedef VMRegImpl* VMReg; @@ -57,235 +58,175 @@ typedef VMRegImpl* VMReg; //=== Integer Registers === //=========================== -// Use Register as shortcut. -class RegisterImpl; -typedef RegisterImpl* Register; - // The implementation of integer registers for z/Architecture. - -inline Register as_Register(int encoding) { - return (Register)(long)encoding; -} - -class RegisterImpl: public AbstractRegisterImpl { - public: +class Register { + int _encoding; +public: enum { number_of_registers = 16, number_of_arg_registers = 5 }; - // general construction - inline friend Register as_Register(int encoding); + constexpr Register(int encoding = NOREG_ENCODING) : _encoding(encoding) {} + bool operator==(const Register rhs) const { return _encoding == rhs._encoding; } + bool operator!=(const Register rhs) const { return _encoding != rhs._encoding; } + const Register* operator->() const { return this; } - inline VMReg as_VMReg(); + // general construction + inline constexpr friend Register as_Register(int encoding); // accessors - int encoding() const { assert(is_valid(), "invalid register"); return value(); } const char* name() const; + inline VMReg as_VMReg() const; + constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; } - // testers - bool is_valid() const { return (0 <= (value()&0x7F) && (value()&0x7F) < number_of_registers); } - bool is_even() const { return (encoding() & 1) == 0; } - bool is_volatile() const { return (0 <= (value()&0x7F) && (value()&0x7F) <= 5) || (value()&0x7F)==14; } - bool is_nonvolatile() const { return is_valid() && !is_volatile(); } - - public: // derived registers, offsets, and addresses - Register predecessor() const { return as_Register((encoding()-1) & (number_of_registers-1)); } - Register successor() const { return as_Register((encoding() + 1) & (number_of_registers-1)); } + Register predecessor() const { return Register((encoding() - 1) & (number_of_registers - 1)); } + Register successor() const { return Register((encoding() + 1) & (number_of_registers - 1)); } + + // testers + constexpr bool is_valid() const { return (0 <= _encoding && _encoding < number_of_registers); } + constexpr bool is_even() const { return (_encoding & 1) == 0; } + constexpr bool is_volatile() const { return (0 <= _encoding && _encoding <= 5) || _encoding == 14; } + constexpr bool is_nonvolatile() const { return is_valid() && !is_volatile(); } }; -// The integer registers of the z/Architecture. +inline constexpr Register as_Register(int encoding) { + assert(encoding == NOREG_ENCODING || + (0 <= encoding && encoding < Register::number_of_registers), "bad register encoding"); + return Register(encoding); +} -CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1)); - -CONSTANT_REGISTER_DECLARATION(Register, Z_R0, (0)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R1, (1)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R2, (2)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R3, (3)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R4, (4)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R5, (5)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R6, (6)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R7, (7)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R8, (8)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R9, (9)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R10, (10)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R11, (11)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R12, (12)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R13, (13)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R14, (14)); -CONSTANT_REGISTER_DECLARATION(Register, Z_R15, (15)); +// The integer registers of the z/Architecture. +constexpr Register noreg = as_Register(NOREG_ENCODING); + +constexpr Register Z_R0 = as_Register( 0); +constexpr Register Z_R1 = as_Register( 1); +constexpr Register Z_R2 = as_Register( 2); +constexpr Register Z_R3 = as_Register( 3); +constexpr Register Z_R4 = as_Register( 4); +constexpr Register Z_R5 = as_Register( 5); +constexpr Register Z_R6 = as_Register( 6); +constexpr Register Z_R7 = as_Register( 7); +constexpr Register Z_R8 = as_Register( 8); +constexpr Register Z_R9 = as_Register( 9); +constexpr Register Z_R10 = as_Register(10); +constexpr Register Z_R11 = as_Register(11); +constexpr Register Z_R12 = as_Register(12); +constexpr Register Z_R13 = as_Register(13); +constexpr Register Z_R14 = as_Register(14); +constexpr Register Z_R15 = as_Register(15); //============================= //=== Condition Registers === //============================= -// Use ConditionRegister as shortcut -class ConditionRegisterImpl; -typedef ConditionRegisterImpl* ConditionRegister; - // The implementation of condition register(s) for the z/Architecture. -class ConditionRegisterImpl: public AbstractRegisterImpl { - public: - +class ConditionRegister { + int _encoding; +public: enum { number_of_registers = 1 }; + constexpr ConditionRegister(int encoding = NOREG_ENCODING) : _encoding(encoding) {} + bool operator==(const ConditionRegister rhs) const { return _encoding == rhs._encoding; } + bool operator!=(const ConditionRegister rhs) const { return _encoding != rhs._encoding; } + const ConditionRegister* operator->() const { return this; } + // accessors - int encoding() const { - assert(is_valid(), "invalid register"); return value(); - } + constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; } + inline VMReg as_VMReg() const; // testers - bool is_valid() const { - return (0 <= value() && value() < number_of_registers); - } - bool is_volatile() const { - return true; - } - bool is_nonvolatile() const { - return false; - } + constexpr bool is_valid() const { return (0 <= _encoding && _encoding < number_of_registers); } + constexpr bool is_volatile() const { return true; } + constexpr bool is_nonvolatile() const { return false;} // construction. - inline friend ConditionRegister as_ConditionRegister(int encoding); - - inline VMReg as_VMReg(); + inline constexpr friend ConditionRegister as_ConditionRegister(int encoding); }; -inline ConditionRegister as_ConditionRegister(int encoding) { - assert(encoding >= 0 && encoding < ConditionRegisterImpl::number_of_registers, "bad condition register encoding"); - return (ConditionRegister)(long)encoding; +inline constexpr ConditionRegister as_ConditionRegister(int encoding) { + assert(encoding == NOREG_ENCODING || + (encoding >= 0 && encoding < ConditionRegister::number_of_registers), "bad condition register encoding"); + return ConditionRegister(encoding); } // The condition register of the z/Architecture. -CONSTANT_REGISTER_DECLARATION(ConditionRegister, Z_CR, (0)); - -// Because z/Architecture has so many registers, #define'ing values for them is -// beneficial in code size and is worth the cost of some of the -// dangers of defines. -// If a particular file has a problem with these defines then it's possible -// to turn them off in that file by defining -// DONT_USE_REGISTER_DEFINES. Register_definitions_s390.cpp does that -// so that it's able to provide real definitions of these registers -// for use in debuggers and such. - -#ifndef DONT_USE_REGISTER_DEFINES -#define noreg ((Register)(noreg_RegisterEnumValue)) - -#define Z_R0 ((Register)(Z_R0_RegisterEnumValue)) -#define Z_R1 ((Register)(Z_R1_RegisterEnumValue)) -#define Z_R2 ((Register)(Z_R2_RegisterEnumValue)) -#define Z_R3 ((Register)(Z_R3_RegisterEnumValue)) -#define Z_R4 ((Register)(Z_R4_RegisterEnumValue)) -#define Z_R5 ((Register)(Z_R5_RegisterEnumValue)) -#define Z_R6 ((Register)(Z_R6_RegisterEnumValue)) -#define Z_R7 ((Register)(Z_R7_RegisterEnumValue)) -#define Z_R8 ((Register)(Z_R8_RegisterEnumValue)) -#define Z_R9 ((Register)(Z_R9_RegisterEnumValue)) -#define Z_R10 ((Register)(Z_R10_RegisterEnumValue)) -#define Z_R11 ((Register)(Z_R11_RegisterEnumValue)) -#define Z_R12 ((Register)(Z_R12_RegisterEnumValue)) -#define Z_R13 ((Register)(Z_R13_RegisterEnumValue)) -#define Z_R14 ((Register)(Z_R14_RegisterEnumValue)) -#define Z_R15 ((Register)(Z_R15_RegisterEnumValue)) - -#define Z_CR ((ConditionRegister)(Z_CR_ConditionRegisterEnumValue)) -#endif // DONT_USE_REGISTER_DEFINES - +constexpr ConditionRegister Z_CR = as_ConditionRegister(0); //========================= //=== Float Registers === //========================= -// Use FloatRegister as shortcut -class FloatRegisterImpl; -typedef FloatRegisterImpl* FloatRegister; - // The implementation of float registers for the z/Architecture. -inline FloatRegister as_FloatRegister(int encoding) { - return (FloatRegister)(long)encoding; -} - -class FloatRegisterImpl: public AbstractRegisterImpl { - public: +class FloatRegister { + int _encoding; +public: enum { number_of_registers = 16, number_of_arg_registers = 4 }; - // construction - inline friend FloatRegister as_FloatRegister(int encoding); + constexpr FloatRegister(int encoding = NOREG_ENCODING) : _encoding(encoding) {} + bool operator==(const FloatRegister rhs) const { return _encoding == rhs._encoding; } + bool operator!=(const FloatRegister rhs) const { return _encoding != rhs._encoding; } + const FloatRegister* operator->() const { return this; } - inline VMReg as_VMReg(); + // construction + inline constexpr friend FloatRegister as_FloatRegister(int encoding); // accessors - int encoding() const { - assert(is_valid(), "invalid register"); return value(); - } + constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; } + inline VMReg as_VMReg() const; + FloatRegister successor() const { return FloatRegister((encoding() + 1) & (number_of_registers - 1)); } - bool is_valid() const { return 0 <= value() && value() < number_of_registers; } - bool is_volatile() const { return (0 <= (value()&0x7F) && (value()&0x7F) <= 7); } - bool is_nonvolatile() const { return (8 <= (value()&0x7F) && (value()&0x7F) <= 15); } + // tester + constexpr bool is_valid() const { return 0 <= _encoding && _encoding < number_of_registers; } + constexpr bool is_volatile() const { return (0 <= _encoding && _encoding <= 7); } + constexpr bool is_nonvolatile() const { return (8 <= _encoding && _encoding <= 15); } const char* name() const; - - FloatRegister successor() const { return as_FloatRegister(encoding() + 1); } }; -// The float registers of z/Architecture. +inline constexpr FloatRegister as_FloatRegister(int encoding) { + assert(encoding == NOREG_ENCODING || + (encoding >= 0 && encoding < FloatRegister::number_of_registers), "bad float register encoding"); + return FloatRegister(encoding); +} -CONSTANT_REGISTER_DECLARATION(FloatRegister, fnoreg, (-1)); - -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F0, (0)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F1, (1)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F2, (2)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F3, (3)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F4, (4)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F5, (5)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F6, (6)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F7, (7)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F8, (8)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F9, (9)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F10, (10)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F11, (11)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F12, (12)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F13, (13)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F14, (14)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F15, (15)); - -#ifndef DONT_USE_REGISTER_DEFINES -#define fnoreg ((FloatRegister)(fnoreg_FloatRegisterEnumValue)) -#define Z_F0 ((FloatRegister)( Z_F0_FloatRegisterEnumValue)) -#define Z_F1 ((FloatRegister)( Z_F1_FloatRegisterEnumValue)) -#define Z_F2 ((FloatRegister)( Z_F2_FloatRegisterEnumValue)) -#define Z_F3 ((FloatRegister)( Z_F3_FloatRegisterEnumValue)) -#define Z_F4 ((FloatRegister)( Z_F4_FloatRegisterEnumValue)) -#define Z_F5 ((FloatRegister)( Z_F5_FloatRegisterEnumValue)) -#define Z_F6 ((FloatRegister)( Z_F6_FloatRegisterEnumValue)) -#define Z_F7 ((FloatRegister)( Z_F7_FloatRegisterEnumValue)) -#define Z_F8 ((FloatRegister)( Z_F8_FloatRegisterEnumValue)) -#define Z_F9 ((FloatRegister)( Z_F9_FloatRegisterEnumValue)) -#define Z_F10 ((FloatRegister)( Z_F10_FloatRegisterEnumValue)) -#define Z_F11 ((FloatRegister)( Z_F11_FloatRegisterEnumValue)) -#define Z_F12 ((FloatRegister)( Z_F12_FloatRegisterEnumValue)) -#define Z_F13 ((FloatRegister)( Z_F13_FloatRegisterEnumValue)) -#define Z_F14 ((FloatRegister)( Z_F14_FloatRegisterEnumValue)) -#define Z_F15 ((FloatRegister)( Z_F15_FloatRegisterEnumValue)) -#endif // DONT_USE_REGISTER_DEFINES +// The float registers of z/Architecture. +constexpr FloatRegister fnoreg = as_FloatRegister(NOREG_ENCODING); + +constexpr FloatRegister Z_F0 = as_FloatRegister( 0); +constexpr FloatRegister Z_F1 = as_FloatRegister( 1); +constexpr FloatRegister Z_F2 = as_FloatRegister( 2); +constexpr FloatRegister Z_F3 = as_FloatRegister( 3); +constexpr FloatRegister Z_F4 = as_FloatRegister( 4); +constexpr FloatRegister Z_F5 = as_FloatRegister( 5); +constexpr FloatRegister Z_F6 = as_FloatRegister( 6); +constexpr FloatRegister Z_F7 = as_FloatRegister( 7); +constexpr FloatRegister Z_F8 = as_FloatRegister( 8); +constexpr FloatRegister Z_F9 = as_FloatRegister( 9); +constexpr FloatRegister Z_F10 = as_FloatRegister(10); +constexpr FloatRegister Z_F11 = as_FloatRegister(11); +constexpr FloatRegister Z_F12 = as_FloatRegister(12); +constexpr FloatRegister Z_F13 = as_FloatRegister(13); +constexpr FloatRegister Z_F14 = as_FloatRegister(14); +constexpr FloatRegister Z_F15 = as_FloatRegister(15); // Single, Double and Quad fp reg classes. These exist to map the ADLC // encoding for a floating point register, to the FloatRegister number -// desired by the macroassembler. A FloatRegister is a number between +// desired by the macroAssembler. A FloatRegister is a number between // 0 and 31 passed around as a pointer. For ADLC, an fp register encoding // is the actual bit encoding used by the z/Architecture hardware. When ADLC used -// the macroassembler to generate an instruction that references, e.g., a -// double fp reg, it passed the bit encoding to the macroassembler via +// the macroAssembler to generate an instruction that references, e.g., a +// double fp reg, it passed the bit encoding to the macroAssembler via // as_FloatRegister, which, for double regs > 30, returns an illegal // register number. // @@ -295,35 +236,39 @@ CONSTANT_REGISTER_DECLARATION(FloatRegister, Z_F15, (15)); // hence the definitions of as_xxxFloatRegister as class methods rather // than as external inline routines. -class SingleFloatRegisterImpl; -typedef SingleFloatRegisterImpl *SingleFloatRegister; +class SingleFloatRegister { +public: + enum { + number_of_registers = 32 + }; + const SingleFloatRegister* operator->() const { return this; } -class SingleFloatRegisterImpl { - public: - friend FloatRegister as_SingleFloatRegister(int encoding) { - assert(encoding < 32, "bad single float register encoding"); + inline constexpr friend FloatRegister as_SingleFloatRegister(int encoding) { + assert(encoding < number_of_registers, "bad single float register encoding"); return as_FloatRegister(encoding); } }; -class DoubleFloatRegisterImpl; -typedef DoubleFloatRegisterImpl *DoubleFloatRegister; +class DoubleFloatRegister { +public: -class DoubleFloatRegisterImpl { - public: - friend FloatRegister as_DoubleFloatRegister(int encoding) { - assert(encoding < 32, "bad double float register encoding"); + const DoubleFloatRegister* operator->() const { return this; } + + inline constexpr friend FloatRegister as_DoubleFloatRegister(int encoding) { return as_FloatRegister(((encoding & 1) << 5) | (encoding & 0x1e)); } }; -class QuadFloatRegisterImpl; -typedef QuadFloatRegisterImpl *QuadFloatRegister; +class QuadFloatRegister { +public: + enum { + number_of_registers = 32 + }; + + const QuadFloatRegister* operator->() const { return this; } -class QuadFloatRegisterImpl { - public: - friend FloatRegister as_QuadFloatRegister(int encoding) { - assert(encoding < 32 && ((encoding & 2) == 0), "bad quad float register encoding"); + inline constexpr friend FloatRegister as_QuadFloatRegister(int encoding) { + assert(encoding < QuadFloatRegister::number_of_registers && ((encoding & 2) == 0), "bad quad float register encoding"); return as_FloatRegister(((encoding & 1) << 5) | (encoding & 0x1c)); } }; @@ -333,36 +278,35 @@ class QuadFloatRegisterImpl { //=== Vector Registers === //========================== -// Use VectorRegister as shortcut -class VectorRegisterImpl; -typedef VectorRegisterImpl* VectorRegister; - // The implementation of vector registers for z/Architecture. -inline VectorRegister as_VectorRegister(int encoding) { - return (VectorRegister)(long)encoding; -} - -class VectorRegisterImpl: public AbstractRegisterImpl { - public: +class VectorRegister { + int _encoding; +public: enum { number_of_registers = 32, number_of_arg_registers = 0 }; + constexpr VectorRegister(int encoding = NOREG_ENCODING) : _encoding(encoding) {} + bool operator==(const VectorRegister rhs) const { return _encoding == rhs._encoding; } + bool operator!=(const VectorRegister rhs) const { return _encoding != rhs._encoding; } + const VectorRegister* operator->() const { return this; } + // construction - inline friend VectorRegister as_VectorRegister(int encoding); + inline constexpr friend VectorRegister as_VectorRegister(int encoding); - inline VMReg as_VMReg(); + inline VMReg as_VMReg() const; // accessors - int encoding() const { - assert(is_valid(), "invalid register"); return value(); - } + constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; } + VectorRegister successor() const { return VectorRegister((encoding() + 1) & (number_of_registers - 1)); } - bool is_valid() const { return 0 <= value() && value() < number_of_registers; } - bool is_volatile() const { return true; } - bool is_nonvolatile() const { return false; } + + // tester + constexpr bool is_valid() const { return 0 <= _encoding && _encoding < number_of_registers; } + constexpr bool is_volatile() const { return true; } + constexpr bool is_nonvolatile() const { return false; } // Register fields in z/Architecture instructions are 4 bits wide, restricting the // addressable register set size to 16. @@ -374,7 +318,7 @@ class VectorRegisterImpl: public AbstractRegisterImpl { // register field in the instruction. // Example: // The register field starting at bit position 12 in the instruction is assigned RXB bit 0b0100. - int64_t RXB_mask(int pos) { + int64_t RXB_mask(int pos) const { if (encoding() >= number_of_registers/2) { switch (pos) { case 8: return ((int64_t)0b1000) << 8; // actual bit pos: 36 @@ -389,83 +333,49 @@ class VectorRegisterImpl: public AbstractRegisterImpl { } const char* name() const; - - VectorRegister successor() const { return as_VectorRegister(encoding() + 1); } }; -// The Vector registers of z/Architecture. - -CONSTANT_REGISTER_DECLARATION(VectorRegister, vnoreg, (-1)); - -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V0, (0)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V1, (1)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V2, (2)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V3, (3)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V4, (4)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V5, (5)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V6, (6)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V7, (7)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V8, (8)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V9, (9)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V10, (10)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V11, (11)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V12, (12)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V13, (13)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V14, (14)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V15, (15)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V16, (16)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V17, (17)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V18, (18)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V19, (19)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V20, (20)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V21, (21)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V22, (22)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V23, (23)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V24, (24)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V25, (25)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V26, (26)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V27, (27)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V28, (28)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V29, (29)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V30, (30)); -CONSTANT_REGISTER_DECLARATION(VectorRegister, Z_V31, (31)); - -#ifndef DONT_USE_REGISTER_DEFINES -#define vnoreg ((VectorRegister)(vnoreg_VectorRegisterEnumValue)) -#define Z_V0 ((VectorRegister)( Z_V0_VectorRegisterEnumValue)) -#define Z_V1 ((VectorRegister)( Z_V1_VectorRegisterEnumValue)) -#define Z_V2 ((VectorRegister)( Z_V2_VectorRegisterEnumValue)) -#define Z_V3 ((VectorRegister)( Z_V3_VectorRegisterEnumValue)) -#define Z_V4 ((VectorRegister)( Z_V4_VectorRegisterEnumValue)) -#define Z_V5 ((VectorRegister)( Z_V5_VectorRegisterEnumValue)) -#define Z_V6 ((VectorRegister)( Z_V6_VectorRegisterEnumValue)) -#define Z_V7 ((VectorRegister)( Z_V7_VectorRegisterEnumValue)) -#define Z_V8 ((VectorRegister)( Z_V8_VectorRegisterEnumValue)) -#define Z_V9 ((VectorRegister)( Z_V9_VectorRegisterEnumValue)) -#define Z_V10 ((VectorRegister)( Z_V10_VectorRegisterEnumValue)) -#define Z_V11 ((VectorRegister)( Z_V11_VectorRegisterEnumValue)) -#define Z_V12 ((VectorRegister)( Z_V12_VectorRegisterEnumValue)) -#define Z_V13 ((VectorRegister)( Z_V13_VectorRegisterEnumValue)) -#define Z_V14 ((VectorRegister)( Z_V14_VectorRegisterEnumValue)) -#define Z_V15 ((VectorRegister)( Z_V15_VectorRegisterEnumValue)) -#define Z_V16 ((VectorRegister)( Z_V16_VectorRegisterEnumValue)) -#define Z_V17 ((VectorRegister)( Z_V17_VectorRegisterEnumValue)) -#define Z_V18 ((VectorRegister)( Z_V18_VectorRegisterEnumValue)) -#define Z_V19 ((VectorRegister)( Z_V19_VectorRegisterEnumValue)) -#define Z_V20 ((VectorRegister)( Z_V20_VectorRegisterEnumValue)) -#define Z_V21 ((VectorRegister)( Z_V21_VectorRegisterEnumValue)) -#define Z_V22 ((VectorRegister)( Z_V22_VectorRegisterEnumValue)) -#define Z_V23 ((VectorRegister)( Z_V23_VectorRegisterEnumValue)) -#define Z_V24 ((VectorRegister)( Z_V24_VectorRegisterEnumValue)) -#define Z_V25 ((VectorRegister)( Z_V25_VectorRegisterEnumValue)) -#define Z_V26 ((VectorRegister)( Z_V26_VectorRegisterEnumValue)) -#define Z_V27 ((VectorRegister)( Z_V27_VectorRegisterEnumValue)) -#define Z_V28 ((VectorRegister)( Z_V28_VectorRegisterEnumValue)) -#define Z_V29 ((VectorRegister)( Z_V29_VectorRegisterEnumValue)) -#define Z_V30 ((VectorRegister)( Z_V30_VectorRegisterEnumValue)) -#define Z_V31 ((VectorRegister)( Z_V31_VectorRegisterEnumValue)) -#endif // DONT_USE_REGISTER_DEFINES +inline constexpr VectorRegister as_VectorRegister(int encoding) { + assert(encoding == NOREG_ENCODING || + (encoding >= 0 && encoding < VectorRegister::number_of_registers), "bad vector register encoding"); + return VectorRegister(encoding); +} +// The Vector registers of z/Architecture. +constexpr VectorRegister vnoreg = as_VectorRegister(NOREG_ENCODING); + +constexpr VectorRegister Z_V0 = as_VectorRegister( 0); +constexpr VectorRegister Z_V1 = as_VectorRegister( 1); +constexpr VectorRegister Z_V2 = as_VectorRegister( 2); +constexpr VectorRegister Z_V3 = as_VectorRegister( 3); +constexpr VectorRegister Z_V4 = as_VectorRegister( 4); +constexpr VectorRegister Z_V5 = as_VectorRegister( 5); +constexpr VectorRegister Z_V6 = as_VectorRegister( 6); +constexpr VectorRegister Z_V7 = as_VectorRegister( 7); +constexpr VectorRegister Z_V8 = as_VectorRegister( 8); +constexpr VectorRegister Z_V9 = as_VectorRegister( 9); +constexpr VectorRegister Z_V10 = as_VectorRegister(10); +constexpr VectorRegister Z_V11 = as_VectorRegister(11); +constexpr VectorRegister Z_V12 = as_VectorRegister(12); +constexpr VectorRegister Z_V13 = as_VectorRegister(13); +constexpr VectorRegister Z_V14 = as_VectorRegister(14); +constexpr VectorRegister Z_V15 = as_VectorRegister(15); +constexpr VectorRegister Z_V16 = as_VectorRegister(16); +constexpr VectorRegister Z_V17 = as_VectorRegister(17); +constexpr VectorRegister Z_V18 = as_VectorRegister(18); +constexpr VectorRegister Z_V19 = as_VectorRegister(19); +constexpr VectorRegister Z_V20 = as_VectorRegister(20); +constexpr VectorRegister Z_V21 = as_VectorRegister(21); +constexpr VectorRegister Z_V22 = as_VectorRegister(22); +constexpr VectorRegister Z_V23 = as_VectorRegister(23); +constexpr VectorRegister Z_V24 = as_VectorRegister(24); +constexpr VectorRegister Z_V25 = as_VectorRegister(25); +constexpr VectorRegister Z_V26 = as_VectorRegister(26); +constexpr VectorRegister Z_V27 = as_VectorRegister(27); +constexpr VectorRegister Z_V28 = as_VectorRegister(28); +constexpr VectorRegister Z_V29 = as_VectorRegister(29); +constexpr VectorRegister Z_V30 = as_VectorRegister(30); +constexpr VectorRegister Z_V31 = as_VectorRegister(31); // Need to know the total number of registers of all sorts for SharedInfo. // Define a class that exports it. @@ -474,8 +384,8 @@ class ConcreteRegisterImpl : public AbstractRegisterImpl { public: enum { number_of_registers = - (RegisterImpl::number_of_registers + - FloatRegisterImpl::number_of_registers) + (Register::number_of_registers + + FloatRegister::number_of_registers) * 2 // register halves + 1 // condition code register }; @@ -485,98 +395,57 @@ class ConcreteRegisterImpl : public AbstractRegisterImpl { // Common register declarations used in assembler code. -REGISTER_DECLARATION(Register, Z_EXC_OOP, Z_R2); -REGISTER_DECLARATION(Register, Z_EXC_PC, Z_R3); -REGISTER_DECLARATION(Register, Z_RET, Z_R2); -REGISTER_DECLARATION(Register, Z_ARG1, Z_R2); -REGISTER_DECLARATION(Register, Z_ARG2, Z_R3); -REGISTER_DECLARATION(Register, Z_ARG3, Z_R4); -REGISTER_DECLARATION(Register, Z_ARG4, Z_R5); -REGISTER_DECLARATION(Register, Z_ARG5, Z_R6); -REGISTER_DECLARATION(Register, Z_SP, Z_R15); -REGISTER_DECLARATION(FloatRegister, Z_FRET, Z_F0); -REGISTER_DECLARATION(FloatRegister, Z_FARG1, Z_F0); -REGISTER_DECLARATION(FloatRegister, Z_FARG2, Z_F2); -REGISTER_DECLARATION(FloatRegister, Z_FARG3, Z_F4); -REGISTER_DECLARATION(FloatRegister, Z_FARG4, Z_F6); - -#ifndef DONT_USE_REGISTER_DEFINES -#define Z_EXC_OOP AS_REGISTER(Register, Z_R2) -#define Z_EXC_PC AS_REGISTER(Register, Z_R3) -#define Z_RET AS_REGISTER(Register, Z_R2) -#define Z_ARG1 AS_REGISTER(Register, Z_R2) -#define Z_ARG2 AS_REGISTER(Register, Z_R3) -#define Z_ARG3 AS_REGISTER(Register, Z_R4) -#define Z_ARG4 AS_REGISTER(Register, Z_R5) -#define Z_ARG5 AS_REGISTER(Register, Z_R6) -#define Z_SP AS_REGISTER(Register, Z_R15) -#define Z_FRET AS_REGISTER(FloatRegister, Z_F0) -#define Z_FARG1 AS_REGISTER(FloatRegister, Z_F0) -#define Z_FARG2 AS_REGISTER(FloatRegister, Z_F2) -#define Z_FARG3 AS_REGISTER(FloatRegister, Z_F4) -#define Z_FARG4 AS_REGISTER(FloatRegister, Z_F6) -#endif +constexpr Register Z_EXC_OOP = Z_R2; +constexpr Register Z_EXC_PC = Z_R3; +constexpr Register Z_RET = Z_R2; +constexpr Register Z_ARG1 = Z_R2; +constexpr Register Z_ARG2 = Z_R3; +constexpr Register Z_ARG3 = Z_R4; +constexpr Register Z_ARG4 = Z_R5; +constexpr Register Z_ARG5 = Z_R6; +constexpr Register Z_SP = Z_R15; +constexpr FloatRegister Z_FRET = Z_F0; +constexpr FloatRegister Z_FARG1 = Z_F0; +constexpr FloatRegister Z_FARG2 = Z_F2; +constexpr FloatRegister Z_FARG3 = Z_F4; +constexpr FloatRegister Z_FARG4 = Z_F6; // Register declarations to be used in frame manager assembly code. // Use only non-volatile registers in order to keep values across C-calls. // Register to cache the integer value on top of the operand stack. -REGISTER_DECLARATION(Register, Z_tos, Z_R2); +constexpr Register Z_tos = Z_R2; // Register to cache the fp value on top of the operand stack. -REGISTER_DECLARATION(FloatRegister, Z_ftos, Z_F0); +constexpr FloatRegister Z_ftos = Z_F0; // Expression stack pointer in interpreted java frame. -REGISTER_DECLARATION(Register, Z_esp, Z_R7); +constexpr Register Z_esp = Z_R7; // Address of current thread. -REGISTER_DECLARATION(Register, Z_thread, Z_R8); +constexpr Register Z_thread = Z_R8; // Address of current method. only valid in interpreter_entry. -REGISTER_DECLARATION(Register, Z_method, Z_R9); +constexpr Register Z_method = Z_R9; // Inline cache register. used by c1 and c2. -REGISTER_DECLARATION(Register, Z_inline_cache,Z_R9); +constexpr Register Z_inline_cache = Z_R9; // Frame pointer of current interpreter frame. only valid while // executing bytecodes. -REGISTER_DECLARATION(Register, Z_fp, Z_R9); +constexpr Register Z_fp = Z_R9; // Address of the locals array in an interpreted java frame. -REGISTER_DECLARATION(Register, Z_locals, Z_R12); +constexpr Register Z_locals = Z_R12; // Bytecode pointer. -REGISTER_DECLARATION(Register, Z_bcp, Z_R13); +constexpr Register Z_bcp = Z_R13; // Bytecode which is dispatched (short lived!). -REGISTER_DECLARATION(Register, Z_bytecode, Z_R14); -#ifndef DONT_USE_REGISTER_DEFINES -#define Z_tos AS_REGISTER(Register, Z_R2) -#define Z_ftos AS_REGISTER(FloatRegister, Z_F0) -#define Z_esp AS_REGISTER(Register, Z_R7) -#define Z_thread AS_REGISTER(Register, Z_R8) -#define Z_method AS_REGISTER(Register, Z_R9) -#define Z_inline_cache AS_REGISTER(Register, Z_R9) -#define Z_fp AS_REGISTER(Register, Z_R9) -#define Z_locals AS_REGISTER(Register, Z_R12) -#define Z_bcp AS_REGISTER(Register, Z_R13) -#define Z_bytecode AS_REGISTER(Register, Z_R14) -#endif +constexpr Register Z_bytecode = Z_R14; // Temporary registers to be used within frame manager. We can use -// the nonvolatiles because the call stub has saved them. +// the nonvolatile ones because the call stub has saved them. // Use only non-volatile registers in order to keep values across C-calls. -REGISTER_DECLARATION(Register, Z_tmp_1, Z_R10); -REGISTER_DECLARATION(Register, Z_tmp_2, Z_R11); -REGISTER_DECLARATION(Register, Z_tmp_3, Z_R12); -REGISTER_DECLARATION(Register, Z_tmp_4, Z_R13); -#ifndef DONT_USE_REGISTER_DEFINES -#define Z_tmp_1 AS_REGISTER(Register, Z_R10) -#define Z_tmp_2 AS_REGISTER(Register, Z_R11) -#define Z_tmp_3 AS_REGISTER(Register, Z_R12) -#define Z_tmp_4 AS_REGISTER(Register, Z_R13) -#endif +constexpr Register Z_tmp_1 = Z_R10; +constexpr Register Z_tmp_2 = Z_R11; +constexpr Register Z_tmp_3 = Z_R12; +constexpr Register Z_tmp_4 = Z_R13; // Scratch registers are volatile. -REGISTER_DECLARATION(Register, Z_R0_scratch, Z_R0); -REGISTER_DECLARATION(Register, Z_R1_scratch, Z_R1); -REGISTER_DECLARATION(FloatRegister, Z_fscratch_1, Z_F1); -#ifndef DONT_USE_REGISTER_DEFINES -#define Z_R0_scratch AS_REGISTER(Register, Z_R0) -#define Z_R1_scratch AS_REGISTER(Register, Z_R1) -#define Z_fscratch_1 AS_REGISTER(FloatRegister, Z_F1) -#endif - +constexpr Register Z_R0_scratch = Z_R0; +constexpr Register Z_R1_scratch = Z_R1; +constexpr FloatRegister Z_fscratch_1 = Z_F1; #endif // CPU_S390_REGISTER_S390_HPP diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 0abe0ce4875e3..bb890b7ef7883 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1,6 +1,6 @@ // // Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -// Copyright (c) 2017, 2020 SAP SE. All rights reserved. +// Copyright (c) 2017, 2024 SAP SE. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -1466,6 +1466,7 @@ int HandlerImpl::emit_exception_handler(CodeBuffer &cbuf) { address base = __ start_a_stub(size_exception_handler()); if (base == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); return 0; // CodeBuffer::expand failed } @@ -1487,6 +1488,7 @@ int HandlerImpl::emit_deopt_handler(CodeBuffer& cbuf) { address base = __ start_a_stub(size_deopt_handler()); if (base == NULL) { + ciEnv::current()->record_failure("CodeCache is full"); return 0; // CodeBuffer::expand failed } diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 1c6c1713c77a8..ad96622e137c4 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -630,7 +630,7 @@ void SharedRuntime::restore_native_result(MacroAssembler *masm, // as framesizes are fixed. // VMRegImpl::stack0 refers to the first slot 0(sp). // VMRegImpl::stack0+1 refers to the memory word 4-byes higher. Registers -// up to RegisterImpl::number_of_registers are the 64-bit integer registers. +// up to Register::number_of_registers are the 64-bit integer registers. // Note: the INPUTS in sig_bt are in units of Java argument words, which are // either 32-bit or 64-bit depending on the build. The OUTPUTS are in 32-bit @@ -666,8 +666,8 @@ int SharedRuntime::java_calling_convention(const BasicType *sig_bt, const int z_num_iarg_registers = sizeof(z_iarg_reg) / sizeof(z_iarg_reg[0]); const int z_num_farg_registers = sizeof(z_farg_reg) / sizeof(z_farg_reg[0]); - assert(RegisterImpl::number_of_arg_registers == z_num_iarg_registers, "iarg reg count mismatch"); - assert(FloatRegisterImpl::number_of_arg_registers == z_num_farg_registers, "farg reg count mismatch"); + assert(Register::number_of_arg_registers == z_num_iarg_registers, "iarg reg count mismatch"); + assert(FloatRegister::number_of_arg_registers == z_num_farg_registers, "farg reg count mismatch"); int i; int stk = 0; @@ -780,8 +780,8 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, const int z_num_farg_registers = sizeof(z_farg_reg) / sizeof(z_farg_reg[0]); // Check calling conventions consistency. - assert(RegisterImpl::number_of_arg_registers == z_num_iarg_registers, "iarg reg count mismatch"); - assert(FloatRegisterImpl::number_of_arg_registers == z_num_farg_registers, "farg reg count mismatch"); + assert(Register::number_of_arg_registers == z_num_iarg_registers, "iarg reg count mismatch"); + assert(FloatRegister::number_of_arg_registers == z_num_farg_registers, "farg reg count mismatch"); // Avoid passing C arguments in the wrong stack slots. @@ -1578,7 +1578,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, SharedRuntime::out_preserve_stack_slots(); // see c_calling_convention // Now the space for the inbound oop handle area. - int total_save_slots = RegisterImpl::number_of_arg_registers * VMRegImpl::slots_per_word; + int total_save_slots = Register::number_of_arg_registers * VMRegImpl::slots_per_word; if (is_critical_native) { // Critical natives may have to call out so they need a save area // for register arguments. @@ -1739,12 +1739,12 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, //-------------------------------------------------------------------- #ifdef ASSERT - bool reg_destroyed[RegisterImpl::number_of_registers]; - bool freg_destroyed[FloatRegisterImpl::number_of_registers]; - for (int r = 0; r < RegisterImpl::number_of_registers; r++) { + bool reg_destroyed[Register::number_of_registers]; + bool freg_destroyed[FloatRegister::number_of_registers]; + for (int r = 0; r < Register::number_of_registers; r++) { reg_destroyed[r] = false; } - for (int f = 0; f < FloatRegisterImpl::number_of_registers; f++) { + for (int f = 0; f < FloatRegister::number_of_registers; f++) { freg_destroyed[f] = false; } #endif // ASSERT diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index d7b68f907c7e3..cdd8097e98ab6 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2021 SAP SE. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,8 +82,8 @@ static const char* z_features[] = {" ", "system-z, g5-z196, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update", "system-z, g6-ec12, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm", "system-z, g7-z13, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr", - "system-z, g8-z14, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1)", - "system-z, g9-z15, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1, instrext3, VEnh2 )" + "system-z, g8-z14, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1", + "system-z, g9-z15, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1, instrext3, venh2" }; void VM_Version::initialize() { @@ -366,7 +366,7 @@ void VM_Version::set_features_string() { strcpy(buf, "z/Architecture (unknown generation)"); } else if (model_ix > 0) { _model_string = z_name[model_ix]; - jio_snprintf(buf, sizeof(buf), "%s, out-of-support_as_of_", z_features[model_ix], z_EOS[model_ix]); + jio_snprintf(buf, sizeof(buf), "%s, out-of-support_as_of_%s", z_features[model_ix], z_EOS[model_ix]); } else if (model_ix < 0) { tty->print_cr("*** WARNING *** Ambiguous z/Architecture detection!"); tty->print_cr(" oldest detected generation is %s", z_features[-model_ix]); diff --git a/src/hotspot/cpu/s390/vmreg_s390.inline.hpp b/src/hotspot/cpu/s390/vmreg_s390.inline.hpp index a775c8f971a5f..593a0d480454e 100644 --- a/src/hotspot/cpu/s390/vmreg_s390.inline.hpp +++ b/src/hotspot/cpu/s390/vmreg_s390.inline.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016 SAP SE. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,18 +26,15 @@ #ifndef CPU_S390_VMREG_S390_INLINE_HPP #define CPU_S390_VMREG_S390_INLINE_HPP -inline VMReg RegisterImpl::as_VMReg() { - if (this == noreg) { - return VMRegImpl::Bad(); - } +inline VMReg Register::as_VMReg() const { return VMRegImpl::as_VMReg(encoding() << 1); } -inline VMReg FloatRegisterImpl::as_VMReg() { +inline VMReg FloatRegister::as_VMReg() const { return VMRegImpl::as_VMReg((encoding() << 1) + ConcreteRegisterImpl::max_gpr); } -inline VMReg ConditionRegisterImpl::as_VMReg() { +inline VMReg ConditionRegister::as_VMReg() const { return VMRegImpl::as_VMReg((encoding() << 1) + ConcreteRegisterImpl::max_fpr); } diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index b05646ac20cc0..d3fad2b6d237f 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -3325,7 +3325,7 @@ void Assembler::negl(Address dst) { emit_operand(as_Register(3), dst); } -void Assembler::nop(int i) { +void Assembler::nop(uint i) { #ifdef ASSERT assert(i > 0, " "); // The fancy nops aren't currently recognized by debuggers making it a @@ -5811,6 +5811,14 @@ void Assembler::xorw(Register dst, Register src) { emit_arith(0x33, 0xC0, dst, src); } +void Assembler::xorw(Register dst, Address src) { + InstructionMark im(this); + emit_int8(0x66); + prefix(src, dst); + emit_int8(0x33); + emit_operand(dst, src, 0); +} + // AVX 3-operands scalar float-point arithmetic instructions void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, Address src) { @@ -10513,17 +10521,6 @@ void Assembler::movsbq(Register dst, Register src) { emit_int24(0x0F, (unsigned char)0xBE, (0xC0 | encode)); } -void Assembler::movslq(Register dst, int32_t imm32) { - // dbx shows movslq(rcx, 3) as movq $0x0000000049000000,(%rbx) - // and movslq(r8, 3); as movl $0x0000000048000000,(%rbx) - // as a result we shouldn't use until tested at runtime... - ShouldNotReachHere(); - InstructionMark im(this); - int encode = prefixq_and_encode(dst->encoding()); - emit_int8(0xC7 | encode); - emit_int32(imm32); -} - void Assembler::movslq(Address dst, int32_t imm32) { assert(is_simm32(imm32), "lost bits"); InstructionMark im(this); diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index e8a61efe9928d..20789b8db9252 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1584,7 +1584,6 @@ class Assembler : public AbstractAssembler { // Move signed 32bit immediate to 64bit extending sign void movslq(Address dst, int32_t imm64); - void movslq(Register dst, int32_t imm64); void movslq(Register dst, Address src); void movslq(Register dst, Register src); @@ -1645,7 +1644,7 @@ class Assembler : public AbstractAssembler { void negq(Address dst); #endif - void nop(int i = 1); + void nop(uint i = 1); void notl(Register dst); @@ -2116,6 +2115,7 @@ class Assembler : public AbstractAssembler { void xorb(Address dst, Register src); void xorb(Register dst, Address src); void xorw(Register dst, Register src); + void xorw(Register dst, Address src); void xorq(Register dst, Address src); void xorq(Address dst, int32_t imm32); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index da4a40ba3f905..ff10ce38eeab4 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -1175,20 +1175,20 @@ void MacroAssembler::addpd(XMMRegister dst, AddressLiteral src) { // Stub code is generated once and never copied. // NMethods can't use this because they get copied and we can't force alignment > 32 bytes. void MacroAssembler::align64() { - align(64, (unsigned long long) pc()); + align(64, (uint)(uintptr_t)pc()); } void MacroAssembler::align32() { - align(32, (unsigned long long) pc()); + align(32, (uint)(uintptr_t)pc()); } -void MacroAssembler::align(int modulus) { +void MacroAssembler::align(uint modulus) { // 8273459: Ensure alignment is possible with current segment alignment - assert(modulus <= CodeEntryAlignment, "Alignment must be <= CodeEntryAlignment"); + assert(modulus <= (uintx)CodeEntryAlignment, "Alignment must be <= CodeEntryAlignment"); align(modulus, offset()); } -void MacroAssembler::align(int modulus, int target) { +void MacroAssembler::align(uint modulus, uint target) { if (target % modulus != 0) { nop(modulus - (target % modulus)); } @@ -1516,7 +1516,12 @@ void MacroAssembler::call(AddressLiteral entry) { void MacroAssembler::ic_call(address entry, jint method_index) { RelocationHolder rh = virtual_call_Relocation::spec(pc(), method_index); +#ifdef _LP64 + // Needs full 64-bit immediate for later patching. + mov64(rax, (intptr_t)Universe::non_oop_word()); +#else movptr(rax, (intptr_t)Universe::non_oop_word()); +#endif call(AddressLiteral(entry, rh)); } @@ -2685,7 +2690,15 @@ void MacroAssembler::movptr(Register dst, Address src) { // src should NEVER be a real pointer. Use AddressLiteral for true pointers void MacroAssembler::movptr(Register dst, intptr_t src) { - LP64_ONLY(mov64(dst, src)) NOT_LP64(movl(dst, src)); +#ifdef _LP64 + if (is_simm32(src)) { + movq(dst, checked_cast(src)); + } else { + mov64(dst, src); + } +#else + movl(dst, src); +#endif } void MacroAssembler::movptr(Address dst, Register src) { @@ -3902,6 +3915,125 @@ void MacroAssembler::lookup_interface_method(Register recv_klass, } } +// Look up the method for a megamorphic invokeinterface call in a single pass over itable: +// - check recv_klass (actual object class) is a subtype of resolved_klass from CompiledICHolder +// - find a holder_klass (class that implements the method) vtable offset and get the method from vtable by index +// The target method is determined by . +// The receiver klass is in recv_klass. +// On success, the result will be in method_result, and execution falls through. +// On failure, execution transfers to the given label. +void MacroAssembler::lookup_interface_method_stub(Register recv_klass, + Register holder_klass, + Register resolved_klass, + Register method_result, + Register scan_temp, + Register temp_reg2, + Register receiver, + int itable_index, + Label& L_no_such_interface) { + assert_different_registers(recv_klass, method_result, holder_klass, resolved_klass, scan_temp, temp_reg2, receiver); + Register temp_itbl_klass = method_result; + Register temp_reg = (temp_reg2 == noreg ? recv_klass : temp_reg2); // reuse recv_klass register on 32-bit x86 impl + + int vtable_base = in_bytes(Klass::vtable_start_offset()); + int itentry_off = itableMethodEntry::method_offset_in_bytes(); + int scan_step = itableOffsetEntry::size() * wordSize; + int vte_size = vtableEntry::size_in_bytes(); + int ioffset = itableOffsetEntry::interface_offset_in_bytes(); + int ooffset = itableOffsetEntry::offset_offset_in_bytes(); + Address::ScaleFactor times_vte_scale = Address::times_ptr; + assert(vte_size == wordSize, "adjust times_vte_scale"); + + Label L_loop_scan_resolved_entry, L_resolved_found, L_holder_found; + + // temp_itbl_klass = recv_klass.itable[0] + // scan_temp = &recv_klass.itable[0] + step + movl(scan_temp, Address(recv_klass, Klass::vtable_length_offset())); + movptr(temp_itbl_klass, Address(recv_klass, scan_temp, times_vte_scale, vtable_base + ioffset)); + lea(scan_temp, Address(recv_klass, scan_temp, times_vte_scale, vtable_base + ioffset + scan_step)); + xorptr(temp_reg, temp_reg); + + // Initial checks: + // - if (holder_klass != resolved_klass), go to "scan for resolved" + // - if (itable[0] == 0), no such interface + // - if (itable[0] == holder_klass), shortcut to "holder found" + cmpptr(holder_klass, resolved_klass); + jccb(Assembler::notEqual, L_loop_scan_resolved_entry); + testptr(temp_itbl_klass, temp_itbl_klass); + jccb(Assembler::zero, L_no_such_interface); + cmpptr(holder_klass, temp_itbl_klass); + jccb(Assembler::equal, L_holder_found); + + // Loop: Look for holder_klass record in itable + // do { + // tmp = itable[index]; + // index += step; + // if (tmp == holder_klass) { + // goto L_holder_found; // Found! + // } + // } while (tmp != 0); + // goto L_no_such_interface // Not found. + Label L_scan_holder; + bind(L_scan_holder); + movptr(temp_itbl_klass, Address(scan_temp, 0)); + addptr(scan_temp, scan_step); + cmpptr(holder_klass, temp_itbl_klass); + jccb(Assembler::equal, L_holder_found); + testptr(temp_itbl_klass, temp_itbl_klass); + jccb(Assembler::notZero, L_scan_holder); + + jmpb(L_no_such_interface); + + // Loop: Look for resolved_class record in itable + // do { + // tmp = itable[index]; + // index += step; + // if (tmp == holder_klass) { + // // Also check if we have met a holder klass + // holder_tmp = itable[index-step-ioffset]; + // } + // if (tmp == resolved_klass) { + // goto L_resolved_found; // Found! + // } + // } while (tmp != 0); + // goto L_no_such_interface // Not found. + // + Label L_loop_scan_resolved; + bind(L_loop_scan_resolved); + movptr(temp_itbl_klass, Address(scan_temp, 0)); + addptr(scan_temp, scan_step); + bind(L_loop_scan_resolved_entry); + cmpptr(holder_klass, temp_itbl_klass); + cmovl(Assembler::equal, temp_reg, Address(scan_temp, ooffset - ioffset - scan_step)); + cmpptr(resolved_klass, temp_itbl_klass); + jccb(Assembler::equal, L_resolved_found); + testptr(temp_itbl_klass, temp_itbl_klass); + jccb(Assembler::notZero, L_loop_scan_resolved); + + jmpb(L_no_such_interface); + + Label L_ready; + + // See if we already have a holder klass. If not, go and scan for it. + bind(L_resolved_found); + testptr(temp_reg, temp_reg); + jccb(Assembler::zero, L_scan_holder); + jmpb(L_ready); + + bind(L_holder_found); + movl(temp_reg, Address(scan_temp, ooffset - ioffset - scan_step)); + + // Finally, temp_reg contains holder_klass vtable offset + bind(L_ready); + assert(itableMethodEntry::size() * wordSize == wordSize, "adjust the scaling in the code below"); + if (temp_reg2 == noreg) { // recv_klass register is clobbered for 32-bit x86 impl + load_klass(scan_temp, receiver, noreg); + movptr(method_result, Address(scan_temp, temp_reg, Address::times_1, itable_index * wordSize + itentry_off)); + } else { + movptr(method_result, Address(recv_klass, temp_reg, Address::times_1, itable_index * wordSize + itentry_off)); + } +} + // virtual method calling void MacroAssembler::lookup_virtual_method(Register recv_klass, diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 88c2b0c664186..c8829759410ff 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -196,8 +196,8 @@ class MacroAssembler: public Assembler { // Alignment void align32(); void align64(); - void align(int modulus); - void align(int modulus, int target); + void align(uint modulus); + void align(uint modulus, uint target); // A 5 byte nop that is safe for patching (see patch_verified_entry) void fat_nop(); @@ -556,6 +556,16 @@ class MacroAssembler: public Assembler { Label& no_such_interface, bool return_method = true); + void lookup_interface_method_stub(Register recv_klass, + Register holder_klass, + Register resolved_klass, + Register method_result, + Register scan_temp, + Register temp_reg2, + Register receiver, + int itable_index, + Label& L_no_such_interface); + // virtual method calling void lookup_virtual_method(Register recv_klass, RegisterOrConstant vtable_index, diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp index dcc48a6c1f03b..78372e1408c2b 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp @@ -798,6 +798,7 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis const Register rounds = 0; const Register pos = r12; + const Register tail = r15; Label PRELOOP_START, EXIT_PRELOOP, REMAINDER, REMAINDER_16, LOOP, END, EXIT, END_LOOP, AES192, AES256, AES192_REMAINDER16, REMAINDER16_END_LOOP, AES256_REMAINDER16, @@ -1228,29 +1229,36 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis // Save encrypted counter value in xmm0 for next invocation, before XOR operation movdqu(Address(saved_encCounter_start, 0), xmm0); // XOR encryted block cipher in xmm0 with PT to produce CT - evpxorq(xmm0, xmm0, Address(src_addr, pos, Address::times_1, 0), Assembler::AVX_128bit); // extract upto 15 bytes of CT from xmm0 as specified by length register testptr(len_reg, 8); jcc(Assembler::zero, EXTRACT_TAIL_4BYTES); - pextrq(Address(dest_addr, pos), xmm0, 0); + pextrq(tail, xmm0, 0); + xorq(tail, Address(src_addr, pos, Address::times_1, 0)); + movq(Address(dest_addr, pos), tail); psrldq(xmm0, 8); addl(pos, 8); bind(EXTRACT_TAIL_4BYTES); testptr(len_reg, 4); jcc(Assembler::zero, EXTRACT_TAIL_2BYTES); - pextrd(Address(dest_addr, pos), xmm0, 0); + pextrd(tail, xmm0, 0); + xorl(tail, Address(src_addr, pos, Address::times_1, 0)); + movl(Address(dest_addr, pos), tail); psrldq(xmm0, 4); addq(pos, 4); bind(EXTRACT_TAIL_2BYTES); testptr(len_reg, 2); jcc(Assembler::zero, EXTRACT_TAIL_1BYTE); - pextrw(Address(dest_addr, pos), xmm0, 0); + pextrw(tail, xmm0, 0); + xorw(tail, Address(src_addr, pos, Address::times_1, 0)); + movw(Address(dest_addr, pos), tail); psrldq(xmm0, 2); addl(pos, 2); bind(EXTRACT_TAIL_1BYTE); testptr(len_reg, 1); jcc(Assembler::zero, END); - pextrb(Address(dest_addr, pos), xmm0, 0); + pextrb(tail, xmm0, 0); + xorb(tail, Address(src_addr, pos, Address::times_1, 0)); + movb(Address(dest_addr, pos), tail); addl(pos, 1); bind(END); diff --git a/src/hotspot/cpu/x86/vtableStubs_x86_32.cpp b/src/hotspot/cpu/x86/vtableStubs_x86_32.cpp index 09322205f06e2..3003226046947 100644 --- a/src/hotspot/cpu/x86/vtableStubs_x86_32.cpp +++ b/src/hotspot/cpu/x86/vtableStubs_x86_32.cpp @@ -179,14 +179,16 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { // rax: CompiledICHolder // rcx: Receiver - // Most registers are in use; we'll use rax, rbx, rsi, rdi + // Most registers are in use; we'll use rax, rbx, rcx, rdx, rsi, rdi // (If we need to make rsi, rdi callee-save, do a push/pop here.) const Register recv_klass_reg = rsi; const Register holder_klass_reg = rax; // declaring interface klass (DECC) - const Register resolved_klass_reg = rbx; // resolved interface klass (REFC) - const Register temp_reg = rdi; + const Register resolved_klass_reg = rdi; // resolved interface klass (REFC) + const Register temp_reg = rdx; + const Register method = rbx; + const Register icholder_reg = rax; + const Register receiver = rcx; - const Register icholder_reg = rax; __ movptr(resolved_klass_reg, Address(icholder_reg, CompiledICHolder::holder_klass_offset())); __ movptr(holder_klass_reg, Address(icholder_reg, CompiledICHolder::holder_metadata_offset())); @@ -198,35 +200,26 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { __ load_klass(recv_klass_reg, rcx, noreg); start_pc = __ pc(); + __ push(rdx); // temp_reg // Receiver subtype check against REFC. - // Destroys recv_klass_reg value. - __ lookup_interface_method(// inputs: rec. class, interface - recv_klass_reg, resolved_klass_reg, noreg, - // outputs: scan temp. reg1, scan temp. reg2 - recv_klass_reg, temp_reg, - L_no_such_interface, - /*return_method=*/false); - - const ptrdiff_t typecheckSize = __ pc() - start_pc; - start_pc = __ pc(); - // Get selected method from declaring class and itable index - const Register method = rbx; - __ load_klass(recv_klass_reg, rcx, noreg); // restore recv_klass_reg - __ lookup_interface_method(// inputs: rec. class, interface, itable index - recv_klass_reg, holder_klass_reg, itable_index, - // outputs: method, scan temp. reg - method, temp_reg, - L_no_such_interface); - + __ lookup_interface_method_stub(recv_klass_reg, // input + holder_klass_reg, // input + resolved_klass_reg, // input + method, // output + temp_reg, + noreg, + receiver, // input (x86_32 only: to restore recv_klass value) + itable_index, + L_no_such_interface); const ptrdiff_t lookupSize = __ pc() - start_pc; // We expect we need index_dependent_slop extra bytes. Reason: // The emitted code in lookup_interface_method changes when itable_index exceeds 31. // For windows, a narrow estimate was found to be 104. Other OSes not tested. const ptrdiff_t estimate = 104; - const ptrdiff_t codesize = typecheckSize + lookupSize + index_dependent_slop; + const ptrdiff_t codesize = lookupSize + index_dependent_slop; slop_delta = (int)(estimate - codesize); slop_bytes += slop_delta; assert(slop_delta >= 0, "itable #%d: Code size estimate (%d) for lookup_interface_method too small, required: %d", itable_index, (int)estimate, (int)codesize); @@ -246,6 +239,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { } #endif // ASSERT + __ pop(rdx); address ame_addr = __ pc(); __ jmp(Address(method, Method::from_compiled_offset())); @@ -255,6 +249,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { // We force resolving of the call site by jumping to the "handle // wrong method" stub, and so let the interpreter runtime do all the // dirty work. + __ pop(rdx); __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); masm->flush(); diff --git a/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp b/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp index c6181f2d007ed..0d17756a30dc5 100644 --- a/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp +++ b/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp @@ -176,10 +176,12 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { // (various calling sequences use r[cd]x, r[sd]i, r[89]; stay away from them) const Register recv_klass_reg = r10; const Register holder_klass_reg = rax; // declaring interface klass (DECC) - const Register resolved_klass_reg = rbx; // resolved interface klass (REFC) + const Register resolved_klass_reg = r14; // resolved interface klass (REFC) const Register temp_reg = r11; + const Register temp_reg2 = r13; + const Register method = rbx; + const Register icholder_reg = rax; - const Register icholder_reg = rax; __ movptr(resolved_klass_reg, Address(icholder_reg, CompiledICHolder::holder_klass_offset())); __ movptr(holder_klass_reg, Address(icholder_reg, CompiledICHolder::holder_metadata_offset())); @@ -193,25 +195,16 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { start_pc = __ pc(); // Receiver subtype check against REFC. - // Destroys recv_klass_reg value. - __ lookup_interface_method(// inputs: rec. class, interface - recv_klass_reg, resolved_klass_reg, noreg, - // outputs: scan temp. reg1, scan temp. reg2 - recv_klass_reg, temp_reg, - L_no_such_interface, - /*return_method=*/false); - - const ptrdiff_t typecheckSize = __ pc() - start_pc; - start_pc = __ pc(); - // Get selected method from declaring class and itable index - const Register method = rbx; - __ load_klass(recv_klass_reg, j_rarg0, temp_reg); // restore recv_klass_reg - __ lookup_interface_method(// inputs: rec. class, interface, itable index - recv_klass_reg, holder_klass_reg, itable_index, - // outputs: method, scan temp. reg - method, temp_reg, - L_no_such_interface); + __ lookup_interface_method_stub(recv_klass_reg, // input + holder_klass_reg, // input + resolved_klass_reg, // input + method, // output + temp_reg, + temp_reg2, + noreg, + itable_index, + L_no_such_interface); const ptrdiff_t lookupSize = __ pc() - start_pc; @@ -219,7 +212,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { // The emitted code in lookup_interface_method changes when itable_index exceeds 15. // For linux, a very narrow estimate would be 112, but Solaris requires some more space (130). const ptrdiff_t estimate = 136; - const ptrdiff_t codesize = typecheckSize + lookupSize + index_dependent_slop; + const ptrdiff_t codesize = lookupSize + index_dependent_slop; slop_delta = (int)(estimate - codesize); slop_bytes += slop_delta; assert(slop_delta >= 0, "itable #%d: Code size estimate (%d) for lookup_interface_method too small, required: %d", itable_index, (int)estimate, (int)codesize); diff --git a/src/hotspot/os/aix/attachListener_aix.cpp b/src/hotspot/os/aix/attachListener_aix.cpp index 461b7fc874f46..fbc77f873eb3a 100644 --- a/src/hotspot/os/aix/attachListener_aix.cpp +++ b/src/hotspot/os/aix/attachListener_aix.cpp @@ -266,7 +266,7 @@ int AixAttachListener::init() { // AixAttachOperation* AixAttachListener::read_request(int s) { char ver_str[8]; - sprintf(ver_str, "%d", ATTACH_PROTOCOL_VER); + os::snprintf_checked(ver_str, sizeof(ver_str), "%d", ATTACH_PROTOCOL_VER); // The request is a sequence of strings so we first figure out the // expected count and the maximum possible length of the request. @@ -311,7 +311,7 @@ AixAttachOperation* AixAttachListener::read_request(int s) { if ((strlen(buf) != strlen(ver_str)) || (atoi(buf) != ATTACH_PROTOCOL_VER)) { char msg[32]; - sprintf(msg, "%d\n", ATTACH_ERROR_BADVERSION); + os::snprintf_checked(msg, sizeof(msg), "%d\n", ATTACH_ERROR_BADVERSION); write_fully(s, msg, strlen(msg)); return NULL; } @@ -441,7 +441,7 @@ void AixAttachOperation::complete(jint result, bufferedStream* st) { // write operation result char msg[32]; - sprintf(msg, "%d\n", result); + os::snprintf_checked(msg, sizeof(msg), "%d\n", result); int rc = AixAttachListener::write_fully(this->socket(), msg, strlen(msg)); // write any result data @@ -544,7 +544,7 @@ bool AttachListener::is_init_trigger() { char fn[PATH_MAX + 1]; int ret; struct stat64 st; - sprintf(fn, ".attach_pid%d", os::current_process_id()); + os::snprintf_checked(fn, sizeof(fn), ".attach_pid%d", os::current_process_id()); RESTARTABLE(::stat64(fn, &st), ret); if (ret == -1) { log_trace(attach)("Failed to find attach file: %s, trying alternate", fn); diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 32e014059da0c..3744f49a65e1e 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -536,7 +536,7 @@ void os::init_system_properties_values() { #endif #define EXTENSIONS_DIR "/lib/ext" - // Buffer that fits several sprintfs. + // Buffer that fits several snprintfs. // Note that the space for the trailing null is provided // by the nulls included by the sizeof operator. const size_t bufsize = @@ -584,13 +584,14 @@ void os::init_system_properties_values() { // Concatenate user and invariant part of ld_library_path. // That's +1 for the colon and +1 for the trailing '\0'. - char *ld_library_path = NEW_C_HEAP_ARRAY(char, strlen(v) + 1 + sizeof(DEFAULT_LIBPATH) + 1, mtInternal); - sprintf(ld_library_path, "%s%s" DEFAULT_LIBPATH, v, v_colon); + size_t pathsize = strlen(v) + 1 + sizeof(DEFAULT_LIBPATH) + 1; + char *ld_library_path = NEW_C_HEAP_ARRAY(char, pathsize, mtInternal); + os::snprintf_checked(ld_library_path, pathsize, "%s%s" DEFAULT_LIBPATH, v, v_colon); Arguments::set_library_path(ld_library_path); FREE_C_HEAP_ARRAY(char, ld_library_path); // Extensions directories. - sprintf(buf, "%s" EXTENSIONS_DIR, Arguments::get_java_home()); + os::snprintf_checked(buf, bufsize, "%s" EXTENSIONS_DIR, Arguments::get_java_home()); Arguments::set_ext_dirs(buf); FREE_C_HEAP_ARRAY(char, buf); @@ -2068,7 +2069,7 @@ static bool checked_mprotect(char* addr, size_t size, int prot) { // // See http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/mprotect.htm - Events::log(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(addr), p2i(addr+size), prot); + Events::log_memprotect(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(addr), p2i(addr+size), prot); bool rc = ::mprotect(addr, size, prot) == 0 ? true : false; if (!rc) { @@ -2107,7 +2108,7 @@ static bool checked_mprotect(char* addr, size_t size, int prot) { // A valid strategy is just to try again. This usually works. :-/ ::usleep(1000); - Events::log(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(addr), p2i(addr+size), prot); + Events::log_memprotect(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(addr), p2i(addr+size), prot); if (::mprotect(addr, size, prot) == 0) { const bool read_protected_2 = (SafeFetch32((int*)addr, 0x12345678) == 0x12345678 && diff --git a/src/hotspot/os/bsd/attachListener_bsd.cpp b/src/hotspot/os/bsd/attachListener_bsd.cpp index b8702c5aa763e..a79df5dbdd482 100644 --- a/src/hotspot/os/bsd/attachListener_bsd.cpp +++ b/src/hotspot/os/bsd/attachListener_bsd.cpp @@ -247,7 +247,7 @@ int BsdAttachListener::init() { // BsdAttachOperation* BsdAttachListener::read_request(int s) { char ver_str[8]; - sprintf(ver_str, "%d", ATTACH_PROTOCOL_VER); + size_t ver_str_len = os::snprintf_checked(ver_str, sizeof(ver_str), "%d", ATTACH_PROTOCOL_VER); // The request is a sequence of strings so we first figure out the // expected count and the maximum possible length of the request. @@ -287,11 +287,11 @@ BsdAttachOperation* BsdAttachListener::read_request(int s) { // The first string is so check it now to // check for protocol mis-match if (str_count == 1) { - if ((strlen(buf) != strlen(ver_str)) || + if ((strlen(buf) != ver_str_len) || (atoi(buf) != ATTACH_PROTOCOL_VER)) { char msg[32]; - sprintf(msg, "%d\n", ATTACH_ERROR_BADVERSION); - write_fully(s, msg, strlen(msg)); + int msg_len = os::snprintf_checked(msg, sizeof(msg), "%d\n", ATTACH_ERROR_BADVERSION); + write_fully(s, msg, msg_len); return NULL; } } @@ -410,8 +410,8 @@ void BsdAttachOperation::complete(jint result, bufferedStream* st) { // write operation result char msg[32]; - sprintf(msg, "%d\n", result); - int rc = BsdAttachListener::write_fully(this->socket(), msg, strlen(msg)); + int msg_len = os::snprintf_checked(msg, sizeof(msg), "%d\n", result); + int rc = BsdAttachListener::write_fully(this->socket(), msg, msg_len); // write any result data if (rc == 0) { diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 071045c6a6e90..073fde86d22c9 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -338,7 +338,7 @@ void os::init_system_properties_values() { #ifndef __APPLE__ - // Buffer that fits several sprintfs. + // Buffer that fits several snprintfs. // Note that the space for the colon and the trailing null are provided // by the nulls included by the sizeof operator. const size_t bufsize = @@ -394,17 +394,16 @@ void os::init_system_properties_values() { const char *v_colon = ":"; if (v == NULL) { v = ""; v_colon = ""; } // That's +1 for the colon and +1 for the trailing '\0'. - char *ld_library_path = NEW_C_HEAP_ARRAY(char, - strlen(v) + 1 + - sizeof(SYS_EXT_DIR) + sizeof("/lib/") + strlen(cpu_arch) + sizeof(DEFAULT_LIBPATH) + 1, - mtInternal); - sprintf(ld_library_path, "%s%s" SYS_EXT_DIR "/lib/%s:" DEFAULT_LIBPATH, v, v_colon, cpu_arch); + const size_t ld_library_path_size = strlen(v) + 1 + sizeof(SYS_EXT_DIR) + + sizeof("/lib/") + strlen(cpu_arch) + sizeof(DEFAULT_LIBPATH) + 1; + char *ld_library_path = NEW_C_HEAP_ARRAY(char, ld_library_path_size, mtInternal); + os::snprintf_checked(ld_library_path, ld_library_path_size, "%s%s" SYS_EXT_DIR "/lib/%s:" DEFAULT_LIBPATH, v, v_colon, cpu_arch); Arguments::set_library_path(ld_library_path); FREE_C_HEAP_ARRAY(char, ld_library_path); } // Extensions directories. - sprintf(buf, "%s" EXTENSIONS_DIR ":" SYS_EXT_DIR EXTENSIONS_DIR, Arguments::get_java_home()); + os::snprintf_checked(buf, bufsize, "%s" EXTENSIONS_DIR ":" SYS_EXT_DIR EXTENSIONS_DIR, Arguments::get_java_home()); Arguments::set_ext_dirs(buf); FREE_C_HEAP_ARRAY(char, buf); @@ -419,7 +418,7 @@ void os::init_system_properties_values() { size_t system_ext_size = strlen(user_home_dir) + sizeof(SYS_EXTENSIONS_DIR) + sizeof(SYS_EXTENSIONS_DIRS); - // Buffer that fits several sprintfs. + // Buffer that fits several snprintfs. // Note that the space for the colon and the trailing null are provided // by the nulls included by the sizeof operator. const size_t bufsize = @@ -489,11 +488,9 @@ void os::init_system_properties_values() { // could cause a change in behavior, but Apple's Java6 behavior // can be achieved by putting "." at the beginning of the // JAVA_LIBRARY_PATH environment variable. - char *ld_library_path = NEW_C_HEAP_ARRAY(char, - strlen(v) + 1 + strlen(l) + 1 + - system_ext_size + 3, - mtInternal); - sprintf(ld_library_path, "%s%s%s%s%s" SYS_EXTENSIONS_DIR ":" SYS_EXTENSIONS_DIRS ":.", + const size_t ld_library_path_size = strlen(v) + 1 + strlen(l) + 1 + system_ext_size + 3; + char *ld_library_path = NEW_C_HEAP_ARRAY(char, ld_library_path_size, mtInternal); + os::snprintf_checked(ld_library_path, ld_library_path_size, "%s%s%s%s%s" SYS_EXTENSIONS_DIR ":" SYS_EXTENSIONS_DIRS ":.", v, v_colon, l, l_colon, user_home_dir); Arguments::set_library_path(ld_library_path); FREE_C_HEAP_ARRAY(char, ld_library_path); @@ -504,7 +501,7 @@ void os::init_system_properties_values() { // Note that the space for the colon and the trailing null are provided // by the nulls included by the sizeof operator (so actually one byte more // than necessary is allocated). - sprintf(buf, "%s" SYS_EXTENSIONS_DIR ":%s" EXTENSIONS_DIR ":" SYS_EXTENSIONS_DIRS, + os::snprintf_checked(buf, bufsize, "%s" SYS_EXTENSIONS_DIR ":%s" EXTENSIONS_DIR ":" SYS_EXTENSIONS_DIRS, user_home_dir, Arguments::get_java_home()); Arguments::set_ext_dirs(buf); @@ -1609,7 +1606,7 @@ bool os::pd_commit_memory(char* addr, size_t size, bool exec) { int prot = exec ? PROT_READ|PROT_WRITE|PROT_EXEC : PROT_READ|PROT_WRITE; #if defined(__OpenBSD__) // XXX: Work-around mmap/MAP_FIXED bug temporarily on OpenBSD - Events::log(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(addr), p2i(addr+size), prot); + Events::log_memprotect(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(addr), p2i(addr+size), prot); if (::mprotect(addr, size, prot) == 0) { return true; } @@ -1711,7 +1708,7 @@ char *os::scan_pages(char *start, char* end, page_info* page_expected, page_info bool os::pd_uncommit_memory(char* addr, size_t size, bool exec) { #if defined(__OpenBSD__) // XXX: Work-around mmap/MAP_FIXED bug temporarily on OpenBSD - Events::log(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with PROT_NONE", p2i(addr), p2i(addr+size)); + Events::log_memprotect(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with PROT_NONE", p2i(addr), p2i(addr+size)); return ::mprotect(addr, size, PROT_NONE) == 0; #elif defined(__APPLE__) if (exec) { @@ -1781,7 +1778,7 @@ static bool bsd_mprotect(char* addr, size_t size, int prot) { assert(addr == bottom, "sanity check"); size = align_up(pointer_delta(addr, bottom, 1) + size, os::Bsd::page_size()); - Events::log(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(bottom), p2i(bottom+size), prot); + Events::log_memprotect(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(bottom), p2i(bottom+size), prot); return ::mprotect(bottom, size, prot) == 0; } @@ -2090,16 +2087,24 @@ jint os::init_2(void) { if (status != 0) { log_info(os)("os::init_2 getrlimit failed: %s", os::strerror(errno)); } else { - nbr_files.rlim_cur = nbr_files.rlim_max; + rlim_t rlim_original = nbr_files.rlim_cur; -#ifdef __APPLE__ - // Darwin returns RLIM_INFINITY for rlim_max, but fails with EINVAL if - // you attempt to use RLIM_INFINITY. As per setrlimit(2), OPEN_MAX must - // be used instead - nbr_files.rlim_cur = MIN(OPEN_MAX, nbr_files.rlim_cur); -#endif + // On macOS according to setrlimit(2), OPEN_MAX must be used instead + // of RLIM_INFINITY, but testing on macOS >= 10.6, reveals that + // we can, in fact, use even RLIM_INFINITY. + // However, we need to limit the value to 0x100000 (which is the max value + // allowed on Linux) so that any existing code that iterates over all allowed + // file descriptors, finishes in a reasonable time, without appearing + // to hang. + nbr_files.rlim_cur = MIN(0x100000, nbr_files.rlim_max); status = setrlimit(RLIMIT_NOFILE, &nbr_files); + if (status != 0) { + // If that fails then try lowering the limit to either OPEN_MAX + // (which is safe) or the original limit, whichever was greater. + nbr_files.rlim_cur = MAX(OPEN_MAX, rlim_original); + status = setrlimit(RLIMIT_NOFILE, &nbr_files); + } if (status != 0) { log_info(os)("os::init_2 setrlimit failed: %s", os::strerror(errno)); } diff --git a/src/hotspot/os/bsd/os_perf_bsd.cpp b/src/hotspot/os/bsd/os_perf_bsd.cpp index e69bfc79558c3..546cef8edc5b7 100644 --- a/src/hotspot/os/bsd/os_perf_bsd.cpp +++ b/src/hotspot/os/bsd/os_perf_bsd.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ #include #include #include + #include #endif static const double NANOS_PER_SEC = 1000000000.0; @@ -46,10 +47,10 @@ static const double NANOS_PER_SEC = 1000000000.0; class CPUPerformanceInterface::CPUPerformance : public CHeapObj { friend class CPUPerformanceInterface; private: - long _total_cpu_nanos; + uint64_t _jvm_real; long _total_csr_nanos; - long _jvm_user_nanos; - long _jvm_system_nanos; + uint64_t _jvm_user; + uint64_t _jvm_system; long _jvm_context_switches; long _used_ticks; long _total_ticks; @@ -83,11 +84,11 @@ class CPUPerformanceInterface::CPUPerformance : public CHeapObj { }; CPUPerformanceInterface::CPUPerformance::CPUPerformance() { - _total_cpu_nanos= 0; - _total_csr_nanos= 0; + _jvm_real = 0; + _total_csr_nanos = 0; _jvm_context_switches = 0; - _jvm_user_nanos = 0; - _jvm_system_nanos = 0; + _jvm_user = 0; + _jvm_system = 0; _used_ticks = 0; _total_ticks = 0; _active_processor_count = 0; @@ -148,42 +149,35 @@ int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_ int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) { #ifdef __APPLE__ int result = cpu_load_total_process(psystemTotalLoad); - mach_port_t task = mach_task_self(); - mach_msg_type_number_t task_info_count = TASK_INFO_MAX; - task_info_data_t task_info_data; - kern_return_t kr = task_info(task, TASK_ABSOLUTETIME_INFO, (task_info_t)task_info_data, &task_info_count); - if (kr != KERN_SUCCESS) { + + struct tms buf; + clock_t jvm_real = times(&buf); + if (jvm_real == (clock_t) (-1)) { return OS_ERR; } - task_absolutetime_info_t absolutetime_info = (task_absolutetime_info_t)task_info_data; int active_processor_count = os::active_processor_count(); - long jvm_user_nanos = absolutetime_info->total_user; - long jvm_system_nanos = absolutetime_info->total_system; - - long total_cpu_nanos; - if(!now_in_nanos(&total_cpu_nanos)) { - return OS_ERR; - } + uint64_t jvm_user = buf.tms_utime; + uint64_t jvm_system = buf.tms_stime; - if (_total_cpu_nanos == 0 || active_processor_count != _active_processor_count) { - // First call or change in active processor count + if (active_processor_count != _active_processor_count) { + // Change in active processor count result = OS_ERR; - } + } else { + uint64_t delta = active_processor_count * (jvm_real - _jvm_real); + if (delta == 0) { + // Avoid division by zero + return OS_ERR; + } - long delta_nanos = active_processor_count * (total_cpu_nanos - _total_cpu_nanos); - if (delta_nanos == 0) { - // Avoid division by zero - return OS_ERR; + *pjvmUserLoad = normalize((double)(jvm_user - _jvm_user) / delta); + *pjvmKernelLoad = normalize((double)(jvm_system - _jvm_system) / delta); } - *pjvmUserLoad = normalize((double)(jvm_user_nanos - _jvm_user_nanos)/delta_nanos); - *pjvmKernelLoad = normalize((double)(jvm_system_nanos - _jvm_system_nanos)/delta_nanos); - _active_processor_count = active_processor_count; - _total_cpu_nanos = total_cpu_nanos; - _jvm_user_nanos = jvm_user_nanos; - _jvm_system_nanos = jvm_system_nanos; + _jvm_real = jvm_real; + _jvm_user = jvm_user; + _jvm_system = jvm_system; return result; #else diff --git a/src/hotspot/os/linux/attachListener_linux.cpp b/src/hotspot/os/linux/attachListener_linux.cpp index 0ce76721ec3cb..ecdd21a486975 100644 --- a/src/hotspot/os/linux/attachListener_linux.cpp +++ b/src/hotspot/os/linux/attachListener_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -182,6 +182,8 @@ int LinuxAttachListener::init() { char initial_path[UNIX_PATH_MAX]; // socket file during setup int listener; // listener socket (file descriptor) + static_assert(sizeof(off_t) == 8, "Expected Large File Support in this file"); + // register function to cleanup if (!_atexit_registered) { _atexit_registered = true; @@ -247,7 +249,7 @@ int LinuxAttachListener::init() { // LinuxAttachOperation* LinuxAttachListener::read_request(int s) { char ver_str[8]; - sprintf(ver_str, "%d", ATTACH_PROTOCOL_VER); + os::snprintf_checked(ver_str, sizeof(ver_str), "%d", ATTACH_PROTOCOL_VER); // The request is a sequence of strings so we first figure out the // expected count and the maximum possible length of the request. @@ -290,7 +292,7 @@ LinuxAttachOperation* LinuxAttachListener::read_request(int s) { if ((strlen(buf) != strlen(ver_str)) || (atoi(buf) != ATTACH_PROTOCOL_VER)) { char msg[32]; - sprintf(msg, "%d\n", ATTACH_ERROR_BADVERSION); + os::snprintf_checked(msg, sizeof(msg), "%d\n", ATTACH_ERROR_BADVERSION); write_fully(s, msg, strlen(msg)); return NULL; } @@ -384,7 +386,7 @@ LinuxAttachOperation* LinuxAttachListener::dequeue() { // write the given buffer to the socket int LinuxAttachListener::write_fully(int s, char* buf, int len) { do { - int n = ::write(s, buf, len); + ssize_t n = ::write(s, buf, len); if (n == -1) { if (errno != EINTR) return -1; } else { @@ -410,7 +412,7 @@ void LinuxAttachOperation::complete(jint result, bufferedStream* st) { // write operation result char msg[32]; - sprintf(msg, "%d\n", result); + os::snprintf_checked(msg, sizeof(msg), "%d\n", result); int rc = LinuxAttachListener::write_fully(this->socket(), msg, strlen(msg)); // write any result data @@ -444,14 +446,14 @@ AttachOperation* AttachListener::dequeue() { void AttachListener::vm_start() { char fn[UNIX_PATH_MAX]; - struct stat64 st; + struct stat st; int ret; int n = snprintf(fn, UNIX_PATH_MAX, "%s/.java_pid%d", os::get_temp_directory(), os::current_process_id()); assert(n < (int)UNIX_PATH_MAX, "java_pid file name buffer overflow"); - RESTARTABLE(::stat64(fn, &st), ret); + RESTARTABLE(::stat(fn, &st), ret); if (ret == 0) { ret = ::unlink(fn); if (ret == -1) { @@ -471,8 +473,8 @@ int AttachListener::pd_init() { bool AttachListener::check_socket_file() { int ret; - struct stat64 st; - ret = stat64(LinuxAttachListener::path(), &st); + struct stat st; + ret = stat(LinuxAttachListener::path(), &st); if (ret == -1) { // need to restart attach listener. log_debug(attach)("Socket file %s does not exist - Restart Attach Listener", LinuxAttachListener::path()); @@ -511,14 +513,14 @@ bool AttachListener::is_init_trigger() { } char fn[PATH_MAX + 1]; int ret; - struct stat64 st; - sprintf(fn, ".attach_pid%d", os::current_process_id()); - RESTARTABLE(::stat64(fn, &st), ret); + struct stat st; + os::snprintf_checked(fn, sizeof(fn), ".attach_pid%d", os::current_process_id()); + RESTARTABLE(::stat(fn, &st), ret); if (ret == -1) { log_trace(attach)("Failed to find attach file: %s, trying alternate", fn); snprintf(fn, sizeof(fn), "%s/.attach_pid%d", os::get_temp_directory(), os::current_process_id()); - RESTARTABLE(::stat64(fn, &st), ret); + RESTARTABLE(::stat(fn, &st), ret); if (ret == -1) { log_debug(attach)("Failed to find attach file: %s", fn); } diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index e62dcf4f75975..e380319e78980 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -225,9 +225,9 @@ void CgroupV1Subsystem::print_version_specific_info(outputStream* st) { jlong kmem_limit = kernel_memory_limit_in_bytes(); jlong kmem_max_usage = kernel_memory_max_usage_in_bytes(); + OSContainer::print_container_helper(st, kmem_limit, "kernel_memory_limit_in_bytes"); OSContainer::print_container_helper(st, kmem_usage, "kernel_memory_usage_in_bytes"); - OSContainer::print_container_helper(st, kmem_limit, "kernel_memory_max_usage_in_bytes"); - OSContainer::print_container_helper(st, kmem_max_usage, "kernel_memory_limit_in_bytes"); + OSContainer::print_container_helper(st, kmem_max_usage, "kernel_memory_max_usage_in_bytes"); } char * CgroupV1Subsystem::cpu_cpuset_cpus() { diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index f86f3938c785d..7c951cee51cf7 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -430,7 +430,7 @@ void os::init_system_properties_values() { #define SYS_EXT_DIR "/usr/java/packages" #define EXTENSIONS_DIR "/lib/ext" - // Buffer that fits several sprintfs. + // Buffer that fits several snprintfs. // Note that the space for the colon and the trailing null are provided // by the nulls included by the sizeof operator. const size_t bufsize = @@ -485,17 +485,15 @@ void os::init_system_properties_values() { const char *v_colon = ":"; if (v == NULL) { v = ""; v_colon = ""; } // That's +1 for the colon and +1 for the trailing '\0'. - char *ld_library_path = NEW_C_HEAP_ARRAY(char, - strlen(v) + 1 + - sizeof(SYS_EXT_DIR) + sizeof("/lib/") + sizeof(DEFAULT_LIBPATH) + 1, - mtInternal); - sprintf(ld_library_path, "%s%s" SYS_EXT_DIR "/lib:" DEFAULT_LIBPATH, v, v_colon); + size_t pathsize = strlen(v) + 1 + sizeof(SYS_EXT_DIR) + sizeof("/lib/") + sizeof(DEFAULT_LIBPATH) + 1; + char *ld_library_path = NEW_C_HEAP_ARRAY(char, pathsize, mtInternal); + os::snprintf_checked(ld_library_path, pathsize, "%s%s" SYS_EXT_DIR "/lib:" DEFAULT_LIBPATH, v, v_colon); Arguments::set_library_path(ld_library_path); FREE_C_HEAP_ARRAY(char, ld_library_path); } // Extensions directories. - sprintf(buf, "%s" EXTENSIONS_DIR ":" SYS_EXT_DIR EXTENSIONS_DIR, Arguments::get_java_home()); + os::snprintf_checked(buf, bufsize, "%s" EXTENSIONS_DIR ":" SYS_EXT_DIR EXTENSIONS_DIR, Arguments::get_java_home()); Arguments::set_ext_dirs(buf); FREE_C_HEAP_ARRAY(char, buf); @@ -1441,14 +1439,6 @@ const char* os::dll_file_extension() { return ".so"; } // directory not the java application's temp directory, ala java.io.tmpdir. const char* os::get_temp_directory() { return "/tmp"; } -static bool file_exists(const char* filename) { - struct stat statbuf; - if (filename == NULL || strlen(filename) == 0) { - return false; - } - return os::stat(filename, &statbuf) == 0; -} - // check if addr is inside libjvm.so bool os::address_is_in_vm(address addr) { static address libjvm_base_addr; @@ -2516,7 +2506,7 @@ static void print_sys_devices_cpu_info(outputStream* st, char* buf, size_t bufle snprintf(hbuf_type, 60, "/sys/devices/system/cpu/cpu0/cache/index%u/type", i); snprintf(hbuf_size, 60, "/sys/devices/system/cpu/cpu0/cache/index%u/size", i); snprintf(hbuf_coherency_line_size, 80, "/sys/devices/system/cpu/cpu0/cache/index%u/coherency_line_size", i); - if (file_exists(hbuf_level)) { + if (os::file_exists(hbuf_level)) { _print_ascii_file_h("cache level", hbuf_level, st); _print_ascii_file_h("cache type", hbuf_type, st); _print_ascii_file_h("cache size", hbuf_size, st); @@ -2753,6 +2743,8 @@ int os::vm_allocation_granularity() { void linux_wrap_code(char* base, size_t size) { static volatile jint cnt = 0; + static_assert(sizeof(off_t) == 8, "Expected Large File Support in this file"); + if (!UseOprofile) { return; } @@ -3597,7 +3589,7 @@ static bool linux_mprotect(char* addr, size_t size, int prot) { #ifdef CAN_SHOW_REGISTERS_ON_ASSERT if (addr != g_assert_poison) #endif - Events::log(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(bottom), p2i(bottom+size), prot); + Events::log_memprotect(NULL, "Protecting memory [" INTPTR_FORMAT "," INTPTR_FORMAT "] with protection modes %x", p2i(bottom), p2i(bottom+size), prot); return ::mprotect(bottom, size, prot) == 0; } @@ -5000,14 +4992,14 @@ int os::open(const char *path, int oflag, int mode) { oflag |= O_CLOEXEC; #endif - int fd = ::open64(path, oflag, mode); + int fd = ::open(path, oflag, mode); if (fd == -1) return -1; //If the open succeeded, the file might still be a directory { - struct stat64 buf64; - int ret = ::fstat64(fd, &buf64); - int st_mode = buf64.st_mode; + struct stat buf; + int ret = ::fstat(fd, &buf); + int st_mode = buf.st_mode; if (ret != -1) { if ((st_mode & S_IFMT) == S_IFDIR) { @@ -5044,17 +5036,17 @@ int os::open(const char *path, int oflag, int mode) { int os::create_binary_file(const char* path, bool rewrite_existing) { int oflags = O_WRONLY | O_CREAT; oflags |= rewrite_existing ? O_TRUNC : O_EXCL; - return ::open64(path, oflags, S_IREAD | S_IWRITE); + return ::open(path, oflags, S_IREAD | S_IWRITE); } // return current position of file pointer jlong os::current_file_offset(int fd) { - return (jlong)::lseek64(fd, (off64_t)0, SEEK_CUR); + return (jlong)::lseek(fd, (off_t)0, SEEK_CUR); } // move file pointer to the specified offset jlong os::seek_to_file_offset(int fd, jlong offset) { - return (jlong)::lseek64(fd, (off64_t)offset, SEEK_SET); + return (jlong)::lseek(fd, (off_t)offset, SEEK_SET); } // This code originates from JDK's sysAvailable @@ -5063,10 +5055,10 @@ jlong os::seek_to_file_offset(int fd, jlong offset) { int os::available(int fd, jlong *bytes) { jlong cur, end; int mode; - struct stat64 buf64; + struct stat buf; - if (::fstat64(fd, &buf64) >= 0) { - mode = buf64.st_mode; + if (::fstat(fd, &buf) >= 0) { + mode = buf.st_mode; if (S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) { int n; if (::ioctl(fd, FIONREAD, &n) >= 0) { @@ -5075,11 +5067,11 @@ int os::available(int fd, jlong *bytes) { } } } - if ((cur = ::lseek64(fd, 0L, SEEK_CUR)) == -1) { + if ((cur = ::lseek(fd, 0L, SEEK_CUR)) == -1) { return 0; - } else if ((end = ::lseek64(fd, 0L, SEEK_END)) == -1) { + } else if ((end = ::lseek(fd, 0L, SEEK_END)) == -1) { return 0; - } else if (::lseek64(fd, cur, SEEK_SET) == -1) { + } else if (::lseek(fd, cur, SEEK_SET) == -1) { return 0; } *bytes = end - cur; diff --git a/src/hotspot/os/linux/os_perf_linux.cpp b/src/hotspot/os/linux/os_perf_linux.cpp index 7c42379a0a710..c37e25c42eb2b 100644 --- a/src/hotspot/os/linux/os_perf_linux.cpp +++ b/src/hotspot/os/linux/os_perf_linux.cpp @@ -847,7 +847,7 @@ SystemProcessInterface::SystemProcesses::ProcessIterator::ProcessIterator() { bool SystemProcessInterface::SystemProcesses::ProcessIterator::initialize() { _dir = os::opendir("/proc"); _entry = NULL; - _valid = true; + _valid = _dir != NULL; // May be null if /proc is not accessible. next_process(); return true; diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index fd94f40282cac..4307a189edf12 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -279,6 +279,7 @@ static char* reserve_mmapped_memory(size_t bytes, char* requested_addr) { } static int util_posix_fallocate(int fd, off_t offset, off_t len) { + static_assert(sizeof(off_t) == 8, "Expected Large File Support in this file"); #ifdef __APPLE__ fstore_t store = { F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, len }; // First we try to get a continuous chunk of disk space @@ -720,7 +721,7 @@ void os::dll_unload(void *lib) { } jlong os::lseek(int fd, jlong offset, int whence) { - return (jlong) BSD_ONLY(::lseek) NOT_BSD(::lseek64)(fd, offset, whence); + return (jlong) AIX_ONLY(::lseek64) NOT_AIX(::lseek)(fd, offset, whence); } int os::fsync(int fd) { @@ -728,7 +729,7 @@ int os::fsync(int fd) { } int os::ftruncate(int fd, jlong length) { - return BSD_ONLY(::ftruncate) NOT_BSD(::ftruncate64)(fd, length); + return AIX_ONLY(::ftruncate64) NOT_AIX(::ftruncate)(fd, length); } const char* os::get_current_directory(char *buf, size_t buflen) { @@ -739,9 +740,9 @@ FILE* os::open(int fd, const char* mode) { return ::fdopen(fd, mode); } -size_t os::write(int fd, const void *buf, unsigned int nBytes) { - size_t res; - RESTARTABLE((size_t) ::write(fd, buf, (size_t) nBytes), res); +ssize_t os::pd_write(int fd, const void *buf, size_t nBytes) { + ssize_t res; + RESTARTABLE(::write(fd, buf, nBytes), res); return res; } @@ -800,10 +801,6 @@ int os::connect(int fd, struct sockaddr* him, socklen_t len) { RESTARTABLE_RETURN_INT(::connect(fd, him, len)); } -struct hostent* os::get_host_by_name(char* name) { - return ::gethostbyname(name); -} - void os::exit(int num) { ::exit(num); } diff --git a/src/hotspot/os/posix/os_posix.hpp b/src/hotspot/os/posix/os_posix.hpp index c1f9601cff9d6..261921adec4a8 100644 --- a/src/hotspot/os/posix/os_posix.hpp +++ b/src/hotspot/os/posix/os_posix.hpp @@ -27,7 +27,7 @@ // Note: the Posix API aims to capture functionality available on all Posix // compliant platforms, but in practice the implementations may depend on -// non-Posix functionality. For example, the use of lseek64 and ftruncate64. +// non-Posix functionality. // This use of non-Posix API's is made possible by compiling/linking in a mode // that is not restricted to being fully Posix complaint, such as by declaring // -D_GNU_SOURCE. But be aware that in doing so we may enable non-Posix diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index 5d3416e2869da..5652802aac2d5 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -89,38 +89,25 @@ static void save_memory_to_file(char* addr, size_t size) { const char* destfile = PerfMemory::get_perfdata_file_path(); assert(destfile[0] != '\0', "invalid PerfData file path"); - int result; + int fd; - RESTARTABLE(os::open(destfile, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR), - result); - if (result == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("Could not create Perfdata save file: %s: %s\n", - destfile, os::strerror(errno)); - } + RESTARTABLE(os::open(destfile, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR), fd); + if (fd == OS_ERR) { + warning("Could not create Perfdata save file: %s: %s\n", + destfile, os::strerror(errno)); } else { - int fd = result; + ssize_t result; - for (size_t remaining = size; remaining > 0;) { - - RESTARTABLE(::write(fd, addr, remaining), result); - if (result == OS_ERR) { - if (PrintMiscellaneous && Verbose) { - warning("Could not write Perfdata save file: %s: %s\n", - destfile, os::strerror(errno)); - } - break; - } - - remaining -= (size_t)result; - addr += result; + bool successful_write = os::write(fd, addr, size); + if (!successful_write) { + warning("Could not write Perfdata save file: %s: %s\n", + destfile, os::strerror(errno)); } + result = ::close(fd); - if (PrintMiscellaneous && Verbose) { - if (result == OS_ERR) { - warning("Could not close %s: %s\n", destfile, os::strerror(errno)); - } + if (result == OS_ERR) { + warning("Could not close %s: %s\n", destfile, os::strerror(errno)); } } FREE_C_HEAP_ARRAY(char, destfile); @@ -880,9 +867,9 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size // Open the filename in the current directory. // Cannot use O_TRUNC here; truncation of an existing file has to happen // after the is_file_secure() check below. - int result; - RESTARTABLE(os::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IRUSR|S_IWUSR), result); - if (result == OS_ERR) { + int fd; + RESTARTABLE(os::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IRUSR|S_IWUSR), fd); + if (fd == OS_ERR) { if (PrintMiscellaneous && Verbose) { if (errno == ELOOP) { warning("file %s is a symlink and is not secure\n", filename); @@ -898,9 +885,6 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size // close the directory and reset the current working directory close_directory_secure_cwd(dirp, saved_cwd_fd); - // save the file descriptor - int fd = result; - // check to see if the file is secure if (!is_file_secure(fd, filename)) { ::close(fd); @@ -933,6 +917,8 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size } #endif + ssize_t result; + // truncate the file to get rid of any existing data RESTARTABLE(::ftruncate(fd, (off_t)0), result); if (result == OS_ERR) { @@ -959,11 +945,11 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size int zero_int = 0; result = (int)os::seek_to_file_offset(fd, (jlong)(seekpos)); if (result == -1 ) break; - RESTARTABLE(::write(fd, &zero_int, 1), result); - if (result != 1) { + if (!os::write(fd, &zero_int, 1)) { if (errno == ENOSPC) { warning("Insufficient space for shared memory file:\n %s\nTry using the -Djava.io.tmpdir= option to select an alternate temp location.\n", filename); } + result = OS_ERR; break; } } @@ -971,7 +957,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size if (result != -1) { return fd; } else { - ::close(fd); + os::close(fd); return -1; } } diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index d3461977fa6cb..dd318b0653256 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -385,8 +385,9 @@ void os::init_system_properties_values() { char path[MAX_PATH]; char buf[2 * MAX_PATH + 2 * sizeof(EXT_DIR) + sizeof(PACKAGE_DIR) + 1]; GetWindowsDirectory(path, MAX_PATH); - sprintf(buf, "%s%s;%s%s%s", Arguments::get_java_home(), EXT_DIR, - path, PACKAGE_DIR, EXT_DIR); + os::snprintf_checked(buf, sizeof(buf), "%s%s;%s%s%s", + Arguments::get_java_home(), EXT_DIR, + path, PACKAGE_DIR, EXT_DIR); Arguments::set_ext_dirs(buf); } #undef EXT_DIR @@ -1896,7 +1897,10 @@ void os::win32::print_windows_version(outputStream* st) { // - 2016 GA 10/2016 build: 14393 // - 2019 GA 11/2018 build: 17763 // - 2022 GA 08/2021 build: 20348 - if (build_number > 20347) { + // - 2025 Preview build : 26040 + if (build_number > 26039) { + st->print("Server 2025"); + } else if (build_number > 20347) { st->print("Server 2022"); } else if (build_number > 17762) { st->print("Server 2019"); @@ -3351,7 +3355,8 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi os::attempt_reserve_memory_at(aligned_base, size); } - assert(aligned_base != NULL, "Did not manage to re-map after %d attempts?", max_attempts); + assert(aligned_base != nullptr, + "Did not manage to re-map after %d attempts (size %zu, alignment %zu, file descriptor %d)", max_attempts, size, alignment, file_desc); return aligned_base; } @@ -4842,8 +4847,19 @@ FILE* os::open(int fd, const char* mode) { return ::_fdopen(fd, mode); } -size_t os::write(int fd, const void *buf, unsigned int nBytes) { - return ::write(fd, buf, nBytes); +ssize_t os::pd_write(int fd, const void *buf, size_t nBytes) { + ssize_t original_len = (ssize_t)nBytes; + while (nBytes > 0) { + unsigned int len = nBytes > INT_MAX ? INT_MAX : (unsigned int)nBytes; + // On Windows, ::write takes 'unsigned int' no of bytes, so nBytes should be split if larger. + ssize_t written_bytes = ::write(fd, buf, len); + if (written_bytes < 0) { + return OS_ERR; + } + nBytes -= written_bytes; + buf = (char *)buf + written_bytes; + } + return original_len; } int os::close(int fd) { @@ -5797,10 +5813,6 @@ static jint initSock() { return JNI_OK; } -struct hostent* os::get_host_by_name(char* name) { - return (struct hostent*)gethostbyname(name); -} - int os::socket_close(int fd) { return ::closesocket(fd); } diff --git a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp index f8e6787505b73..f3b80be3c02da 100644 --- a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp +++ b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2012, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -253,7 +253,7 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, stub = SharedRuntime::get_handle_wrong_method_stub(); } - else if ((sig == USE_POLL_BIT_ONLY ? SIGTRAP : SIGSEGV) && + else if ((sig == (USE_POLL_BIT_ONLY ? SIGTRAP : SIGSEGV)) && // A linux-ppc64 kernel before 2.6.6 doesn't set si_addr on some segfaults // in 64bit mode (cf. http://www.kernel.org/pub/linux/kernel/v2.6/ChangeLog-2.6.6), // especially when we try to read from the safepoint polling page. So the check diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index 3d56c648e4680..34a02f74dedb1 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -560,6 +560,21 @@ void os::print_context(outputStream *st, const void *context) { st->print(", ERR=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_ERR]); st->cr(); st->print(" TRAPNO=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_TRAPNO]); + // Add XMM registers + MXCSR. Note that C2 uses XMM to spill GPR values including pointers. + st->cr(); + st->cr(); + // Sanity check: fpregs should point into the context. + if ((address)uc->uc_mcontext.fpregs < (address)uc || + pointer_delta(uc->uc_mcontext.fpregs, uc, 1) >= sizeof(ucontext_t)) { + st->print_cr("bad uc->uc_mcontext.fpregs: " INTPTR_FORMAT " (uc: " INTPTR_FORMAT ")", + p2i(uc->uc_mcontext.fpregs), p2i(uc)); + } else { + for (int i = 0; i < 16; ++i) { + const int64_t* xmm_val_addr = (int64_t*)&(uc->uc_mcontext.fpregs->_xmm[i]); + st->print_cr("XMM[%d]=" INTPTR_FORMAT " " INTPTR_FORMAT, i, xmm_val_addr[1], xmm_val_addr[0]); + } + st->print(" MXCSR=" UINT32_FORMAT_X_0, uc->uc_mcontext.fpregs->mxcsr); + } #else st->print( "EAX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EAX]); st->print(", EBX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EBX]); diff --git a/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp b/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp index 96de0c05af604..51bf241a601b7 100644 --- a/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp +++ b/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp @@ -432,6 +432,15 @@ void os::print_context(outputStream *st, const void *context) { st->cr(); st->print( "RIP=" INTPTR_FORMAT, uc->Rip); st->print(", EFLAGS=" INTPTR_FORMAT, uc->EFlags); + // Add XMM registers + MXCSR. Note that C2 uses XMM to spill GPR values including pointers. + st->cr(); + st->cr(); + for (int i = 0; i < 16; ++i) { + const uint64_t *xmm = ((const uint64_t*)&(uc->Xmm0)) + 2 * i; + st->print_cr("XMM[%d]=" INTPTR_FORMAT " " INTPTR_FORMAT, + i, xmm[1], xmm[0]); + } + st->print(" MXCSR=" UINT32_FORMAT_X_0, uc->MxCsr); #else st->print( "EAX=" INTPTR_FORMAT, uc->Eax); st->print(", EBX=" INTPTR_FORMAT, uc->Ebx); diff --git a/src/hotspot/share/adlc/adlc.hpp b/src/hotspot/share/adlc/adlc.hpp index 19567f05d4049..ec3a0c4e153ca 100644 --- a/src/hotspot/share/adlc/adlc.hpp +++ b/src/hotspot/share/adlc/adlc.hpp @@ -108,4 +108,8 @@ typedef unsigned int uintptr_t; // it everywhere it needs to be available. extern ArchDesc* globalAD; +// Performs snprintf and asserts the result is non-negative (so there was not +// an encoding error) and that the output was not truncated. +extern int snprintf_checked(char* buf, size_t len, const char* fmt, ...); + #endif // SHARE_ADLC_ADLC_HPP diff --git a/src/hotspot/share/adlc/adlparse.cpp b/src/hotspot/share/adlc/adlparse.cpp index 283713bb1f838..bcbf3ab7e1b43 100644 --- a/src/hotspot/share/adlc/adlparse.cpp +++ b/src/hotspot/share/adlc/adlparse.cpp @@ -211,8 +211,9 @@ void ADLParser::instr_parse(void) { return; } assert(match_rules_cnt < 100," too many match rule clones"); - char* buf = (char*) AdlAllocateHeap(strlen(instr->_ident) + 4); - sprintf(buf, "%s_%d", instr->_ident, match_rules_cnt++); + const size_t buf_size = strlen(instr->_ident) + 4; + char* buf = (char*) AdlAllocateHeap(buf_size); + snprintf_checked(buf, buf_size, "%s_%d", instr->_ident, match_rules_cnt++); rule->_result = buf; // Check for commutative operations with tree operands. matchrule_clone_and_swap(rule, instr->_ident, match_rules_cnt); @@ -2805,8 +2806,9 @@ void ADLParser::ins_encode_parse_block(InstructForm& inst) { // Create a new encoding name based on the name of the instruction // definition, which should be unique. const char* prefix = "__ins_encode_"; - char* ec_name = (char*) AdlAllocateHeap(strlen(inst._ident) + strlen(prefix) + 1); - sprintf(ec_name, "%s%s", prefix, inst._ident); + const size_t ec_name_size = strlen(inst._ident) + strlen(prefix) + 1; + char* ec_name = (char*) AdlAllocateHeap(ec_name_size); + snprintf_checked(ec_name, ec_name_size, "%s%s", prefix, inst._ident); assert(_AD._encode->encClass(ec_name) == NULL, "shouldn't already exist"); EncClass* encoding = _AD._encode->add_EncClass(ec_name); @@ -3276,8 +3278,9 @@ void ADLParser::constant_parse(InstructForm& inst) { // Create a new encoding name based on the name of the instruction // definition, which should be unique. const char* prefix = "__constant_"; - char* ec_name = (char*) AdlAllocateHeap(strlen(inst._ident) + strlen(prefix) + 1); - sprintf(ec_name, "%s%s", prefix, inst._ident); + const size_t ec_name_size = strlen(inst._ident) + strlen(prefix) + 1; + char* ec_name = (char*) AdlAllocateHeap(ec_name_size); + snprintf_checked(ec_name, ec_name_size, "%s%s", prefix, inst._ident); assert(_AD._encode->encClass(ec_name) == NULL, "shouldn't already exist"); EncClass* encoding = _AD._encode->add_EncClass(ec_name); @@ -4596,8 +4599,9 @@ char *ADLParser::get_ident_or_literal_constant(const char* description) { // Grab a constant expression. param = get_paren_expr(description); if (param[0] != '(') { - char* buf = (char*) AdlAllocateHeap(strlen(param) + 3); - sprintf(buf, "(%s)", param); + const size_t buf_size = strlen(param) + 3; + char* buf = (char*) AdlAllocateHeap(buf_size); + snprintf_checked(buf, buf_size, "(%s)", param); param = buf; } assert(is_literal_constant(param), @@ -5204,8 +5208,9 @@ void ADLParser::next_line() { char* ADLParser::get_line_string(int linenum) { const char* file = _AD._ADL_file._name; int line = linenum ? linenum : this->linenum(); - char* location = (char *)AdlAllocateHeap(strlen(file) + 100); - sprintf(location, "\n#line %d \"%s\"\n", line, file); + const size_t location_size = strlen(file) + 100; + char* location = (char *)AdlAllocateHeap(location_size); + snprintf_checked(location, location_size, "\n#line %d \"%s\"\n", line, file); return location; } diff --git a/src/hotspot/share/adlc/archDesc.cpp b/src/hotspot/share/adlc/archDesc.cpp index cd9aab9e2ed66..1d83cb1811739 100644 --- a/src/hotspot/share/adlc/archDesc.cpp +++ b/src/hotspot/share/adlc/archDesc.cpp @@ -815,7 +815,7 @@ static const char *getRegMask(const char *reg_class_name) { const char *mask = "_mask"; int length = (int)strlen(rc_name) + (int)strlen(mask) + 5; char *regMask = new char[length]; - sprintf(regMask,"%s%s()", rc_name, mask); + snprintf_checked(regMask, length, "%s%s()", rc_name, mask); delete[] rc_name; return regMask; } @@ -908,7 +908,7 @@ char *ArchDesc::stack_or_reg_mask(OperandForm &opForm) { const char *stack_or = "STACK_OR_"; int length = (int)strlen(stack_or) + (int)strlen(reg_mask_name) + 1; char *result = new char[length]; - sprintf(result,"%s%s", stack_or, reg_mask_name); + snprintf_checked(result, length, "%s%s", stack_or, reg_mask_name); return result; } diff --git a/src/hotspot/share/adlc/dfa.cpp b/src/hotspot/share/adlc/dfa.cpp index 5abc43652973a..b277c5c63f057 100644 --- a/src/hotspot/share/adlc/dfa.cpp +++ b/src/hotspot/share/adlc/dfa.cpp @@ -207,13 +207,13 @@ Expr *ArchDesc::calc_cost(FILE *fp, const char *spaces, MatchList &mList, Produc Expr *c = new Expr("0"); if (mList._lchild) { // If left child, add it in const char* lchild_to_upper = ArchDesc::getMachOperEnum(mList._lchild); - sprintf(Expr::buffer(), "_kids[0]->_cost[%s]", lchild_to_upper); + snprintf_checked(Expr::buffer(), STRING_BUFFER_LENGTH, "_kids[0]->_cost[%s]", lchild_to_upper); c->add(Expr::buffer()); delete[] lchild_to_upper; } if (mList._rchild) { // If right child, add it in const char* rchild_to_upper = ArchDesc::getMachOperEnum(mList._rchild); - sprintf(Expr::buffer(), "_kids[1]->_cost[%s]", rchild_to_upper); + snprintf_checked(Expr::buffer(), STRING_BUFFER_LENGTH, "_kids[1]->_cost[%s]", rchild_to_upper); c->add(Expr::buffer()); delete[] rchild_to_upper; } @@ -730,7 +730,7 @@ const char *Expr::compute_expr(const Expr *c1, const Expr *c2) { snprintf(string_buffer, STRING_BUFFER_LENGTH, "%s", c2->_expr); } else { - sprintf( string_buffer, "0"); + snprintf_checked(string_buffer, STRING_BUFFER_LENGTH, "0"); } string_buffer[STRING_BUFFER_LENGTH - 1] = '\0'; char *cost = strdup(string_buffer); diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 854b1b310e497..b45f432dac9ab 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -25,6 +25,8 @@ // FORMS.CPP - Definitions for ADL Parser Forms Classes #include "adlc.hpp" +#define remaining_buflen(buffer, position) (sizeof(buffer) - ((position) - (buffer))) + //==============================Instructions=================================== //------------------------------InstructForm----------------------------------- InstructForm::InstructForm(const char *id, bool ideal_only) @@ -1303,7 +1305,7 @@ bool InstructForm::check_branch_variant(ArchDesc &AD, InstructForm *short_branch void InstructForm::rep_var_format(FILE *fp, const char *rep_var) { // Handle special constant table variables. if (strcmp(rep_var, "constanttablebase") == 0) { - fprintf(fp, "char reg[128]; ra->dump_register(in(mach_constant_base_node_input()), reg);\n"); + fprintf(fp, "char reg[128]; ra->dump_register(in(mach_constant_base_node_input()), reg, sizeof(reg));\n"); fprintf(fp, " st->print(\"%%s\", reg);\n"); return; } @@ -1538,7 +1540,7 @@ Predicate *InstructForm::build_predicate() { s += strlen(s); } // Add predicate to working buffer - sprintf(s,"/*%s*/(",(char*)i._key); + snprintf_checked(s, remaining_buflen(buf, s), "/*%s*/(",(char*)i._key); s += strlen(s); mnode->build_instr_pred(s,(char*)i._key, 0, path_bitmask, 0); s += strlen(s); @@ -2501,7 +2503,7 @@ void OperandForm::int_format(FILE *fp, FormDict &globals, uint index) { strcmp(ideal_type(globalAD->globalNames()), "RegFlags") == 0)) { // !!!!! !!!!! fprintf(fp," { char reg_str[128];\n"); - fprintf(fp," ra->dump_register(node,reg_str);\n"); + fprintf(fp," ra->dump_register(node,reg_str, sizeof(reg_str));\n"); fprintf(fp," st->print(\"%cs\",reg_str);\n",'%'); fprintf(fp," }\n"); } else if (_matrule && (dtype = _matrule->is_base_constant(globals)) != Form::none) { @@ -2509,7 +2511,7 @@ void OperandForm::int_format(FILE *fp, FormDict &globals, uint index) { } else if (ideal_to_sReg_type(_ident) != Form::none) { // Special format for Stack Slot Register fprintf(fp," { char reg_str[128];\n"); - fprintf(fp," ra->dump_register(node,reg_str);\n"); + fprintf(fp," ra->dump_register(node,reg_str, sizeof(reg_str));\n"); fprintf(fp," st->print(\"%cs\",reg_str);\n",'%'); fprintf(fp," }\n"); } else { @@ -2530,7 +2532,7 @@ void OperandForm::ext_format(FILE *fp, FormDict &globals, uint index) { fprintf(fp," { char reg_str[128];\n"); fprintf(fp," ra->dump_register(node->in(idx"); if ( index != 0 ) fprintf(fp, "+%d",index); - fprintf(fp, "),reg_str);\n"); + fprintf(fp, "),reg_str,sizeof(reg_str));\n"); fprintf(fp," st->print(\"%cs\",reg_str);\n",'%'); fprintf(fp," }\n"); } else if (_matrule && (dtype = _matrule->is_base_constant(globals)) != Form::none) { @@ -2540,7 +2542,7 @@ void OperandForm::ext_format(FILE *fp, FormDict &globals, uint index) { fprintf(fp," { char reg_str[128];\n"); fprintf(fp," ra->dump_register(node->in(idx"); if ( index != 0 ) fprintf(fp, "+%d",index); - fprintf(fp, "),reg_str);\n"); + fprintf(fp, "),reg_str,sizeof(reg_str));\n"); fprintf(fp," st->print(\"%cs\",reg_str);\n",'%'); fprintf(fp," }\n"); } else { @@ -3476,7 +3478,7 @@ void MatchNode::build_internalop( ) { _rChild->_internalop : _rChild->_opType) : ""; len += (int)strlen(lstr) + (int)strlen(rstr); subtree = (char *)AdlAllocateHeap(len); - sprintf(subtree,"_%s_%s_%s", _opType, lstr, rstr); + snprintf_checked(subtree, len, "_%s_%s_%s", _opType, lstr, rstr); // Hash the subtree string in _internalOps; if a name exists, use it iop = (char *)_AD._internalOps[subtree]; // Else create a unique name, and add it to the hash table @@ -3898,8 +3900,9 @@ void MatchRule::matchrule_swap_commutative_op(const char* instr_ident, int count MatchRule* clone = new MatchRule(_AD, this); // Swap operands of commutative operation ((MatchNode*)clone)->swap_commutative_op(true, count); - char* buf = (char*) AdlAllocateHeap(strlen(instr_ident) + 4); - sprintf(buf, "%s_%d", instr_ident, match_rules_cnt++); + const size_t buf_size = strlen(instr_ident) + 4; + char* buf = (char*) AdlAllocateHeap(buf_size); + snprintf_checked(buf, buf_size, "%s_%d", instr_ident, match_rules_cnt++); clone->_result = buf; clone->_next = this->_next; diff --git a/src/hotspot/share/adlc/main.cpp b/src/hotspot/share/adlc/main.cpp index 6f6c1bc6e30c9..5ffd258dc96ae 100644 --- a/src/hotspot/share/adlc/main.cpp +++ b/src/hotspot/share/adlc/main.cpp @@ -467,7 +467,7 @@ static char *base_plus_suffix(const char* base, const char *suffix) int len = (int)strlen(base) + (int)strlen(suffix) + 1; char* fname = new char[len]; - sprintf(fname,"%s%s",base,suffix); + snprintf_checked(fname,len,"%s%s",base,suffix); return fname; } diff --git a/src/hotspot/share/adlc/output_c.cpp b/src/hotspot/share/adlc/output_c.cpp index 0a183ab260273..5c7d7ab7e3121 100644 --- a/src/hotspot/share/adlc/output_c.cpp +++ b/src/hotspot/share/adlc/output_c.cpp @@ -26,6 +26,8 @@ #include "adlc.hpp" +#define remaining_buflen(buffer, position) (sizeof(buffer) - (position - buffer)) + // Utilities to characterize effect statements static bool is_def(int usedef) { switch(usedef) { @@ -35,6 +37,16 @@ static bool is_def(int usedef) { return false; } +int snprintf_checked(char* buf, size_t len, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int result = vsnprintf(buf, len, fmt, args); + va_end(args); + assert(result >= 0, "snprintf error"); + assert(static_cast(result) < len, "snprintf truncated"); + return result; +} + // Define an array containing the machine register names, strings. static void defineRegNames(FILE *fp, RegisterForm *registers) { if (registers) { @@ -197,7 +209,8 @@ static int pipeline_reads_initializer(FILE *fp_cpp, NameList &pipeline_reads, Pi return -1; } - char *operand_stages = new char [templen]; + const size_t operand_stages_size = templen; + char *operand_stages = new char [operand_stages_size]; operand_stages[0] = 0; int i = 0; templen = 0; @@ -211,7 +224,7 @@ static int pipeline_reads_initializer(FILE *fp_cpp, NameList &pipeline_reads, Pi while ( (paramname = pipeclass->_parameters.iter()) != NULL ) { const PipeClassOperandForm *tmppipeopnd = (const PipeClassOperandForm *)pipeclass->_localUsage[paramname]; - templen += sprintf(&operand_stages[templen], " stage_%s%c\n", + templen += snprintf_checked(&operand_stages[templen], operand_stages_size - templen, " stage_%s%c\n", tmppipeopnd ? tmppipeopnd->_stage : "undefined", (++i < paramcount ? ',' : ' ') ); } @@ -278,6 +291,7 @@ static int pipeline_res_stages_initializer( int templen = 1 + commentlen + pipeline->_rescount * (max_stage + 14); // Allocate space for the resource list + const size_t resource_stages_size = templen; char * resource_stages = new char [templen]; templen = 0; @@ -285,7 +299,7 @@ static int pipeline_res_stages_initializer( const char * const resname = res_stages[i] == 0 ? "undefined" : pipeline->_stages.name(res_stages[i]-1); - templen += sprintf(&resource_stages[templen], " stage_%s%-*s // %s\n", + templen += snprintf_checked(&resource_stages[templen], resource_stages_size - templen, " stage_%s%-*s // %s\n", resname, max_stage - (int)strlen(resname) + 1, (i < pipeline->_rescount-1) ? "," : "", pipeline->_reslist.name(i)); @@ -344,7 +358,7 @@ static int pipeline_res_cycles_initializer( for (i = 0; i < pipeline->_rescount; i++) { if (max_cycles < res_cycles[i]) max_cycles = res_cycles[i]; - templen = sprintf(temp, "%d", res_cycles[i]); + templen = snprintf_checked(temp, sizeof(temp), "%d", res_cycles[i]); if (cyclelen < templen) cyclelen = templen; commentlen += (int)strlen(pipeline->_reslist.name(i)); @@ -353,12 +367,13 @@ static int pipeline_res_cycles_initializer( templen = 1 + commentlen + (cyclelen + 8) * pipeline->_rescount; // Allocate space for the resource list - char * resource_cycles = new char [templen]; + const size_t resource_cycles_size = templen; + char * resource_cycles = new char [resource_cycles_size]; templen = 0; for (i = 0; i < pipeline->_rescount; i++) { - templen += sprintf(&resource_cycles[templen], " %*d%c // %s\n", + templen += snprintf_checked(&resource_cycles[templen], resource_cycles_size - templen, " %*d%c // %s\n", cyclelen, res_cycles[i], (i < pipeline->_rescount-1) ? ',' : ' ', pipeline->_reslist.name(i)); } @@ -431,7 +446,8 @@ static int pipeline_res_mask_initializer( (cyclemasksize * 12) + masklen + (cycledigit * 2) + 30) * element_count; // Allocate space for the resource list - char * resource_mask = new char [templen]; + const size_t resource_mask_size = templen; + char * resource_mask = new char [resource_mask_size]; char * last_comma = NULL; templen = 0; @@ -456,7 +472,7 @@ static int pipeline_res_mask_initializer( } int formatlen = - sprintf(&resource_mask[templen], " %s(0x%0*x, %*d, %*d, %s %s(", + snprintf_checked(&resource_mask[templen], resource_mask_size - templen, " %s(0x%0*x, %*d, %*d, %s %s(", pipeline_use_element, masklen, used_mask, cycledigit, lb, cycledigit, ub, @@ -496,7 +512,7 @@ static int pipeline_res_mask_initializer( for (j = cyclemasksize-1; j >= 0; j--) { formatlen = - sprintf(&resource_mask[templen], "0x%08x%s", res_mask[j], j > 0 ? ", " : ""); + snprintf_checked(&resource_mask[templen], resource_mask_size - templen, "0x%08x%s", res_mask[j], j > 0 ? ", " : ""); templen += formatlen; } @@ -527,9 +543,8 @@ static int pipeline_res_mask_initializer( // "0x012345678, 0x012345678, 4294967295" char* args = new char [36 + 1]; - int printed = sprintf(args, "0x%x, 0x%x, %u", - resources_used, resources_used_exclusively, element_count); - assert(printed <= 36, "overflow"); + snprintf_checked(args, 36 + 1, "0x%x, 0x%x, %u", + resources_used, resources_used_exclusively, element_count); pipeline_res_args.addName(args); } @@ -1066,9 +1081,9 @@ static void build_instruction_index_mapping( FILE *fp, FormDict &globals, PeepMa InstructForm *inst = globals[inst_name]->is_instruction(); if( inst != NULL ) { char inst_prefix[] = "instXXXX_"; - sprintf(inst_prefix, "inst%d_", inst_position); + snprintf_checked(inst_prefix, sizeof(inst_prefix), "inst%d_", inst_position); char receiver[] = "instXXXX->"; - sprintf(receiver, "inst%d->", inst_position); + snprintf_checked(receiver, sizeof(receiver), "inst%d->", inst_position); inst->index_temps( fp, globals, inst_prefix, receiver ); } } @@ -1162,7 +1177,7 @@ static void check_peepconstraints(FILE *fp, FormDict &globals, PeepMatch *pmatch char left_reg_index[] = ",inst4294967295_idx4294967295"; if( left_op_index != 0 ) { // Must have index into operands - sprintf(left_reg_index,",inst%u_idx%u", (unsigned)left_index, (unsigned)left_op_index); + snprintf_checked(left_reg_index, sizeof(left_reg_index), ",inst%u_idx%u", (unsigned)left_index, (unsigned)left_op_index); } else { strcpy(left_reg_index, ""); } @@ -1174,7 +1189,7 @@ static void check_peepconstraints(FILE *fp, FormDict &globals, PeepMatch *pmatch char right_reg_index[] = ",inst4294967295_idx4294967295"; if( right_op_index != 0 ) { // Must have index into operands - sprintf(right_reg_index,",inst%u_idx%u", (unsigned)right_index, (unsigned)right_op_index); + snprintf_checked(right_reg_index, sizeof(right_reg_index), ",inst%u_idx%u", (unsigned)right_index, (unsigned)right_op_index); } else { strcpy(right_reg_index, ""); } @@ -2516,19 +2531,19 @@ void ArchDesc::define_postalloc_expand(FILE *fp, InstructForm &inst) { const char* arg_name = ins_encode->rep_var_name(inst, param_no); int idx = inst.operand_position_format(arg_name); if (strcmp(arg_name, "constanttablebase") == 0) { - ib += sprintf(ib, " unsigned idx_%-5s = mach_constant_base_node_input(); \t// %s, \t%s\n", + ib += snprintf_checked(ib, remaining_buflen(idxbuf, ib), " unsigned idx_%-5s = mach_constant_base_node_input(); \t// %s, \t%s\n", name, type, arg_name); - nb += sprintf(nb, " Node *n_%-7s = lookup(idx_%s);\n", name, name); + nb += snprintf_checked(nb, remaining_buflen(nbuf, nb), " Node *n_%-7s = lookup(idx_%s);\n", name, name); // There is no operand for the constanttablebase. } else if (inst.is_noninput_operand(idx)) { globalAD->syntax_err(inst._linenum, "In %s: you can not pass the non-input %s to a postalloc expand encoding.\n", inst._ident, arg_name); } else { - ib += sprintf(ib, " unsigned idx_%-5s = idx%d; \t// %s, \t%s\n", + ib += snprintf_checked(ib, remaining_buflen(idxbuf, ib), " unsigned idx_%-5s = idx%d; \t// %s, \t%s\n", name, idx, type, arg_name); - nb += sprintf(nb, " Node *n_%-7s = lookup(idx_%s);\n", name, name); - ob += sprintf(ob, " %sOper *op_%s = (%sOper *)opnd_array(%d);\n", type, name, type, idx); + nb += snprintf_checked(nb, remaining_buflen(nbuf, nb), " Node *n_%-7s = lookup(idx_%s);\n", name, name); + ob += snprintf_checked(ob, remaining_buflen(opbuf, ob), " %sOper *op_%s = (%sOper *)opnd_array(%d);\n", type, name, type, idx); } param_no++; } diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index 0012152d48db3..2d219121cc7dd 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -135,10 +135,11 @@ CodeBuffer::~CodeBuffer() { // Previous incarnations of this buffer are held live, so that internal // addresses constructed before expansions will not be confused. cb->free_blob(); + } + if (_overflow_arena != nullptr) { // free any overflow storage - delete cb->_overflow_arena; + delete _overflow_arena; } - // Claim is that stack allocation ensures resources are cleaned up. // This is resource clean up, let's hope that all were properly copied out. NOT_PRODUCT(free_strings();) @@ -940,8 +941,6 @@ void CodeBuffer::take_over_code_from(CodeBuffer* cb) { CodeSection* this_sect = code_section(n); this_sect->take_over_code_from(cb_sect); } - _overflow_arena = cb->_overflow_arena; - cb->_overflow_arena = NULL; // Make sure the old cb won't try to use it or free it. DEBUG_ONLY(cb->_blob = (BufferBlob*)badAddress); } diff --git a/src/hotspot/share/asm/register.hpp b/src/hotspot/share/asm/register.hpp index 66f58d5295382..f57dc09437eb7 100644 --- a/src/hotspot/share/asm/register.hpp +++ b/src/hotspot/share/asm/register.hpp @@ -99,263 +99,17 @@ const type name = ((type)name##_##type##EnumValue) // Debugging support -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b -) { - assert( - a != b, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT "", p2i(a), p2i(b) - ); -} - - -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b, - AbstractRegister c -) { - assert( - a != b && a != c - && b != c, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT - ", c=" INTPTR_FORMAT "", - p2i(a), p2i(b), p2i(c) - ); -} - - -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b, - AbstractRegister c, - AbstractRegister d -) { - assert( - a != b && a != c && a != d - && b != c && b != d - && c != d, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT - ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT "", - p2i(a), p2i(b), p2i(c), p2i(d) - ); -} - - -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b, - AbstractRegister c, - AbstractRegister d, - AbstractRegister e -) { - assert( - a != b && a != c && a != d && a != e - && b != c && b != d && b != e - && c != d && c != e - && d != e, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT - ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT "", - p2i(a), p2i(b), p2i(c), p2i(d), p2i(e) - ); -} - - -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b, - AbstractRegister c, - AbstractRegister d, - AbstractRegister e, - AbstractRegister f -) { - assert( - a != b && a != c && a != d && a != e && a != f - && b != c && b != d && b != e && b != f - && c != d && c != e && c != f - && d != e && d != f - && e != f, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT - ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT - ", f=" INTPTR_FORMAT "", - p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f) - ); -} - - -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b, - AbstractRegister c, - AbstractRegister d, - AbstractRegister e, - AbstractRegister f, - AbstractRegister g -) { - assert( - a != b && a != c && a != d && a != e && a != f && a != g - && b != c && b != d && b != e && b != f && b != g - && c != d && c != e && c != f && c != g - && d != e && d != f && d != g - && e != f && e != g - && f != g, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT - ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT - ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT "", - p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g) - ); -} - - -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b, - AbstractRegister c, - AbstractRegister d, - AbstractRegister e, - AbstractRegister f, - AbstractRegister g, - AbstractRegister h -) { - assert( - a != b && a != c && a != d && a != e && a != f && a != g && a != h - && b != c && b != d && b != e && b != f && b != g && b != h - && c != d && c != e && c != f && c != g && c != h - && d != e && d != f && d != g && d != h - && e != f && e != g && e != h - && f != g && f != h - && g != h, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT - ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT - ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT ", h=" INTPTR_FORMAT "", - p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g), p2i(h) - ); -} - - -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b, - AbstractRegister c, - AbstractRegister d, - AbstractRegister e, - AbstractRegister f, - AbstractRegister g, - AbstractRegister h, - AbstractRegister i -) { - assert( - a != b && a != c && a != d && a != e && a != f && a != g && a != h && a != i - && b != c && b != d && b != e && b != f && b != g && b != h && b != i - && c != d && c != e && c != f && c != g && c != h && c != i - && d != e && d != f && d != g && d != h && d != i - && e != f && e != g && e != h && e != i - && f != g && f != h && f != i - && g != h && g != i - && h != i, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT - ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT - ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT ", h=" INTPTR_FORMAT - ", i=" INTPTR_FORMAT "", - p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g), p2i(h), p2i(i) - ); -} - -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b, - AbstractRegister c, - AbstractRegister d, - AbstractRegister e, - AbstractRegister f, - AbstractRegister g, - AbstractRegister h, - AbstractRegister i, - AbstractRegister j -) { - assert( - a != b && a != c && a != d && a != e && a != f && a != g && a != h && a != i && a != j - && b != c && b != d && b != e && b != f && b != g && b != h && b != i && b != j - && c != d && c != e && c != f && c != g && c != h && c != i && c != j - && d != e && d != f && d != g && d != h && d != i && d != j - && e != f && e != g && e != h && e != i && e != j - && f != g && f != h && f != i && f != j - && g != h && g != i && g != j - && h != i && h != j - && i != j, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT - ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT - ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT ", h=" INTPTR_FORMAT - ", i=" INTPTR_FORMAT ", j=" INTPTR_FORMAT "", - p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g), p2i(h), p2i(i), p2i(j) - ); -} - -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b, - AbstractRegister c, - AbstractRegister d, - AbstractRegister e, - AbstractRegister f, - AbstractRegister g, - AbstractRegister h, - AbstractRegister i, - AbstractRegister j, - AbstractRegister k -) { - assert( - a != b && a != c && a != d && a != e && a != f && a != g && a != h && a != i && a != j && a !=k - && b != c && b != d && b != e && b != f && b != g && b != h && b != i && b != j && b !=k - && c != d && c != e && c != f && c != g && c != h && c != i && c != j && c !=k - && d != e && d != f && d != g && d != h && d != i && d != j && d !=k - && e != f && e != g && e != h && e != i && e != j && e !=k - && f != g && f != h && f != i && f != j && f !=k - && g != h && g != i && g != j && g !=k - && h != i && h != j && h !=k - && i != j && i !=k - && j !=k, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT - ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT - ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT ", h=" INTPTR_FORMAT - ", i=" INTPTR_FORMAT ", j=" INTPTR_FORMAT ", k=" INTPTR_FORMAT "", - p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g), p2i(h), p2i(i), p2i(j), p2i(k) - ); -} - -inline void assert_different_registers( - AbstractRegister a, - AbstractRegister b, - AbstractRegister c, - AbstractRegister d, - AbstractRegister e, - AbstractRegister f, - AbstractRegister g, - AbstractRegister h, - AbstractRegister i, - AbstractRegister j, - AbstractRegister k, - AbstractRegister l -) { - assert( - a != b && a != c && a != d && a != e && a != f && a != g && a != h && a != i && a != j && a !=k && a !=l - && b != c && b != d && b != e && b != f && b != g && b != h && b != i && b != j && b !=k && b !=l - && c != d && c != e && c != f && c != g && c != h && c != i && c != j && c !=k && c !=l - && d != e && d != f && d != g && d != h && d != i && d != j && d !=k && d !=l - && e != f && e != g && e != h && e != i && e != j && e !=k && e !=l - && f != g && f != h && f != i && f != j && f !=k && f !=l - && g != h && g != i && g != j && g !=k && g !=l - && h != i && h != j && h !=k && h !=l - && i != j && i !=k && i !=l - && j !=k && j !=l - && k !=l, - "registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT - ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT - ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT ", h=" INTPTR_FORMAT - ", i=" INTPTR_FORMAT ", j=" INTPTR_FORMAT ", k=" INTPTR_FORMAT - ", l=" INTPTR_FORMAT "", - p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g), p2i(h), p2i(i), p2i(j), p2i(k), p2i(l) - ); +template +inline void assert_different_registers(R first_register, Rx... more_registers) { +#ifdef ASSERT + const R regs[] = { first_register, more_registers... }; + // Verify there are no equal entries. + for (size_t i = 0; i < ARRAY_SIZE(regs) - 1; ++i) { + for (size_t j = i + 1; j < ARRAY_SIZE(regs); ++j) { + assert(regs[i] != regs[j], "Multiple uses of register: %s", regs[i]->name()); + } + } +#endif } #endif // SHARE_ASM_REGISTER_HPP diff --git a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp index 5a50060a3c164..f663aca00b42c 100644 --- a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp +++ b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -466,14 +466,14 @@ void RangeCheckEliminator::in_block_motion(BlockBegin *block, AccessIndexedList if (c) { jint value = c->type()->as_IntConstant()->value(); - if (value != min_jint) { - if (ao->op() == Bytecodes::_isub) { - value = -value; - } + if (ao->op() == Bytecodes::_iadd) { base = java_add(base, value); - last_integer = base; - last_instruction = other; + } else { + assert(ao->op() == Bytecodes::_isub, "unexpected bytecode"); + base = java_subtract(base, value); } + last_integer = base; + last_instruction = other; index = other; } else { break; diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index fb178432d8bbb..14cf71eb09ad3 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -657,7 +657,7 @@ JRT_ENTRY(void, Runtime1::throw_range_check_exception(JavaThread* current, int i const int len = 35; assert(len < strlen("Index %d out of bounds for length %d"), "Must allocate more space for message."); char message[2 * jintAsStringSize + len]; - sprintf(message, "Index %d out of bounds for length %d", index, a->length()); + os::snprintf_checked(message, sizeof(message), "Index %d out of bounds for length %d", index, a->length()); SharedRuntime::throw_and_post_jvmti_exception(current, vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), message); JRT_END @@ -665,7 +665,7 @@ JRT_END JRT_ENTRY(void, Runtime1::throw_index_exception(JavaThread* current, int index)) NOT_PRODUCT(_throw_index_exception_count++;) char message[16]; - sprintf(message, "%d", index); + os::snprintf_checked(message, sizeof(message), "%d", index); SharedRuntime::throw_and_post_jvmti_exception(current, vmSymbols::java_lang_IndexOutOfBoundsException(), message); JRT_END diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 242b57ee57965..a045b18e41ce5 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -158,7 +158,7 @@ template static void get_header_version(char (&header_version) [N]) { strncpy(header_version, vm_version, JVM_IDENT_MAX-9); // Append the hash code as eight hex digits. - sprintf(&header_version[JVM_IDENT_MAX-9], "%08x", hash); + os::snprintf_checked(&header_version[JVM_IDENT_MAX-9], 9, "%08x", hash); header_version[JVM_IDENT_MAX-1] = 0; // Null terminate. } @@ -1420,8 +1420,7 @@ size_t FileMapInfo::write_archive_heap_regions(GrowableArray *heap_me void FileMapInfo::write_bytes(const void* buffer, size_t nbytes) { assert(_file_open, "must be"); - size_t n = os::write(_fd, buffer, (unsigned int)nbytes); - if (n != nbytes) { + if (!os::write(_fd, buffer, nbytes)) { // If the shared archive is corrupted, close it and remove it. close(); remove(_full_path); diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 052d013368a5a..eb52819e67e01 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -5803,8 +5803,8 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream, Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_NoClassDefFoundError(), "%s (wrong name: %s)", - class_name_in_cp->as_C_string(), - _class_name->as_C_string() + _class_name->as_C_string(), + class_name_in_cp->as_C_string() ); return; } else { diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index 73d17a3dd6b32..006e9c9602f20 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -552,6 +552,21 @@ void ClassLoaderData::unload() { // after erroneous classes are released. classes_do(InstanceKlass::unload_class); + // Method::clear_jmethod_ids only sets the jmethod_ids to NULL without + // releasing the memory for related JNIMethodBlocks and JNIMethodBlockNodes. + // This is done intentionally because native code (e.g. JVMTI agent) holding + // jmethod_ids may access them after the associated classes and class loader + // are unloaded. The Java Native Interface Specification says "method ID + // does not prevent the VM from unloading the class from which the ID has + // been derived. After the class is unloaded, the method or field ID becomes + // invalid". In real world usages, the native code may rely on jmethod_ids + // being NULL after class unloading. Hence, it is unsafe to free the memory + // from the VM side without knowing when native code is going to stop using + // them. + if (_jmethod_ids != NULL) { + Method::clear_jmethod_ids(this); + } + // Clean up global class iterator for compiler ClassLoaderDataGraph::adjust_saved_class(this); } @@ -696,20 +711,7 @@ ClassLoaderData::~ClassLoaderData() { _metaspace = NULL; delete m; } - // Method::clear_jmethod_ids only sets the jmethod_ids to NULL without - // releasing the memory for related JNIMethodBlocks and JNIMethodBlockNodes. - // This is done intentionally because native code (e.g. JVMTI agent) holding - // jmethod_ids may access them after the associated classes and class loader - // are unloaded. The Java Native Interface Specification says "method ID - // does not prevent the VM from unloading the class from which the ID has - // been derived. After the class is unloaded, the method or field ID becomes - // invalid". In real world usages, the native code may rely on jmethod_ids - // being NULL after class unloading. Hence, it is unsafe to free the memory - // from the VM side without knowing when native code is going to stop using - // them. - if (_jmethod_ids != NULL) { - Method::clear_jmethod_ids(this); - } + // Delete lock delete _metaspace_lock; diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp index 72a1e78838f2e..c1fb1d68f4b1b 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp @@ -186,7 +186,7 @@ void ClassLoaderDataGraph::walk_metadata_and_clean_metaspaces() { // on the stack or in the code cache, so we only have to repeat the full walk if // they were found at that time. // TODO: have redefinition clean old methods out of the code cache. They still exist in some places. - bool walk_all_metadata = InstanceKlass::has_previous_versions_and_reset(); + bool walk_all_metadata = InstanceKlass::should_clean_previous_versions_and_reset(); MetadataOnStackMark md_on_stack(walk_all_metadata, /*redefinition_walk*/false); clean_deallocate_lists(walk_all_metadata); diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp b/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp index e5b51ac277cc3..e4b10f95656b9 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp @@ -73,7 +73,7 @@ bool ClassLoaderDataGraph::should_clean_metaspaces_and_reset() { // Only clean metaspaces after full GC. bool do_cleaning = _safepoint_cleanup_needed; #if INCLUDE_JVMTI - do_cleaning = do_cleaning && (_should_clean_deallocate_lists || InstanceKlass::has_previous_versions()); + do_cleaning = do_cleaning && (_should_clean_deallocate_lists || InstanceKlass::should_clean_previous_versions()); #else do_cleaning = do_cleaning && _should_clean_deallocate_lists; #endif diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index bf817989dc18e..cac737125f1db 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -2375,17 +2375,18 @@ static void print_stack_element_to_stream(outputStream* st, Handle mirror, int m } // Allocate temporary buffer with extra space for formatting and line number - char* buf = NEW_RESOURCE_ARRAY(char, buf_len + 64); + const size_t buf_size = buf_len + 64; + char* buf = NEW_RESOURCE_ARRAY(char, buf_size); // Print stack trace line in buffer - sprintf(buf, "\tat %s.%s(", klass_name, method_name); + size_t buf_off = os::snprintf_checked(buf, buf_size, "\tat %s.%s(", klass_name, method_name); // Print module information if (module_name != NULL) { if (module_version != NULL) { - sprintf(buf + (int)strlen(buf), "%s@%s/", module_name, module_version); + buf_off += os::snprintf_checked(buf + buf_off, buf_size - buf_off, "%s@%s/", module_name, module_version); } else { - sprintf(buf + (int)strlen(buf), "%s/", module_name); + buf_off += os::snprintf_checked(buf + buf_off, buf_size - buf_off, "%s/", module_name); } } @@ -2400,17 +2401,17 @@ static void print_stack_element_to_stream(outputStream* st, Handle mirror, int m } else { if (source_file_name != NULL && (line_number != -1)) { // Sourcename and linenumber - sprintf(buf + (int)strlen(buf), "%s:%d)", source_file_name, line_number); + buf_off += os::snprintf_checked(buf + buf_off, buf_size - buf_off, "%s:%d)", source_file_name, line_number); } else if (source_file_name != NULL) { // Just sourcename - sprintf(buf + (int)strlen(buf), "%s)", source_file_name); + buf_off += os::snprintf_checked(buf + buf_off, buf_size - buf_off, "%s)", source_file_name); } else { // Neither sourcename nor linenumber - sprintf(buf + (int)strlen(buf), "Unknown Source)"); + buf_off += os::snprintf_checked(buf + buf_off, buf_size - buf_off, "Unknown Source)"); } CompiledMethod* nm = method->code(); if (WizardMode && nm != NULL) { - sprintf(buf + (int)strlen(buf), "(nmethod " INTPTR_FORMAT ")", (intptr_t)nm); + os::snprintf_checked(buf + buf_off, buf_size - buf_off, "(nmethod " INTPTR_FORMAT ")", (intptr_t)nm); } } } diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index 3e3f949ae8a67..8df69fd649662 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,6 +56,31 @@ const size_t ON_STACK_BUFFER_LENGTH = 128; // -------------------------------------------------------------------------- +Symbol* volatile TempSymbolCleanupDelayer::_queue[QueueSize] = {}; +volatile uint TempSymbolCleanupDelayer::_index = 0; + +// Keep this symbol alive for some time to allow for reuse. +// Temp symbols for the same string can often be created in quick succession, +// and this queue allows them to be reused instead of churning. +void TempSymbolCleanupDelayer::delay_cleanup(Symbol* sym) { + assert(sym != nullptr, "precondition"); + sym->increment_refcount(); + uint i = Atomic::add(&_index, 1u) % QueueSize; + Symbol* old = Atomic::xchg(&_queue[i], sym); + if (old != nullptr) { + old->decrement_refcount(); + } +} + +void TempSymbolCleanupDelayer::drain_queue() { + for (uint i = 0; i < QueueSize; i++) { + Symbol* sym = Atomic::xchg(&_queue[i], (Symbol*) nullptr); + if (sym != nullptr) { + sym->decrement_refcount(); + } + } +} + inline bool symbol_equals_compact_hashtable_entry(Symbol* value, const char* key, int len) { if (value->equals(key, len)) { return true; @@ -338,7 +363,23 @@ Symbol* SymbolTable::lookup_common(const char* name, return sym; } +// Symbols should represent entities from the constant pool that are +// limited to <64K in length, but usage errors creep in allowing Symbols +// to be used for arbitrary strings. For debug builds we will assert if +// a string is too long, whereas product builds will truncate it. +static int check_length(const char* name, int len) { + assert(len <= Symbol::max_length(), + "String length %d exceeds the maximum Symbol length of %d", len, Symbol::max_length()); + if (len > Symbol::max_length()) { + warning("A string \"%.80s ... %.80s\" exceeds the maximum Symbol " + "length of %d and has been truncated", name, (name + len - 80), Symbol::max_length()); + len = Symbol::max_length(); + } + return len; +} + Symbol* SymbolTable::new_symbol(const char* name, int len) { + len = check_length(name, len); unsigned int hash = hash_symbol(name, len, _alt_hash); Symbol* sym = lookup_common(name, len, hash); if (sym == NULL) { @@ -472,6 +513,7 @@ void SymbolTable::new_symbols(ClassLoaderData* loader_data, const constantPoolHa for (int i = 0; i < names_count; i++) { const char *name = names[i]; int len = lengths[i]; + assert(len <= Symbol::max_length(), "must be - these come from the constant pool"); unsigned int hash = hashValues[i]; assert(lookup_shared(name, len, hash) == NULL, "must have checked already"); Symbol* sym = do_add_if_needed(name, len, hash, c_heap); @@ -481,6 +523,7 @@ void SymbolTable::new_symbols(ClassLoaderData* loader_data, const constantPoolHa } Symbol* SymbolTable::do_add_if_needed(const char* name, int len, uintx hash, bool heap) { + assert(len <= Symbol::max_length(), "caller should have ensured this"); SymbolTableLookup lookup(name, len, hash); SymbolTableGet stg; bool clean_hint = false; @@ -515,7 +558,7 @@ Symbol* SymbolTable::do_add_if_needed(const char* name, int len, uintx hash, boo Symbol* SymbolTable::new_permanent_symbol(const char* name) { unsigned int hash = 0; - int len = (int)strlen(name); + int len = check_length(name, (int)strlen(name)); Symbol* sym = SymbolTable::lookup_only(name, len, hash); if (sym == NULL) { sym = do_add_if_needed(name, len, hash, false); diff --git a/src/hotspot/share/classfile/symbolTable.hpp b/src/hotspot/share/classfile/symbolTable.hpp index a0b7287f936ab..8441a52713e27 100644 --- a/src/hotspot/share/classfile/symbolTable.hpp +++ b/src/hotspot/share/classfile/symbolTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,16 @@ class JavaThread; template class GrowableArray; +class TempSymbolCleanupDelayer : AllStatic { + static Symbol* volatile _queue[]; + static volatile uint _index; + +public: + static const uint QueueSize = 128; + static void delay_cleanup(Symbol* s); + static void drain_queue(); +}; + // TempNewSymbol acts as a handle class in a handle/body idiom and is // responsible for proper resource management of the body (which is a Symbol*). // The body is resource managed by a reference counting scheme. @@ -54,7 +64,14 @@ class TempNewSymbol : public StackObj { // Conversion from a Symbol* to a TempNewSymbol. // Does not increment the current reference count. - TempNewSymbol(Symbol *s) : _temp(s) {} + TempNewSymbol(Symbol *s) : _temp(s) { + // Delay cleanup for temp symbols. Refcount is incremented while in + // queue. But don't requeue existing entries, or entries that are held + // elsewhere - it's a waste of effort. + if (s != nullptr && s->refcount() == 1) { + TempSymbolCleanupDelayer::delay_cleanup(s); + } + } // Copy constructor increments reference count. TempNewSymbol(const TempNewSymbol& rhs) : _temp(rhs._temp) { diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 590cce18ba2c8..5ad53e9eca198 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -32,6 +32,7 @@ #include "classfile/stackMapTableFormat.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/verifier.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" @@ -211,6 +212,12 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { exception_name == vmSymbols::java_lang_ClassFormatError())) { log_info(verification)("Fail over class verification to old verifier for: %s", klass->external_name()); log_info(class, init)("Fail over class verification to old verifier for: %s", klass->external_name()); + // Exclude any classes that fail over during dynamic dumping + if (CDS_ONLY(DynamicDumpSharedSpaces) NOT_CDS(false)) { + ResourceMark rm; + log_warning(cds)("Skipping %s: Failed over class verification while dynamic dumping", klass->name()->as_C_string()); + SystemDictionaryShared::set_excluded(klass); + } message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len); exception_message = message_buffer; exception_name = inference_verify( diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index bca8b7dc9d046..f4753fd6cfffa 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -101,22 +101,37 @@ class CodeBlob_sizes { scopes_pcs_size = 0; } - int total() { return total_size; } - bool is_empty() { return count == 0; } - - void print(const char* title) { - tty->print_cr(" #%d %s = %dK (hdr %d%%, loc %d%%, code %d%%, stub %d%%, [oops %d%%, metadata %d%%, data %d%%, pcs %d%%])", - count, - title, - (int)(total() / K), - header_size * 100 / total_size, - relocation_size * 100 / total_size, - code_size * 100 / total_size, - stub_size * 100 / total_size, - scopes_oop_size * 100 / total_size, - scopes_metadata_size * 100 / total_size, - scopes_data_size * 100 / total_size, - scopes_pcs_size * 100 / total_size); + int total() const { return total_size; } + bool is_empty() const { return count == 0; } + + void print(const char* title) const { + if (is_empty()) { + tty->print_cr(" #%d %s = %dK", + count, + title, + total() / (int)K); + } else { + tty->print_cr(" #%d %s = %dK (hdr %dK %d%%, loc %dK %d%%, code %dK %d%%, stub %dK %d%%, [oops %dK %d%%, metadata %dK %d%%, data %dK %d%%, pcs %dK %d%%])", + count, + title, + total() / (int)K, + header_size / (int)K, + header_size * 100 / total_size, + relocation_size / (int)K, + relocation_size * 100 / total_size, + code_size / (int)K, + code_size * 100 / total_size, + stub_size / (int)K, + stub_size * 100 / total_size, + scopes_oop_size / (int)K, + scopes_oop_size * 100 / total_size, + scopes_metadata_size / (int)K, + scopes_metadata_size * 100 / total_size, + scopes_data_size / (int)K, + scopes_data_size * 100 / total_size, + scopes_pcs_size / (int)K, + scopes_pcs_size * 100 / total_size); + } } void add(CodeBlob* cb) { @@ -1468,27 +1483,73 @@ void CodeCache::print() { #ifndef PRODUCT if (!Verbose) return; - CodeBlob_sizes live; - CodeBlob_sizes dead; + CodeBlob_sizes live[CompLevel_full_optimization + 1]; + CodeBlob_sizes dead[CompLevel_full_optimization + 1]; + CodeBlob_sizes runtimeStub; + CodeBlob_sizes uncommonTrapStub; + CodeBlob_sizes deoptimizationStub; + CodeBlob_sizes adapter; + CodeBlob_sizes bufferBlob; + CodeBlob_sizes other; FOR_ALL_ALLOCABLE_HEAPS(heap) { FOR_ALL_BLOBS(cb, *heap) { - if (!cb->is_alive()) { - dead.add(cb); + if (cb->is_nmethod()) { + const int level = cb->as_nmethod()->comp_level(); + assert(0 <= level && level <= CompLevel_full_optimization, "Invalid compilation level"); + if (!cb->is_alive()) { + dead[level].add(cb); + } else { + live[level].add(cb); + } + } else if (cb->is_runtime_stub()) { + runtimeStub.add(cb); + } else if (cb->is_deoptimization_stub()) { + deoptimizationStub.add(cb); + } else if (cb->is_uncommon_trap_stub()) { + uncommonTrapStub.add(cb); + } else if (cb->is_adapter_blob()) { + adapter.add(cb); + } else if (cb->is_buffer_blob()) { + bufferBlob.add(cb); } else { - live.add(cb); + other.add(cb); } } } - tty->print_cr("CodeCache:"); tty->print_cr("nmethod dependency checking time %fs", dependentCheckTime.seconds()); - if (!live.is_empty()) { - live.print("live"); - } - if (!dead.is_empty()) { - dead.print("dead"); + tty->print_cr("nmethod blobs per compilation level:"); + for (int i = 0; i <= CompLevel_full_optimization; i++) { + const char *level_name; + switch (i) { + case CompLevel_none: level_name = "none"; break; + case CompLevel_simple: level_name = "simple"; break; + case CompLevel_limited_profile: level_name = "limited profile"; break; + case CompLevel_full_profile: level_name = "full profile"; break; + case CompLevel_full_optimization: level_name = "full optimization"; break; + default: assert(false, "invalid compilation level"); + } + tty->print_cr("%s:", level_name); + live[i].print("live"); + dead[i].print("dead"); + } + + struct { + const char* name; + const CodeBlob_sizes* sizes; + } non_nmethod_blobs[] = { + { "runtime", &runtimeStub }, + { "uncommon trap", &uncommonTrapStub }, + { "deoptimization", &deoptimizationStub }, + { "adapter", &adapter }, + { "buffer blob", &bufferBlob }, + { "other", &other }, + }; + tty->print_cr("Non-nmethod blobs:"); + for (auto& blob: non_nmethod_blobs) { + blob.sizes->print(blob.name); } if (WizardMode) { diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp index 306280dfc432d..bb597e5010612 100644 --- a/src/hotspot/share/code/dependencies.cpp +++ b/src/hotspot/share/code/dependencies.cpp @@ -769,7 +769,8 @@ void Dependencies::write_dependency_to(xmlStream* xtty, xtty->object("x", arg.metadata_value()); } } else { - char xn[12]; sprintf(xn, "x%d", j); + char xn[12]; + os::snprintf_checked(xn, sizeof(xn), "x%d", j); if (arg.is_oop()) { xtty->object(xn, Handle(thread, arg.oop_value())); } else { diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index eef8e6b4bbb16..f5861d1f3edef 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1550,7 +1550,7 @@ void nmethod::flush() { assert_locked_or_safepoint(CodeCache_lock); // completely deallocate this method - Events::log(JavaThread::current(), "flushing nmethod " INTPTR_FORMAT, p2i(this)); + Events::log_nmethod_flush(Thread::current(), "flushing %s nmethod " INTPTR_FORMAT, is_osr_method() ? "osr" : "", p2i(this)); if (PrintMethodFlushing) { tty->print_cr("*flushing %s nmethod %3d/" INTPTR_FORMAT ". Live blobs:" UINT32_FORMAT "/Free CodeCache:" SIZE_FORMAT "Kb", diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index 026915246ccc4..d40a01834158a 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -1011,7 +1011,7 @@ CompLevel CompilationPolicy::common(const methodHandle& method, CompLevel cur_le if (force_comp_at_level_simple(method)) { next_level = CompLevel_simple; } else { - if (is_trivial(method)) { + if (is_trivial(method) || method->is_native()) { next_level = CompilationModeFlag::disable_intermediate() ? CompLevel_full_optimization : CompLevel_simple; } else { switch(cur_level) { diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index db5f2b5e42f1a..f54308944e281 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -998,7 +998,7 @@ void CompileBroker::init_compiler_sweeper_threads() { // for JVMCI compiler which can create further ones on demand. JVMCI_ONLY(if (!UseJVMCICompiler || !UseDynamicNumberOfCompilerThreads || i == 0) {) // Create a name for our thread. - sprintf(name_buffer, "%s CompilerThread%d", _compilers[1]->name(), i); + os::snprintf_checked(name_buffer, sizeof(name_buffer), "%s CompilerThread%d", _compilers[1]->name(), i); Handle thread_oop = create_thread_oop(name_buffer, CHECK); thread_handle = JNIHandles::make_global(thread_oop); JVMCI_ONLY(}) @@ -1022,7 +1022,7 @@ void CompileBroker::init_compiler_sweeper_threads() { for (int i = 0; i < _c1_count; i++) { // Create a name for our thread. - sprintf(name_buffer, "C1 CompilerThread%d", i); + os::snprintf_checked(name_buffer, sizeof(name_buffer), "C1 CompilerThread%d", i); Handle thread_oop = create_thread_oop(name_buffer, CHECK); jobject thread_handle = JNIHandles::make_global(thread_oop); _compiler1_objects[i] = thread_handle; @@ -1095,7 +1095,7 @@ void CompileBroker::possibly_add_compiler_threads(JavaThread* THREAD) { // transitions if we bind them to new JavaThreads. if (!THREAD->can_call_java()) break; char name_buffer[256]; - sprintf(name_buffer, "%s CompilerThread%d", _compilers[1]->name(), i); + os::snprintf_checked(name_buffer, sizeof(name_buffer), "%s CompilerThread%d", _compilers[1]->name(), i); Handle thread_oop; { // We have to give up the lock temporarily for the Java calls. @@ -2731,7 +2731,7 @@ void CompileBroker::print_times(bool per_compiler, bool aggregate) { char tier_name[256]; for (int tier = CompLevel_simple; tier <= CompilationPolicy::highest_compile_level(); tier++) { CompilerStatistics* stats = &_stats_per_level[tier-1]; - sprintf(tier_name, "Tier%d", tier); + os::snprintf_checked(tier_name, sizeof(tier_name), "Tier%d", tier); print_times(tier_name, stats); } } diff --git a/src/hotspot/share/gc/g1/g1Allocator.cpp b/src/hotspot/share/gc/g1/g1Allocator.cpp index 1aca61234e589..9d045d4d90080 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.cpp +++ b/src/hotspot/share/gc/g1/g1Allocator.cpp @@ -192,11 +192,14 @@ size_t G1Allocator::unsafe_max_tlab_alloc() { uint node_index = current_node_index(); HeapRegion* hr = mutator_alloc_region(node_index)->get(); size_t max_tlab = _g1h->max_tlab_size() * wordSize; - if (hr == NULL) { + + if (hr == NULL || hr->free() < MinTLABSize) { + // The next TLAB allocation will most probably happen in a new region, + // therefore we can attempt to allocate the maximum allowed TLAB size. return max_tlab; - } else { - return clamp(hr->free(), MinTLABSize, max_tlab); } + + return MIN2(hr->free(), max_tlab); } size_t G1Allocator::used_in_alloc_regions() { @@ -292,6 +295,8 @@ G1PLABAllocator::G1PLABAllocator(G1Allocator* allocator) : for (uint node_index = 0; node_index < length; node_index++) { _alloc_buffers[state][node_index] = new PLAB(_g1h->desired_plab_sz(state)); } + _num_plab_fills[state] = 0; + _num_direct_allocations[state] = 0; } } @@ -324,6 +329,8 @@ HeapWord* G1PLABAllocator::allocate_direct_or_new_plab(G1HeapRegionAttr dest, PLAB* alloc_buf = alloc_buffer(dest, node_index); alloc_buf->retire(); + _num_plab_fills[dest.type()]++; + size_t actual_plab_size = 0; HeapWord* buf = _allocator->par_allocate_during_gc(dest, required_in_plab, @@ -332,7 +339,7 @@ HeapWord* G1PLABAllocator::allocate_direct_or_new_plab(G1HeapRegionAttr dest, node_index); assert(buf == NULL || ((actual_plab_size >= required_in_plab) && (actual_plab_size <= plab_word_size)), - "Requested at minimum " SIZE_FORMAT ", desired " SIZE_FORMAT " words, but got " SIZE_FORMAT " at " PTR_FORMAT, + "Requested at minimum %zu, desired %zu words, but got %zu at " PTR_FORMAT, required_in_plab, plab_word_size, actual_plab_size, p2i(buf)); if (buf != NULL) { @@ -340,7 +347,7 @@ HeapWord* G1PLABAllocator::allocate_direct_or_new_plab(G1HeapRegionAttr dest, HeapWord* const obj = alloc_buf->allocate(word_sz); assert(obj != NULL, "PLAB should have been big enough, tried to allocate " - SIZE_FORMAT " requiring " SIZE_FORMAT " PLAB size " SIZE_FORMAT, + "%zu requiring %zu PLAB size %zu", word_sz, required_in_plab, plab_word_size); return obj; } @@ -351,6 +358,7 @@ HeapWord* G1PLABAllocator::allocate_direct_or_new_plab(G1HeapRegionAttr dest, HeapWord* result = _allocator->par_allocate_during_gc(dest, word_sz, node_index); if (result != NULL) { _direct_allocated[dest.type()] += word_sz; + _num_direct_allocations[dest.type()]++; } return result; } @@ -368,8 +376,9 @@ void G1PLABAllocator::flush_and_retire_stats() { buf->flush_and_retire_stats(stats); } } + stats->add_num_plab_filled(_num_plab_fills[state]); stats->add_direct_allocated(_direct_allocated[state]); - _direct_allocated[state] = 0; + stats->add_num_direct_allocated(_num_direct_allocations[state]); } } diff --git a/src/hotspot/share/gc/g1/g1Allocator.hpp b/src/hotspot/share/gc/g1/g1Allocator.hpp index c9bf3015dfc44..16feef2e6b1be 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.hpp +++ b/src/hotspot/share/gc/g1/g1Allocator.hpp @@ -161,6 +161,10 @@ class G1PLABAllocator : public CHeapObj { // Number of words allocated directly (not counting PLAB allocation). size_t _direct_allocated[G1HeapRegionAttr::Num]; + // Number of PLAB refills experienced so far. + size_t _num_plab_fills[G1HeapRegionAttr::Num]; + size_t _num_direct_allocations[G1HeapRegionAttr::Num]; + void flush_and_retire_stats(); inline PLAB* alloc_buffer(G1HeapRegionAttr dest, uint node_index) const; inline PLAB* alloc_buffer(region_type_t dest, uint node_index) const; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index eb3586231bda6..8acbaae9bb57d 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -2545,7 +2545,8 @@ G1HeapSummary G1CollectedHeap::create_g1_heap_summary() { G1EvacSummary G1CollectedHeap::create_g1_evac_summary(G1EvacStats* stats) { return G1EvacSummary(stats->allocated(), stats->wasted(), stats->undo_wasted(), stats->unused(), stats->used(), stats->region_end_waste(), - stats->regions_filled(), stats->direct_allocated(), + stats->regions_filled(), stats->num_plab_filled(), + stats->direct_allocated(), stats->num_direct_allocated(), stats->failure_used(), stats->failure_waste()); } diff --git a/src/hotspot/share/gc/g1/g1EvacStats.cpp b/src/hotspot/share/gc/g1/g1EvacStats.cpp index f8acabf6be658..62d5cc3b257f6 100644 --- a/src/hotspot/share/gc/g1/g1EvacStats.cpp +++ b/src/hotspot/share/gc/g1/g1EvacStats.cpp @@ -33,15 +33,19 @@ void G1EvacStats::log_plab_allocation() { PLABStats::log_plab_allocation(); log_debug(gc, plab)("%s other allocation: " - "region end waste: " SIZE_FORMAT "B, " + "region end waste: %zuB, " "regions filled: %u, " - "direct allocated: " SIZE_FORMAT "B, " - "failure used: " SIZE_FORMAT "B, " - "failure wasted: " SIZE_FORMAT "B", + "num plab filled: %zu, " + "direct allocated: %zuB, " + "num direct allocated: %zu, " + "failure used: %zuB, " + "failure wasted: %zuB", _description, _region_end_waste * HeapWordSize, _regions_filled, + _num_plab_filled, _direct_allocated * HeapWordSize, + _num_direct_allocated, _failure_used * HeapWordSize, _failure_waste * HeapWordSize); } @@ -94,7 +98,9 @@ G1EvacStats::G1EvacStats(const char* description, size_t default_per_thread_plab PLABStats(description, default_per_thread_plab_size, default_per_thread_plab_size * ParallelGCThreads, wt), _region_end_waste(0), _regions_filled(0), + _num_plab_filled(0), _direct_allocated(0), + _num_direct_allocated(0), _failure_used(0), _failure_waste(0) { } diff --git a/src/hotspot/share/gc/g1/g1EvacStats.hpp b/src/hotspot/share/gc/g1/g1EvacStats.hpp index 0bbd1d9661f91..c3ecf98efabfa 100644 --- a/src/hotspot/share/gc/g1/g1EvacStats.hpp +++ b/src/hotspot/share/gc/g1/g1EvacStats.hpp @@ -32,7 +32,9 @@ class G1EvacStats : public PLABStats { private: size_t _region_end_waste; // Number of words wasted due to skipping to the next region. uint _regions_filled; // Number of regions filled completely. + size_t _num_plab_filled; // Number of PLABs filled and retired. size_t _direct_allocated; // Number of words allocated directly into the regions. + size_t _num_direct_allocated; // Number of direct allocation attempts. // Number of words in live objects remaining in regions that ultimately suffered an // evacuation failure. This is used in the regions when the regions are made old regions. @@ -46,7 +48,9 @@ class G1EvacStats : public PLABStats { PLABStats::reset(); _region_end_waste = 0; _regions_filled = 0; + _num_plab_filled = 0; _direct_allocated = 0; + _num_direct_allocated = 0; _failure_used = 0; _failure_waste = 0; } @@ -61,15 +65,19 @@ class G1EvacStats : public PLABStats { ~G1EvacStats(); uint regions_filled() const { return _regions_filled; } + size_t num_plab_filled() const { return _num_plab_filled; } size_t region_end_waste() const { return _region_end_waste; } size_t direct_allocated() const { return _direct_allocated; } + size_t num_direct_allocated() const { return _num_direct_allocated; } // Amount of space in heapwords used in the failing regions when an evacuation failure happens. size_t failure_used() const { return _failure_used; } // Amount of space in heapwords wasted (unused) in the failing regions when an evacuation failure happens. size_t failure_waste() const { return _failure_waste; } + inline void add_num_plab_filled(size_t value); inline void add_direct_allocated(size_t value); + inline void add_num_direct_allocated(size_t value); inline void add_region_end_waste(size_t value); inline void add_failure_used_and_waste(size_t used, size_t waste); }; diff --git a/src/hotspot/share/gc/g1/g1EvacStats.inline.hpp b/src/hotspot/share/gc/g1/g1EvacStats.inline.hpp index 9ebc1dd57a885..4b4c6e104b7e3 100644 --- a/src/hotspot/share/gc/g1/g1EvacStats.inline.hpp +++ b/src/hotspot/share/gc/g1/g1EvacStats.inline.hpp @@ -33,6 +33,14 @@ inline void G1EvacStats::add_direct_allocated(size_t value) { Atomic::add(&_direct_allocated, value); } +inline void G1EvacStats::add_num_plab_filled(size_t value) { + Atomic::add(&_num_plab_filled, value); +} + +inline void G1EvacStats::add_num_direct_allocated(size_t value) { + Atomic::add(&_num_direct_allocated, value); +} + inline void G1EvacStats::add_region_end_waste(size_t value) { Atomic::add(&_region_end_waste, value); Atomic::inc(&_regions_filled); diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp index f97727c8bc888..e5817ca6f5c4a 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp @@ -169,22 +169,6 @@ void PSAdaptiveSizePolicy::major_collection_end(size_t amount_live, _major_timer.start(); } -// If the remaining free space in the old generation is less that -// that expected to be needed by the next collection, do a full -// collection now. -bool PSAdaptiveSizePolicy::should_full_GC(size_t old_free_in_bytes) { - - // A similar test is done in the scavenge's should_attempt_scavenge(). If - // this is changed, decide if that test should also be changed. - bool result = padded_average_promoted_in_bytes() > (float) old_free_in_bytes; - log_trace(gc, ergo)("%s after scavenge average_promoted " SIZE_FORMAT " padded_average_promoted " SIZE_FORMAT " free in old gen " SIZE_FORMAT, - result ? "Full" : "No full", - (size_t) average_promoted_in_bytes(), - (size_t) padded_average_promoted_in_bytes(), - old_free_in_bytes); - return result; -} - void PSAdaptiveSizePolicy::clear_generation_free_space_flags() { AdaptiveSizePolicy::clear_generation_free_space_flags(); diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp index c39514922fc6b..1b058e2dd2990 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp @@ -306,10 +306,6 @@ class PSAdaptiveSizePolicy : public AdaptiveSizePolicy { } float major_collection_slope() { return _major_collection_estimator->slope();} - // Given the amount of live data in the heap, should we - // perform a Full GC? - bool should_full_GC(size_t live_in_old_gen); - // Calculates optimal (free) space sizes for both the young and old // generations. Stores results in _eden_size and _promo_size. // Takes current used space in all generations as input, as well diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index c4011fff4e5f0..ef14ad53aa063 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -238,8 +238,7 @@ bool PSScavenge::invoke() { IsGCActiveMark mark; const bool scavenge_done = PSScavenge::invoke_no_policy(); - const bool need_full_gc = !scavenge_done || - policy->should_full_GC(heap->old_gen()->free_in_bytes()); + const bool need_full_gc = !scavenge_done; bool full_gc_done = false; if (UsePerfData) { @@ -739,8 +738,6 @@ bool PSScavenge::should_attempt_scavenge() { // Test to see if the scavenge will likely fail. PSAdaptiveSizePolicy* policy = heap->size_policy(); - // A similar test is done in the policy's should_full_GC(). If this is - // changed, decide if that test should also be changed. size_t avg_promoted = (size_t) policy->padded_average_promoted_in_bytes(); size_t promotion_estimate = MIN2(avg_promoted, young_gen->used_in_bytes()); bool result = promotion_estimate < old_gen->free_in_bytes(); diff --git a/src/hotspot/share/gc/shared/gcHeapSummary.hpp b/src/hotspot/share/gc/shared/gcHeapSummary.hpp index c1a10fd2b698b..c0ed793fb67b0 100644 --- a/src/hotspot/share/gc/shared/gcHeapSummary.hpp +++ b/src/hotspot/share/gc/shared/gcHeapSummary.hpp @@ -177,7 +177,9 @@ class G1EvacSummary : public StackObj { size_t _region_end_waste; // Number of words wasted due to skipping to the next region. uint _regions_filled; // Number of regions filled completely. + size_t _num_plab_filled; // Number of PLABs refilled/retired. size_t _direct_allocated; // Number of words allocated directly into the regions. + size_t _num_direct_allocated; // Number of direct allocations. // Number of words in live objects remaining in regions that ultimately suffered an // evacuation failure. This is used in the regions when the regions are made old regions. @@ -187,12 +189,23 @@ class G1EvacSummary : public StackObj { // end of regions. size_t _failure_waste; public: - G1EvacSummary(size_t allocated, size_t wasted, size_t undo_wasted, size_t unused, - size_t used, size_t region_end_waste, uint regions_filled, size_t direct_allocated, - size_t failure_used, size_t failure_waste) : + G1EvacSummary(size_t allocated, + size_t wasted, + size_t undo_wasted, + size_t unused, + size_t used, + size_t region_end_waste, + uint regions_filled, + size_t num_plab_filled, + size_t direct_allocated, + size_t num_direct_allocated, + size_t failure_used, + size_t failure_waste) : _allocated(allocated), _wasted(wasted), _undo_wasted(undo_wasted), _unused(unused), - _used(used), _region_end_waste(region_end_waste), _regions_filled(regions_filled), - _direct_allocated(direct_allocated), _failure_used(failure_used), _failure_waste(failure_waste) + _used(used), _region_end_waste(region_end_waste), + _regions_filled(regions_filled), _num_plab_filled(num_plab_filled), + _direct_allocated(direct_allocated),_num_direct_allocated(num_direct_allocated), + _failure_used(failure_used), _failure_waste(failure_waste) { } size_t allocated() const { return _allocated; } @@ -202,7 +215,9 @@ class G1EvacSummary : public StackObj { size_t used() const { return _used; } size_t region_end_waste() const { return _region_end_waste; } uint regions_filled() const { return _regions_filled; } + size_t num_plab_filled() const { return _num_plab_filled; } size_t direct_allocated() const { return _direct_allocated; } + size_t num_direct_allocated() const { return _num_direct_allocated; } size_t failure_used() const { return _failure_used; } size_t failure_waste() const { return _failure_waste; } }; diff --git a/src/hotspot/share/gc/shared/gcOverheadChecker.cpp b/src/hotspot/share/gc/shared/gcOverheadChecker.cpp index d72d1f1e59f5b..84ecf6d9d6868 100644 --- a/src/hotspot/share/gc/shared/gcOverheadChecker.cpp +++ b/src/hotspot/share/gc/shared/gcOverheadChecker.cpp @@ -41,7 +41,11 @@ void GCOverheadChecker::check_gc_overhead_limit(GCOverheadTester* time_overhead, bool is_full_gc, GCCause::Cause gc_cause, SoftRefPolicy* soft_ref_policy) { - + if (is_full_gc) { + // Explicit Full GC would do the clearing of soft-refs as well + // So reset in the beginning + soft_ref_policy->set_should_clear_all_soft_refs(false); + } // Ignore explicit GC's. Exiting here does not set the flag and // does not reset the count. if (GCCause::is_user_requested_gc(gc_cause) || diff --git a/src/hotspot/share/gc/shared/genArguments.cpp b/src/hotspot/share/gc/shared/genArguments.cpp index 805f353576220..6bba0f2cf69f2 100644 --- a/src/hotspot/share/gc/shared/genArguments.cpp +++ b/src/hotspot/share/gc/shared/genArguments.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -273,6 +273,9 @@ void GenArguments::initialize_size_info() { // and maximum heap size since no explicit flags exist // for setting the old generation maximum. MaxOldSize = MAX2(MaxHeapSize - max_young_size, GenAlignment); + MinOldSize = MIN3(MaxOldSize, + InitialHeapSize - initial_young_size, + MinHeapSize - MinNewSize); size_t initial_old_size = OldSize; @@ -284,9 +287,8 @@ void GenArguments::initialize_size_info() { // with the overall heap size). In either case make // the minimum, maximum and initial sizes consistent // with the young sizes and the overall heap sizes. - MinOldSize = GenAlignment; initial_old_size = clamp(InitialHeapSize - initial_young_size, MinOldSize, MaxOldSize); - // MaxOldSize has already been made consistent above. + // MaxOldSize and MinOldSize have already been made consistent above. } else { // OldSize has been explicitly set on the command line. Use it // for the initial size but make sure the minimum allow a young @@ -301,9 +303,10 @@ void GenArguments::initialize_size_info() { ", -XX:OldSize flag is being ignored", MaxHeapSize); initial_old_size = MaxOldSize; + } else if (initial_old_size < MinOldSize) { + log_warning(gc, ergo)("Inconsistency between initial old size and minimum old size"); + MinOldSize = initial_old_size; } - - MinOldSize = MIN2(initial_old_size, MinHeapSize - MinNewSize); } // The initial generation sizes should match the initial heap size, diff --git a/src/hotspot/share/gc/shared/memAllocator.cpp b/src/hotspot/share/gc/shared/memAllocator.cpp index a68a3604a6a20..1167a013e2caa 100644 --- a/src/hotspot/share/gc/shared/memAllocator.cpp +++ b/src/hotspot/share/gc/shared/memAllocator.cpp @@ -308,18 +308,15 @@ HeapWord* MemAllocator::allocate_inside_tlab_slow(Allocation& allocation) const PTR_FORMAT " min: " SIZE_FORMAT ", desired: " SIZE_FORMAT, p2i(mem), min_tlab_size, new_tlab_size); + // ...and clear or zap just allocated TLAB, if needed. if (ZeroTLAB) { - // ..and clear it. Copy::zero_to_words(mem, allocation._allocated_tlab_size); - } else { - // ...and zap just allocated object. -#ifdef ASSERT + } else if (ZapTLAB) { // Skip mangling the space corresponding to the object header to // ensure that the returned space is not considered parsable by // any concurrent GC thread. size_t hdr_size = oopDesc::header_size(); Copy::fill_to_words(mem + hdr_size, allocation._allocated_tlab_size - hdr_size, badHeapWordVal); -#endif // ASSERT } tlab.fill(mem, mem + _word_size, allocation._allocated_tlab_size); diff --git a/src/hotspot/share/gc/shared/oopStorage.inline.hpp b/src/hotspot/share/gc/shared/oopStorage.inline.hpp index 4bcff174b5f47..000700afde6bb 100644 --- a/src/hotspot/share/gc/shared/oopStorage.inline.hpp +++ b/src/hotspot/share/gc/shared/oopStorage.inline.hpp @@ -28,8 +28,6 @@ #include "gc/shared/oopStorage.hpp" #include "memory/allocation.hpp" -#include "metaprogramming/conditional.hpp" -#include "metaprogramming/isConst.hpp" #include "oops/oop.hpp" #include "runtime/safepoint.hpp" #include "utilities/align.hpp" @@ -37,6 +35,8 @@ #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include + // Array of all active blocks. Refcounted for lock-free reclaim of // old array when a new array is allocated for expansion. class OopStorage::ActiveArray { @@ -361,7 +361,7 @@ inline bool OopStorage::iterate_impl(F f, Storage* storage) { assert_at_safepoint(); // Propagate const/non-const iteration to the block layer, by using // const or non-const blocks as corresponding to Storage. - typedef typename Conditional::value, const Block*, Block*>::type BlockPtr; + using BlockPtr = std::conditional_t::value, const Block*, Block*>; ActiveArray* blocks = storage->_active_array; size_t limit = blocks->block_count(); for (size_t i = 0; i < limit; ++i) { diff --git a/src/hotspot/share/gc/shared/oopStorageParState.hpp b/src/hotspot/share/gc/shared/oopStorageParState.hpp index 3f3d11393d872..e76541483e55d 100644 --- a/src/hotspot/share/gc/shared/oopStorageParState.hpp +++ b/src/hotspot/share/gc/shared/oopStorageParState.hpp @@ -28,6 +28,8 @@ #include "gc/shared/oopStorage.hpp" #include "utilities/globalDefinitions.hpp" +#include + ////////////////////////////////////////////////////////////////////////////// // Support for parallel and optionally concurrent state iteration. // @@ -167,9 +169,7 @@ template class OopStorage::ParState { BasicParState _basic_state; - typedef typename Conditional::type StoragePtr; + using StoragePtr = std::conditional_t; public: ParState(StoragePtr storage, diff --git a/src/hotspot/share/gc/shared/oopStorageParState.inline.hpp b/src/hotspot/share/gc/shared/oopStorageParState.inline.hpp index ce37d4ede9387..9e48ea7c77bea 100644 --- a/src/hotspot/share/gc/shared/oopStorageParState.inline.hpp +++ b/src/hotspot/share/gc/shared/oopStorageParState.inline.hpp @@ -28,9 +28,10 @@ #include "gc/shared/oopStorageParState.hpp" #include "gc/shared/oopStorage.inline.hpp" -#include "metaprogramming/conditional.hpp" #include "utilities/macros.hpp" +#include + template class OopStorage::BasicParState::AlwaysTrueFn { F _f; @@ -56,7 +57,7 @@ inline void OopStorage::BasicParState::iterate(F f) { while (claim_next_segment(&data)) { assert(data._segment_start < data._segment_end, "invariant"); assert(data._segment_end <= _block_count, "invariant"); - typedef typename Conditional::type BlockPtr; + using BlockPtr = std::conditional_t; size_t i = data._segment_start; do { BlockPtr block = _active_array->at(i); diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp index 3b167d752e7f8..56762d9e5eb71 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp @@ -39,14 +39,8 @@ inline HeapWord* ThreadLocalAllocBuffer::allocate(size_t size) { invariants(); HeapWord* obj = top(); if (pointer_delta(end(), obj) >= size) { - // successful thread-local allocation -#ifdef ASSERT - // Skip mangling the space corresponding to the object header to - // ensure that the returned space is not considered parsable by - // any concurrent GC thread. - size_t hdr_size = oopDesc::header_size(); - Copy::fill_to_words(obj + hdr_size, size - hdr_size, badHeapWordVal); -#endif // ASSERT + // Successful thread-local allocation. + // This addition is safe because we know that top is // at least size below end, so the add can't wrap. set_top(obj + size); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 3222a0262ebfd..caf642d0847b9 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -1048,6 +1048,7 @@ void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const Mem Node* u = ctrl->fast_out(i); if (u->_idx < last && u != barrier && + !u->depends_only_on_test() && // preserve dependency on test !uses_to_ignore.member(u) && (u->in(0) != ctrl || (!u->is_Region() && !u->is_Phi())) && (ctrl->Opcode() != Op_CatchProj || u->Opcode() != Op_CreateEx)) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp index ebc288efda973..d26e085a4b270 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp @@ -36,13 +36,19 @@ #include "runtime/threadWXSetters.inline.hpp" bool ShenandoahBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { + if (!is_armed(nm)) { + // Some other thread got here first and healed the oops + // and disarmed the nmethod. No need to continue. + return true; + } + ShenandoahReentrantLock* lock = ShenandoahNMethod::lock_for_nmethod(nm); assert(lock != NULL, "Must be"); ShenandoahReentrantLocker locker(lock); if (!is_armed(nm)) { - // Some other thread got here first and healed the oops - // and disarmed the nmethod. + // Some other thread managed to complete while we were + // waiting for lock. No need to continue. return true; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index 1230030da64ad..ab80fd5939014 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -163,13 +164,6 @@ class ShenandoahDisarmNMethodsTask : public AbstractGangTask { AbstractGangTask("Shenandoah Disarm NMethods"), _iterator(ShenandoahCodeRoots::table()) { assert(SafepointSynchronize::is_at_safepoint(), "Only at a safepoint"); - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _iterator.nmethods_do_begin(); - } - - ~ShenandoahDisarmNMethodsTask() { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _iterator.nmethods_do_end(); } virtual void work(uint worker_id) { @@ -269,15 +263,7 @@ class ShenandoahUnlinkTask : public AbstractGangTask { AbstractGangTask("Shenandoah Unlink NMethods"), _cl(unloading_occurred), _verifier(verifier), - _iterator(ShenandoahCodeRoots::table()) { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _iterator.nmethods_do_begin(); - } - - ~ShenandoahUnlinkTask() { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _iterator.nmethods_do_end(); - } + _iterator(ShenandoahCodeRoots::table()) {} virtual void work(uint worker_id) { ICRefillVerifierMark mark(_verifier); @@ -329,15 +315,7 @@ class ShenandoahNMethodPurgeTask : public AbstractGangTask { ShenandoahNMethodPurgeTask() : AbstractGangTask("Shenandoah Purge NMethods"), _cl(), - _iterator(ShenandoahCodeRoots::table()) { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _iterator.nmethods_do_begin(); - } - - ~ShenandoahNMethodPurgeTask() { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _iterator.nmethods_do_end(); - } + _iterator(ShenandoahCodeRoots::table()) {} virtual void work(uint worker_id) { _iterator.nmethods_do(&_cl); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 817f43e693246..9d49d4ca4dd30 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2021, Red Hat, Inc. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -748,18 +749,9 @@ class ShenandoahConcurrentWeakRootsEvacUpdateTask : public AbstractGangTask { _vm_roots(phase), _cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers()), _nmethod_itr(ShenandoahCodeRoots::table()), - _phase(phase) { - if (ShenandoahHeap::heap()->unload_classes()) { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _nmethod_itr.nmethods_do_begin(); - } - } + _phase(phase) {} ~ShenandoahConcurrentWeakRootsEvacUpdateTask() { - if (ShenandoahHeap::heap()->unload_classes()) { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _nmethod_itr.nmethods_do_end(); - } // Notify runtime data structures of potentially dead oops _vm_roots.report_num_dead(); } @@ -860,19 +852,7 @@ class ShenandoahConcurrentRootsEvacUpdateTask : public AbstractGangTask { _phase(phase), _vm_roots(phase), _cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers()), - _nmethod_itr(ShenandoahCodeRoots::table()) { - if (!ShenandoahHeap::heap()->unload_classes()) { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _nmethod_itr.nmethods_do_begin(); - } - } - - ~ShenandoahConcurrentRootsEvacUpdateTask() { - if (!ShenandoahHeap::heap()->unload_classes()) { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _nmethod_itr.nmethods_do_end(); - } - } + _nmethod_itr(ShenandoahCodeRoots::table()) {} void work(uint worker_id) { ShenandoahConcurrentWorkerSession worker_session(worker_id); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index d0cc794eeaab4..2d2c86d1dfd8e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -361,11 +361,13 @@ class ShenandoahPrepareForCompactionObjectClosure : public ObjectClosure { _compact_point = _to_region->bottom(); } - // Object fits into current region, record new location: + // Object fits into current region, record new location, if object does not move: assert(_compact_point + obj_size <= _to_region->end(), "must fit"); shenandoah_assert_not_forwarded(NULL, p); - _preserved_marks->push_if_necessary(p, p->mark()); - p->forward_to(cast_to_oop(_compact_point)); + if (_compact_point != cast_from_oop(p)) { + _preserved_marks->push_if_necessary(p, p->mark()); + p->forward_to(cast_to_oop(_compact_point)); + } _compact_point += obj_size; } }; @@ -845,6 +847,7 @@ class ShenandoahCompactObjectsClosure : public ObjectClosure { if (p->is_forwarded()) { HeapWord* compact_from = cast_from_oop(p); HeapWord* compact_to = cast_from_oop(p->forwardee()); + assert(compact_from != compact_to, "Forwarded object should move"); Copy::aligned_conjoint_words(compact_from, compact_to, size); oop new_obj = cast_to_oop(compact_to); new_obj->init_mark(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 67ea25d31fa4d..ceb067389a2a4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -778,18 +778,15 @@ HeapWord* ShenandoahHeap::allocate_from_gclab_slow(Thread* thread, size_t size) assert (size <= actual_size, "allocation should fit"); + // ...and clear or zap just allocated TLAB, if needed. if (ZeroTLAB) { - // ..and clear it. Copy::zero_to_words(gclab_buf, actual_size); - } else { - // ...and zap just allocated object. -#ifdef ASSERT + } else if (ZapTLAB) { // Skip mangling the space corresponding to the object header to // ensure that the returned space is not considered parsable by // any concurrent GC thread. size_t hdr_size = oopDesc::header_size(); Copy::fill_to_words(gclab_buf + hdr_size, actual_size - hdr_size, badHeapWordVal); -#endif // ASSERT } gclab->set_buf(gclab_buf, actual_size); return gclab->allocate(size); @@ -897,7 +894,11 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { } HeapWord* ShenandoahHeap::allocate_memory_under_lock(ShenandoahAllocRequest& req, bool& in_new_region) { - ShenandoahHeapLocker locker(lock()); + // If we are dealing with mutator allocation, then we may need to block for safepoint. + // We cannot block for safepoint for GC allocations, because there is a high chance + // we are already running at safepoint or from stack watermark machinery, and we cannot + // block again. + ShenandoahHeapLocker locker(lock(), req.is_mutator_alloc()); return _free_set->allocate(req, in_new_region); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp b/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp index 96260a05d8ab8..ee9559a3fb77c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp @@ -28,9 +28,57 @@ #include "gc/shenandoah/shenandoahLock.hpp" #include "runtime/atomic.hpp" +#include "runtime/interfaceSupport.inline.hpp" #include "runtime/os.inline.hpp" #include "runtime/thread.hpp" +void ShenandoahLock::contended_lock(bool allow_block_for_safepoint) { + Thread* thread = Thread::current(); + if (allow_block_for_safepoint && thread->is_Java_thread()) { + contended_lock_internal(thread->as_Java_thread()); + } else { + contended_lock_internal(nullptr); + } +} + +template +void ShenandoahLock::contended_lock_internal(JavaThread* java_thread) { + assert(!ALLOW_BLOCK || java_thread != nullptr, "Must have a Java thread when allowing block."); + // Spin this much, but only on multi-processor systems. + int ctr = os::is_MP() ? 0xFF : 0; + // Apply TTAS to avoid more expensive CAS calls if the lock is still held by other thread. + while (Atomic::load(&_state) == locked || + Atomic::cmpxchg(&_state, unlocked, locked) != unlocked) { + if (ctr > 0 && !SafepointSynchronize::is_synchronizing()) { + // Lightly contended, spin a little if no safepoint is pending. + SpinPause(); + ctr--; + } else if (ALLOW_BLOCK) { + ThreadBlockInVM block(java_thread); + if (SafepointSynchronize::is_synchronizing()) { + // If safepoint is pending, we want to block and allow safepoint to proceed. + // Normally, TBIVM above would block us in its destructor. + // + // But that blocking only happens when TBIVM knows the thread poll is armed. + // There is a window between announcing a safepoint and arming the thread poll + // during which trying to continuously enter TBIVM is counter-productive. + // Under high contention, we may end up going in circles thousands of times. + // To avoid it, we wait here until local poll is armed and then proceed + // to TBVIM exit for blocking. We do not SpinPause, but yield to let + // VM thread to arm the poll sooner. + while (SafepointSynchronize::is_synchronizing() && + !SafepointMechanism::local_poll_armed(java_thread)) { + os::naked_yield(); + } + } else { + os::naked_yield(); + } + } else { + os::naked_yield(); + } + } +} + ShenandoahSimpleLock::ShenandoahSimpleLock() { assert(os::mutex_init_done(), "Too early!"); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp b/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp index 4c84ef49a9f22..0d9206a4355d8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp @@ -35,34 +35,41 @@ class ShenandoahLock { enum LockState { unlocked = 0, locked = 1 }; shenandoah_padding(0); - volatile int _state; + volatile LockState _state; shenandoah_padding(1); - volatile Thread* _owner; + Thread* volatile _owner; shenandoah_padding(2); + template + void contended_lock_internal(JavaThread* java_thread); public: ShenandoahLock() : _state(unlocked), _owner(NULL) {}; - void lock() { -#ifdef ASSERT - assert(_owner != Thread::current(), "reentrant locking attempt, would deadlock"); -#endif - Thread::SpinAcquire(&_state, "Shenandoah Heap Lock"); -#ifdef ASSERT - assert(_state == locked, "must be locked"); - assert(_owner == NULL, "must not be owned"); - _owner = Thread::current(); -#endif + void lock(bool allow_block_for_safepoint) { + assert(Atomic::load(&_owner) != Thread::current(), "reentrant locking attempt, would deadlock"); + + if ((allow_block_for_safepoint && SafepointSynchronize::is_synchronizing()) || + (Atomic::cmpxchg(&_state, unlocked, locked) != unlocked)) { + // 1. Java thread, and there is a pending safepoint. Dive into contended locking + // immediately without trying anything else, and block. + // 2. Fast lock fails, dive into contended lock handling. + contended_lock(allow_block_for_safepoint); + } + + assert(Atomic::load(&_state) == locked, "must be locked"); + assert(Atomic::load(&_owner) == NULL, "must not be owned"); + DEBUG_ONLY(Atomic::store(&_owner, Thread::current());) } void unlock() { -#ifdef ASSERT - assert (_owner == Thread::current(), "sanity"); - _owner = NULL; -#endif - Thread::SpinRelease(&_state); + assert(Atomic::load(&_owner) == Thread::current(), "sanity"); + DEBUG_ONLY(Atomic::store(&_owner, (Thread*)NULL);) + OrderAccess::fence(); + Atomic::store(&_state, unlocked); } + void contended_lock(bool allow_block_for_safepoint); + bool owned_by_self() { #ifdef ASSERT return _state == locked && _owner == Thread::current(); @@ -77,9 +84,9 @@ class ShenandoahLocker : public StackObj { private: ShenandoahLock* const _lock; public: - ShenandoahLocker(ShenandoahLock* lock) : _lock(lock) { + ShenandoahLocker(ShenandoahLock* lock, bool allow_block_for_safepoint = false) : _lock(lock) { if (_lock != NULL) { - _lock->lock(); + _lock->lock(allow_block_for_safepoint); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index e0f5b9ee71ba4..6c1f08096fe9b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019, 2021, Red Hat, Inc. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2022, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +29,7 @@ #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahNMethod.inline.hpp" #include "memory/resourceArea.hpp" +#include "runtime/safepointVerifiers.hpp" ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray& oops, bool non_immediate_oops) : _nm(nm), _oops(NULL), _oops_count(0), _unregistered(false) { @@ -542,21 +544,40 @@ void ShenandoahNMethodTableSnapshot::concurrent_nmethods_do(NMethodClosure* cl) } ShenandoahConcurrentNMethodIterator::ShenandoahConcurrentNMethodIterator(ShenandoahNMethodTable* table) : - _table(table), _table_snapshot(NULL) { -} - -void ShenandoahConcurrentNMethodIterator::nmethods_do_begin() { - assert(CodeCache_lock->owned_by_self(), "Lock must be held"); - _table_snapshot = _table->snapshot_for_iteration(); -} + _table(table), + _table_snapshot(nullptr), + _started_workers(0), + _finished_workers(0) {} void ShenandoahConcurrentNMethodIterator::nmethods_do(NMethodClosure* cl) { - assert(_table_snapshot != NULL, "Must first call nmethod_do_begin()"); - _table_snapshot->concurrent_nmethods_do(cl); -} + // Cannot safepoint when iteration is running, because this can cause deadlocks + // with other threads waiting on iteration to be over. + NoSafepointVerifier nsv; -void ShenandoahConcurrentNMethodIterator::nmethods_do_end() { - assert(CodeCache_lock->owned_by_self(), "Lock must be held"); - _table->finish_iteration(_table_snapshot); - CodeCache_lock->notify_all(); + MutexLocker ml(CodeCache_lock, Mutex::_no_safepoint_check_flag); + + if (_finished_workers > 0) { + // Some threads have already finished. We are now in rampdown: we are now + // waiting for all currently recorded workers to finish. No new workers + // should start. + return; + } + + // Record a new worker and initialize the snapshot if it is a first visitor. + if (_started_workers++ == 0) { + _table_snapshot = _table->snapshot_for_iteration(); + } + + // All set, relinquish the lock and go concurrent. + { + MutexUnlocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + _table_snapshot->concurrent_nmethods_do(cl); + } + + // Record completion. Last worker shuts down the iterator and notifies any waiters. + uint count = ++_finished_workers; + if (count == _started_workers) { + _table->finish_iteration(_table_snapshot); + CodeCache_lock->notify_all(); + } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp index ba67517fe2635..3b4045666dece 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp @@ -187,13 +187,13 @@ class ShenandoahConcurrentNMethodIterator { private: ShenandoahNMethodTable* const _table; ShenandoahNMethodTableSnapshot* _table_snapshot; + uint _started_workers; + uint _finished_workers; public: ShenandoahConcurrentNMethodIterator(ShenandoahNMethodTable* table); - void nmethods_do_begin(); void nmethods_do(NMethodClosure* cl); - void nmethods_do_end(); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHNMETHOD_HPP diff --git a/src/hotspot/share/gc/z/zSafeDelete.hpp b/src/hotspot/share/gc/z/zSafeDelete.hpp index f18d1e4153ec3..62c3495c57b2c 100644 --- a/src/hotspot/share/gc/z/zSafeDelete.hpp +++ b/src/hotspot/share/gc/z/zSafeDelete.hpp @@ -26,12 +26,13 @@ #include "gc/z/zArray.hpp" #include "gc/z/zLock.hpp" -#include "metaprogramming/removeExtent.hpp" + +#include template class ZSafeDeleteImpl { private: - typedef typename RemoveExtent::type ItemT; + using ItemT = std::remove_extent_t; ZLock* _lock; uint64_t _enabled; diff --git a/src/hotspot/share/gc/z/zSafeDelete.inline.hpp b/src/hotspot/share/gc/z/zSafeDelete.inline.hpp index 9db212bd4c242..460193827e055 100644 --- a/src/hotspot/share/gc/z/zSafeDelete.inline.hpp +++ b/src/hotspot/share/gc/z/zSafeDelete.inline.hpp @@ -27,9 +27,10 @@ #include "gc/z/zSafeDelete.hpp" #include "gc/z/zArray.inline.hpp" -#include "metaprogramming/isArray.hpp" #include "utilities/debug.hpp" +#include + template ZSafeDeleteImpl::ZSafeDeleteImpl(ZLock* lock) : _lock(lock), @@ -49,7 +50,7 @@ bool ZSafeDeleteImpl::deferred_delete(ItemT* item) { template void ZSafeDeleteImpl::immediate_delete(ItemT* item) { - if (IsArray::value) { + if (std::is_array::value) { delete [] item; } else { delete item; diff --git a/src/hotspot/share/interpreter/bootstrapInfo.cpp b/src/hotspot/share/interpreter/bootstrapInfo.cpp index 830c015cc3920..4070a68e1f9d9 100644 --- a/src/hotspot/share/interpreter/bootstrapInfo.cpp +++ b/src/hotspot/share/interpreter/bootstrapInfo.cpp @@ -230,9 +230,9 @@ void BootstrapInfo::print_msg_on(outputStream* st, const char* msg) { st = st ? st : tty; if (_indy_index != -1) - sprintf(what, "indy#%d", decode_indy_index()); + os::snprintf_checked(what, sizeof(what), "indy#%d", decode_indy_index()); else - sprintf(what, "condy"); + os::snprintf_checked(what, sizeof(what), "condy"); bool have_msg = (msg != NULL && strlen(msg) > 0); st->print_cr("%s%sBootstrap in %s %s@CP[%d] %s:%s%s BSMS[%d] BSM@CP[%d]%s argc=%d%s", (have_msg ? msg : ""), (have_msg ? " " : ""), @@ -251,11 +251,11 @@ void BootstrapInfo::print_msg_on(outputStream* st, const char* msg) { for (int i = 0; i < _argc; i++) { int pos = (int) strlen(argbuf); if (pos + 20 > (int)sizeof(argbuf)) { - sprintf(argbuf + pos, "..."); + os::snprintf_checked(argbuf + pos, sizeof(argbuf) - pos, "..."); break; } if (i > 0) argbuf[pos++] = ','; - sprintf(argbuf+pos, "%d", arg_index(i)); + os::snprintf_checked(argbuf+pos, sizeof(argbuf) - pos, "%d", arg_index(i)); } st->print_cr(" argument indexes: {%s}", argbuf); } diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp index b93bb30f0c870..3fc57c7690308 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp @@ -1942,11 +1942,9 @@ void BytecodeInterpreter::run(interpreterState istate) { size_t obj_size = ik->size_helper(); HeapWord* result = THREAD->tlab().allocate(obj_size); if (result != NULL) { - // Initialize object field block: - // - if TLAB is pre-zeroed, we can skip this path - // - in debug mode, ThreadLocalAllocBuffer::allocate mangles - // this area, and we still need to initialize it - if (DEBUG_ONLY(true ||) !ZeroTLAB) { + // Initialize object field block. + if (!ZeroTLAB) { + // The TLAB was not pre-zeroed, we need to clear the memory here. size_t hdr_size = oopDesc::header_size(); Copy::fill_to_words(result + hdr_size, obj_size - hdr_size, 0); } diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp index a21a4fa7d4823..7c7d3d819b8cc 100644 --- a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp +++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp @@ -103,10 +103,6 @@ class JNIHandleBlockManager : public StackObj { } }; -static bool is_module_available(outputStream* output, TRAPS) { - return JfrJavaSupport::is_jdk_jfr_module_available(output, THREAD); -} - static bool is_disabled(outputStream* output) { if (Jfr::is_disabled()) { if (output != NULL) { @@ -119,7 +115,28 @@ static bool is_disabled(outputStream* output) { static bool invalid_state(outputStream* out, TRAPS) { DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); - return is_disabled(out) || !is_module_available(out, THREAD); + if (is_disabled(out)) { + return true; + } + if (!JfrJavaSupport::is_jdk_jfr_module_available()) { + JfrJavaSupport::load_jdk_jfr_module(THREAD); + if (HAS_PENDING_EXCEPTION) { + // Log exception here, but let is_jdk_jfr_module_available(out, THREAD) + // handle output to the user. + ResourceMark rm(THREAD); + oop throwable = PENDING_EXCEPTION; + assert(throwable != nullptr, "invariant"); + oop msg = java_lang_Throwable::message(throwable); + if (msg != nullptr) { + char* text = java_lang_String::as_utf8_string(msg); + if (text != nullptr) { + log_debug(jfr, startup)("Flight Recorder can not be enabled. %s", text); + } + } + CLEAR_PENDING_EXCEPTION; + } + } + return !JfrJavaSupport::is_jdk_jfr_module_available(out, THREAD); } static void handle_pending_exception(outputStream* output, bool startup, oop throwable) { diff --git a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp index 1ff65aaf5c955..147b17cbfd19f 100644 --- a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp +++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp @@ -41,6 +41,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/java.hpp" +#include "runtime/javaCalls.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/semaphore.inline.hpp" #include "runtime/synchronizer.hpp" @@ -607,6 +608,23 @@ JfrJavaSupport::CAUSE JfrJavaSupport::cause() { const char* const JDK_JFR_MODULE_NAME = "jdk.jfr"; const char* const JDK_JFR_PACKAGE_NAME = "jdk/jfr"; + + +void JfrJavaSupport::load_jdk_jfr_module(TRAPS) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD)); + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + Handle h_module_name = java_lang_String::create_from_str(JDK_JFR_MODULE_NAME, CHECK); + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, + vmClasses::module_Modules_klass(), + vmSymbols::loadModule_name(), + vmSymbols::loadModule_signature(), + h_module_name, + CHECK + ); +} + static bool is_jdk_jfr_module_in_readability_graph() { // take one of the packages in the module to be located and query for its definition. TempNewSymbol pkg_sym = SymbolTable::new_symbol(JDK_JFR_PACKAGE_NAME); diff --git a/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp index d167e82edee65..2a64c3a6497d6 100644 --- a/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp +++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp @@ -86,6 +86,7 @@ class JfrJavaSupport : public AllStatic { static void throw_class_format_error(const char* message, TRAPS); static void throw_runtime_exception(const char* message, TRAPS); + static void load_jdk_jfr_module(TRAPS); static bool is_jdk_jfr_module_available(); static bool is_jdk_jfr_module_available(outputStream* stream, TRAPS); diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp index 8e2a1060528af..ed5144d4bfa56 100644 --- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp +++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp @@ -214,9 +214,6 @@ class StackTraceBlobInstaller { StackTraceBlobInstaller() : _cache(JfrOptionSet::old_object_queue_size()) { prepare_for_resolution(); } - ~StackTraceBlobInstaller() { - JfrStackTraceRepository::clear_leak_profiler(); - } void sample_do(ObjectSample* sample) { if (stack_trace_precondition(sample)) { install(sample); @@ -270,11 +267,14 @@ void ObjectSampleCheckpoint::on_rotation(const ObjectSampler* sampler) { assert(LeakProfiler::is_running(), "invariant"); JavaThread* const thread = JavaThread::current(); DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(thread);) - // can safepoint here - ThreadInVMfromNative transition(thread); - MutexLocker lock(ClassLoaderDataGraph_lock); - // the lock is needed to ensure the unload lists do not grow in the middle of inspection. - install_stack_traces(sampler); + { + // can safepoint here + ThreadInVMfromNative transition(thread); + MutexLocker lock(ClassLoaderDataGraph_lock); + // the lock is needed to ensure the unload lists do not grow in the middle of inspection. + install_stack_traces(sampler); + } + JfrStackTraceRepository::clear_leak_profiler(); } static bool is_klass_unloaded(traceid klass_id) { diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp index d66e9236d560b..7d3a1f1663f6c 100644 --- a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp +++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -248,12 +248,6 @@ bool ReferenceToThreadRootClosure::do_thread_stack_detailed(JavaThread* jt) { ReferenceLocateClosure rcl(_callback, OldObjectRoot::_threads, OldObjectRoot::_stack_variable, jt); if (jt->has_last_Java_frame()) { - // Traverse the monitor chunks - MonitorChunk* chunk = jt->monitor_chunks(); - for (; chunk != NULL; chunk = chunk->next()) { - chunk->oops_do(&rcl); - } - if (rcl.complete()) { return true; } diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 5ebb5502b0b59..1b4ba9da167d6 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -350,7 +350,9 @@ + + @@ -658,7 +660,7 @@ - + @@ -1049,7 +1051,7 @@ - + diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp index affd884924558..dc59166bb515d 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp @@ -103,6 +103,9 @@ bool JfrCheckpointManager::initialize() { // preallocate buffer count to each of the epoch live lists for (size_t i = 0; i < global_buffer_prealloc_count * 2; ++i) { Buffer* const buffer = mspace_allocate(global_buffer_size, _global_mspace); + if (buffer == nullptr) { + return false; + } _global_mspace->add_to_live_list(buffer, i % 2 == 0); } assert(_global_mspace->free_list_is_empty(), "invariant"); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index 78d6ab48f97f2..dc2add22020c4 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -228,7 +228,7 @@ static int write_klass(JfrCheckpointWriter* writer, KlassPtr klass, bool leakp) writer->write(cld != NULL ? cld_id(cld, leakp) : 0); writer->write(mark_symbol(klass, leakp)); writer->write(package_id(klass, leakp)); - writer->write(get_flags(klass)); + writer->write(klass->modifier_flags()); writer->write(klass->is_hidden()); return 1; } @@ -429,8 +429,6 @@ static void do_previous_epoch_artifact(JfrArtifactClosure* callback, T* value) { assert(value != NULL, "invariant"); if (USED_PREVIOUS_EPOCH(value)) { callback->do_artifact(value); - assert(IS_NOT_SERIALIZED(value), "invariant"); - return; } if (IS_SERIALIZED(value)) { CLEAR_SERIALIZED(value); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp index 5eb70fb23d2df..cad6f6bc07931 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp @@ -207,7 +207,7 @@ static const char* create_hidden_klass_symbol(const InstanceKlass* ik, uintptr_t const oop mirror = ik->java_mirror_no_keepalive(); assert(mirror != NULL, "invariant"); char hash_buf[40]; - sprintf(hash_buf, "/" UINTX_FORMAT, hash); + snprintf(hash_buf, sizeof(hash_buf), "/" UINTX_FORMAT, hash); const size_t hash_len = strlen(hash_buf); const size_t result_len = ik->name()->utf8_length(); hidden_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp index 29bade3def6a3..9b7ddcd1273a2 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -109,7 +109,14 @@ inline traceid JfrTraceIdLoadBarrier::load(const PackageEntry* package) { inline traceid JfrTraceIdLoadBarrier::load(const ClassLoaderData* cld) { assert(cld != NULL, "invariant"); - return cld->has_class_mirror_holder() ? 0 : set_used_and_get(cld); + if (cld->has_class_mirror_holder()) { + return 0; + } + const Klass* const class_loader_klass = cld->class_loader_klass(); + if (class_loader_klass != nullptr && should_tag(class_loader_klass)) { + load_barrier(class_loader_klass); + } + return set_used_and_get(cld); } inline traceid JfrTraceIdLoadBarrier::load_leakp(const Klass* klass, const Method* method) { diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp index c7cd5a4d2dde0..a611707462c68 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp @@ -350,13 +350,17 @@ static void write_repository_files(const RepositoryIterator& iterator, char* con const ssize_t read_result = os::read_at(current_fd, copy_block, (int)block_size, bytes_read); if (-1 == read_result) { log_info(jfr)( // For user, should not be "jfr, system" - "Unable to recover JFR data"); + "Unable to recover JFR data, read failed."); break; } bytes_read += (int64_t)read_result; assert(bytes_read - bytes_written <= (int64_t)block_size, "invariant"); - bytes_written += (int64_t)os::write(emergency_fd, copy_block, bytes_read - bytes_written); - assert(bytes_read == bytes_written, "invariant"); + if (!os::write(emergency_fd, copy_block, bytes_read - bytes_written)) { + log_info(jfr)( // For user, should not be "jfr, system" + "Unable to recover JFR data, write failed."); + break; + } + bytes_written = bytes_read; } os::close(current_fd); } diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp index 0627593611cd3..657c5645ae77f 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp @@ -543,9 +543,7 @@ void JfrRecorderService::pre_safepoint_write() { ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire()); } write_storage(_storage, _chunkwriter); - if (_stack_trace_repository.is_modified()) { - write_stacktrace(_stack_trace_repository, _chunkwriter, false); - } + write_stacktrace(_stack_trace_repository, _chunkwriter, true); } void JfrRecorderService::invoke_safepoint_write() { diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp index 82f3362d6ee09..1e940ef6f974d 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp @@ -98,11 +98,10 @@ bool JfrStackTraceRepository::is_modified() const { } size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) { + MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); if (_entries == 0) { return 0; } - MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); - assert(_entries > 0, "invariant"); int count = 0; for (u4 i = 0; i < TABLE_SIZE; ++i) { JfrStackTrace* stacktrace = _table[i]; diff --git a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp index 132295230ac5a..367b471440626 100644 --- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp +++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp @@ -85,6 +85,9 @@ bool JfrStringPool::initialize() { // preallocate buffer count to each of the epoch live lists for (size_t i = 0; i < string_pool_cache_count * 2; ++i) { Buffer* const buffer = mspace_allocate(string_pool_buffer_size, _mspace); + if (buffer == nullptr) { + return false; + } _mspace->add_to_live_list(buffer, i % 2 == 0); } assert(_mspace->free_list_is_empty(), "invariant"); diff --git a/src/hotspot/share/jfr/writers/jfrStreamWriterHost.inline.hpp b/src/hotspot/share/jfr/writers/jfrStreamWriterHost.inline.hpp index f8900a13b4de0..52c69473a1407 100644 --- a/src/hotspot/share/jfr/writers/jfrStreamWriterHost.inline.hpp +++ b/src/hotspot/share/jfr/writers/jfrStreamWriterHost.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,14 +76,14 @@ inline void StreamWriterHost::write_bytes(const u1* buf, intptr_t l assert(len >= 0, "invariant"); while (len > 0) { const unsigned int nBytes = len > INT_MAX ? INT_MAX : (unsigned int)len; - const ssize_t num_written = (ssize_t)os::write(_fd, buf, nBytes); - if (errno == ENOSPC) { + const bool successful_write = os::write(_fd, buf, nBytes); + if (!successful_write && errno == ENOSPC) { JfrJavaSupport::abort("Failed to write to jfr stream because no space left on device", false); } - guarantee(num_written > 0, "Nothing got written, or os::write() failed"); - _stream_pos += num_written; - len -= num_written; - buf += num_written; + guarantee(successful_write, "Not all the bytes got written, or os::write() failed"); + _stream_pos += nBytes; + len -= nBytes; + buf += nBytes; } } diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index 41478060accc3..82fc404c16620 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -303,9 +303,9 @@ jobjectArray readConfiguration0(JNIEnv *env, JVMCI_TRAPS) { JVMCIObjectArray vmFields = JVMCIENV->new_VMField_array(len, JVMCI_CHECK_NULL); for (int i = 0; i < len ; i++) { VMStructEntry vmField = JVMCIVMStructs::localHotSpotVMStructs[i]; - size_t name_buf_len = strlen(vmField.typeName) + strlen(vmField.fieldName) + 2 /* "::" */; - char* name_buf = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, name_buf_len + 1); - sprintf(name_buf, "%s::%s", vmField.typeName, vmField.fieldName); + const size_t name_buf_size = strlen(vmField.typeName) + strlen(vmField.fieldName) + 2 + 1 /* "::" */; + char* name_buf = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, name_buf_size); + os::snprintf_checked(name_buf, name_buf_size, "%s::%s", vmField.typeName, vmField.fieldName); CSTRING_TO_JSTRING(name, name_buf); CSTRING_TO_JSTRING(type, vmField.typeString); JVMCIObject box; diff --git a/src/hotspot/share/logging/logFileOutput.cpp b/src/hotspot/share/logging/logFileOutput.cpp index 4b8065f686e7b..b0ada2620fb22 100644 --- a/src/hotspot/share/logging/logFileOutput.cpp +++ b/src/hotspot/share/logging/logFileOutput.cpp @@ -92,11 +92,6 @@ static size_t parse_value(const char* value_str) { return value; } -static bool file_exists(const char* filename) { - struct stat dummy_stat; - return os::stat(filename, &dummy_stat) == 0; -} - static uint number_of_digits(uint number) { return number < 10 ? 1 : (number < 100 ? 2 : 3); } @@ -139,7 +134,7 @@ static uint next_file_number(const char* filename, assert(ret > 0 && static_cast(ret) == len - 1, "incorrect buffer length calculation"); - if (file_exists(archive_name) && !is_regular_file(archive_name)) { + if (os::file_exists(archive_name) && !is_regular_file(archive_name)) { // We've encountered something that's not a regular file among the // possible file rotation targets. Fail immediately to prevent // problems later. @@ -150,7 +145,7 @@ static uint next_file_number(const char* filename, } // Stop looking if we find an unused file name - if (!file_exists(archive_name)) { + if (!os::file_exists(archive_name)) { next_num = i; found = true; break; @@ -233,7 +228,7 @@ bool LogFileOutput::initialize(const char* options, outputStream* errstream) { return false; } - bool file_exist = file_exists(_file_name); + bool file_exist = os::file_exists(_file_name); if (file_exist && _is_default_file_count && is_fifo_file(_file_name)) { _file_count = 0; // Prevent file rotation for fifo's such as named pipes. } diff --git a/src/hotspot/share/logging/logOutputList.cpp b/src/hotspot/share/logging/logOutputList.cpp index 91139c900cfff..f2de18b5eec1a 100644 --- a/src/hotspot/share/logging/logOutputList.cpp +++ b/src/hotspot/share/logging/logOutputList.cpp @@ -124,17 +124,22 @@ void LogOutputList::add_output(LogOutput* output, LogLevelType level) { node->_next = node->_next->_next) { } + // To allow lock-free iteration of the output list the updates in the loops + // below require release semantics. + OrderAccess::release(); + // Update the _level_start index for (int l = LogLevel::Last; l >= level; l--) { - if (_level_start[l] == NULL || _level_start[l]->_level < level) { - _level_start[l] = node; + LogOutputNode* lnode = Atomic::load(&_level_start[l]); + if (lnode == nullptr || lnode->_level < level) { + Atomic::store(&_level_start[l], node); } } // Add the node the list - for (LogOutputNode* cur = _level_start[LogLevel::Last]; cur != NULL; cur = cur->_next) { - if (cur != node && cur->_next == node->_next) { - cur->_next = node; + for (LogOutputNode* cur = Atomic::load(&_level_start[LogLevel::Last]); cur != nullptr; cur = Atomic::load(&cur->_next)) { + if (cur != node && Atomic::load(&cur->_next) == node->_next) { + Atomic::store(&cur->_next, node); break; } } diff --git a/src/hotspot/share/logging/logOutputList.hpp b/src/hotspot/share/logging/logOutputList.hpp index e2179cc68ea9a..bf96292de3281 100644 --- a/src/hotspot/share/logging/logOutputList.hpp +++ b/src/hotspot/share/logging/logOutputList.hpp @@ -26,6 +26,7 @@ #include "logging/logLevel.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" class LogOutput; @@ -48,11 +49,11 @@ class LogOutputList { private: struct LogOutputNode : public CHeapObj { LogOutput* _value; - LogOutputNode* _next; + LogOutputNode* volatile _next; LogLevelType _level; }; - LogOutputNode* _level_start[LogLevel::Count]; + LogOutputNode* volatile _level_start[LogLevel::Count]; volatile jint _active_readers; LogOutputNode* find(const LogOutput* output) const; @@ -124,7 +125,9 @@ class LogOutputList { } void operator++(int) { - _current = _current->_next; + // FIXME: memory_order_consume could be used here. + // Atomic access on the reading side for LogOutputList. + _current = Atomic::load_acquire(&_current->_next); } bool operator!=(const LogOutputNode *ref) const { @@ -138,7 +141,9 @@ class LogOutputList { Iterator iterator(LogLevelType level = LogLevel::Last) { increase_readers(); - return Iterator(this, _level_start[level]); + // FIXME: memory_order_consume could be used here. + // Atomic access on the reading side for LogOutputList. + return Iterator(this, Atomic::load_acquire(&_level_start[level])); } LogOutputNode* end() const { diff --git a/src/hotspot/share/logging/logSelectionList.hpp b/src/hotspot/share/logging/logSelectionList.hpp index 398f53ba8d892..bdff767f9daf5 100644 --- a/src/hotspot/share/logging/logSelectionList.hpp +++ b/src/hotspot/share/logging/logSelectionList.hpp @@ -37,7 +37,7 @@ class LogTagSet; // Consists of ordered LogSelections, i.e. "tag1+tag2=level1,tag3*=level2". class LogSelectionList : public StackObj { public: - static const size_t MaxSelections = 256; + static const size_t MaxSelections = 320; private: friend void LogConfiguration::configure_stdout(LogLevelType, int, ...); diff --git a/src/hotspot/share/memory/guardedMemory.cpp b/src/hotspot/share/memory/guardedMemory.cpp index d978b0bea6539..6cd141ce4ff0d 100644 --- a/src/hotspot/share/memory/guardedMemory.cpp +++ b/src/hotspot/share/memory/guardedMemory.cpp @@ -33,7 +33,9 @@ void* GuardedMemory::wrap_copy(const void* ptr, const size_t len, const void* ta if (outerp != NULL) { GuardedMemory guarded(outerp, len, tag); void* innerp = guarded.get_user_ptr(); - memcpy(innerp, ptr, len); + if (ptr != nullptr) { + memcpy(innerp, ptr, len); + } return innerp; } return NULL; // OOM diff --git a/src/hotspot/share/memory/iterator.inline.hpp b/src/hotspot/share/memory/iterator.inline.hpp index cc339f2213d14..428a057797f25 100644 --- a/src/hotspot/share/memory/iterator.inline.hpp +++ b/src/hotspot/share/memory/iterator.inline.hpp @@ -39,6 +39,8 @@ #include "oops/typeArrayKlass.inline.hpp" #include "utilities/debug.hpp" +#include + // Defaults to strong claiming. inline MetadataVisitingOopIterateClosure::MetadataVisitingOopIterateClosure(ReferenceDiscoverer* rd) : ClaimMetadataVisitingOopIterateClosure(ClassLoaderData::_claim_strong, rd) {} @@ -94,16 +96,16 @@ inline void ClaimMetadataVisitingOopIterateClosure::do_klass(Klass* k) { // p - The oop (or narrowOop) field to pass to the closure template -static typename EnableIf::value, void>::type +static typename EnableIf::value, void>::type call_do_oop(void (Receiver::*)(T*), void (Base::*)(T*), OopClosureType* closure, T* p) { closure->do_oop(p); } template -static typename EnableIf::value, void>::type +static typename EnableIf::value, void>::type call_do_oop(void (Receiver::*)(T*), void (Base::*)(T*), OopClosureType* closure, T* p) { // Sanity check - STATIC_ASSERT((!IsSame::value)); + STATIC_ASSERT((!std::is_same::value)); closure->OopClosureType::do_oop(p); } @@ -115,13 +117,13 @@ inline void Devirtualizer::do_oop(OopClosureType* closure, T* p) { // Implementation of the non-virtual do_metadata dispatch. template -static typename EnableIf::value, bool>::type +static typename EnableIf::value, bool>::type call_do_metadata(bool (Receiver::*)(), bool (Base::*)(), OopClosureType* closure) { return closure->do_metadata(); } template -static typename EnableIf::value, bool>::type +static typename EnableIf::value, bool>::type call_do_metadata(bool (Receiver::*)(), bool (Base::*)(), OopClosureType* closure) { return closure->OopClosureType::do_metadata(); } @@ -134,13 +136,13 @@ inline bool Devirtualizer::do_metadata(OopClosureType* closure) { // Implementation of the non-virtual do_klass dispatch. template -static typename EnableIf::value, void>::type +static typename EnableIf::value, void>::type call_do_klass(void (Receiver::*)(Klass*), void (Base::*)(Klass*), OopClosureType* closure, Klass* k) { closure->do_klass(k); } template -static typename EnableIf::value, void>::type +static typename EnableIf::value, void>::type call_do_klass(void (Receiver::*)(Klass*), void (Base::*)(Klass*), OopClosureType* closure, Klass* k) { closure->OopClosureType::do_klass(k); } @@ -153,13 +155,13 @@ inline void Devirtualizer::do_klass(OopClosureType* closure, Klass* k) { // Implementation of the non-virtual do_cld dispatch. template -static typename EnableIf::value, void>::type +static typename EnableIf::value, void>::type call_do_cld(void (Receiver::*)(ClassLoaderData*), void (Base::*)(ClassLoaderData*), OopClosureType* closure, ClassLoaderData* cld) { closure->do_cld(cld); } template -static typename EnableIf::value, void>::type +static typename EnableIf::value, void>::type call_do_cld(void (Receiver::*)(ClassLoaderData*), void (Base::*)(ClassLoaderData*), OopClosureType* closure, ClassLoaderData* cld) { closure->OopClosureType::do_cld(cld); } diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 1e897615eafca..d1882c70e2cec 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -566,12 +566,6 @@ void Metaspace::initialize_class_space(ReservedSpace rs) { "wrong alignment"); MetaspaceContext::initialize_class_space_context(rs); - - // This does currently not work because rs may be the result of a split - // operation and NMT seems not to be able to handle splits. - // Will be fixed with JDK-8243535. - // MemTracker::record_virtual_memory_type((address)rs.base(), mtClass); - } // Returns true if class space has been setup (initialize_class_space). @@ -840,6 +834,9 @@ void Metaspace::global_initialize() { CompressedClassSpaceSize)); } + // Mark class space as such + MemTracker::record_virtual_memory_type((address)rs.base(), mtClass); + // Initialize space Metaspace::initialize_class_space(rs); diff --git a/src/hotspot/share/memory/metaspace/counters.hpp b/src/hotspot/share/memory/metaspace/counters.hpp index 066d36279a6fd..1c474eb9b5258 100644 --- a/src/hotspot/share/memory/metaspace/counters.hpp +++ b/src/hotspot/share/memory/metaspace/counters.hpp @@ -26,11 +26,12 @@ #ifndef SHARE_MEMORY_METASPACE_COUNTERS_HPP #define SHARE_MEMORY_METASPACE_COUNTERS_HPP -#include "metaprogramming/isSigned.hpp" #include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include + namespace metaspace { // We seem to be counting a lot of things which makes it worthwhile to @@ -43,7 +44,7 @@ class AbstractCounter { T _c; // Only allow unsigned values for now - STATIC_ASSERT(IsSigned::value == false); + STATIC_ASSERT(std::is_signed::value == false); public: @@ -88,7 +89,7 @@ class AbstractAtomicCounter { volatile T _c; // Only allow unsigned values for now - STATIC_ASSERT(IsSigned::value == false); + STATIC_ASSERT(std::is_signed::value == false); public: diff --git a/src/hotspot/share/memory/resourceArea.cpp b/src/hotspot/share/memory/resourceArea.cpp index 3d12ccd4ca77f..0b7b100924d92 100644 --- a/src/hotspot/share/memory/resourceArea.cpp +++ b/src/hotspot/share/memory/resourceArea.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,18 @@ void ResourceArea::bias_to(MEMFLAGS new_flags) { #ifdef ASSERT +ResourceMark::ResourceMark(ResourceArea* area, Thread* thread) : + _impl(area), + _thread(thread), + _previous_resource_mark(nullptr) +{ + if (_thread != nullptr) { + assert(_thread == Thread::current(), "not the current thread"); + _previous_resource_mark = _thread->current_resource_mark(); + _thread->set_current_resource_mark(this); + } +} + void ResourceArea::verify_has_resource_mark() { if (_nesting <= 0) { // Only report the first occurrence of an allocating thread that diff --git a/src/hotspot/share/memory/resourceArea.hpp b/src/hotspot/share/memory/resourceArea.hpp index d8ec3580b88bb..8e5abf1ec00f8 100644 --- a/src/hotspot/share/memory/resourceArea.hpp +++ b/src/hotspot/share/memory/resourceArea.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -195,17 +195,7 @@ class ResourceMark: public StackObj { #ifndef ASSERT ResourceMark(ResourceArea* area, Thread* thread) : _impl(area) {} #else - ResourceMark(ResourceArea* area, Thread* thread) : - _impl(area), - _thread(thread), - _previous_resource_mark(nullptr) - { - if (_thread != nullptr) { - assert(_thread == Thread::current(), "not the current thread"); - _previous_resource_mark = _thread->current_resource_mark(); - _thread->set_current_resource_mark(this); - } - } + ResourceMark(ResourceArea* area, Thread* thread); #endif // ASSERT public: diff --git a/src/hotspot/share/metaprogramming/conditional.hpp b/src/hotspot/share/metaprogramming/conditional.hpp deleted file mode 100644 index 2d4fe1a86e8f7..0000000000000 --- a/src/hotspot/share/metaprogramming/conditional.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_CONDITIONAL_HPP -#define SHARE_METAPROGRAMMING_CONDITIONAL_HPP - -#include "memory/allocation.hpp" - -// This trait evaluates its typedef called "type" to TrueType iff the condition -// is true. Otherwise it evaluates to FalseType. - -template -struct Conditional: AllStatic { - typedef TrueType type; -}; - -template -struct Conditional: AllStatic { - typedef FalseType type; -}; - -#endif // SHARE_METAPROGRAMMING_CONDITIONAL_HPP diff --git a/src/hotspot/share/metaprogramming/decay.hpp b/src/hotspot/share/metaprogramming/decay.hpp deleted file mode 100644 index 4dff0500f134d..0000000000000 --- a/src/hotspot/share/metaprogramming/decay.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_DECAY_HPP -#define SHARE_METAPROGRAMMING_DECAY_HPP - -#include "memory/allocation.hpp" -#include "metaprogramming/removeCV.hpp" -#include "metaprogramming/removeReference.hpp" - -// This trait trims the type from CV qualifiers and references. -// This trait provides a subset of the functionality of std::decay; -// array types and function types are not supported here. - -template -struct Decay: AllStatic { - typedef typename RemoveCV::type>::type type; -}; - -#endif // SHARE_METAPROGRAMMING_DECAY_HPP diff --git a/src/hotspot/share/metaprogramming/integralConstant.hpp b/src/hotspot/share/metaprogramming/integralConstant.hpp deleted file mode 100644 index ce22849e3bec6..0000000000000 --- a/src/hotspot/share/metaprogramming/integralConstant.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_INTEGRALCONSTANT_HPP -#define SHARE_METAPROGRAMMING_INTEGRALCONSTANT_HPP - - -// An Integral Constant is a class providing a compile-time value of an -// integral type. An Integral Constant is also a nullary metafunction, -// returning itself. An integral constant object is implicitly -// convertible to the associated value. -// -// A type n is a model of Integral Constant if it meets the following -// requirements: -// -// n::ValueType : The integral type of n::value -// n::value : An integral constant expression -// n::type : IsSame::value is true -// n::value_type const c = n() : c == n::value - -// A model of the Integer Constant concept. -// T is an integral type, and is the value_type. -// v is an integral constant, and is the value. -template -struct IntegralConstant { - typedef T value_type; - static const value_type value = v; - typedef IntegralConstant type; - operator value_type() { return value; } -}; - -// A bool valued IntegralConstant whose value is true. -typedef IntegralConstant TrueType; - -// A bool valued IntegralConstant whose value is false. -typedef IntegralConstant FalseType; - -#endif // SHARE_METAPROGRAMMING_INTEGRALCONSTANT_HPP diff --git a/src/hotspot/share/metaprogramming/isArray.hpp b/src/hotspot/share/metaprogramming/isArray.hpp deleted file mode 100644 index 10040ccdf4dd1..0000000000000 --- a/src/hotspot/share/metaprogramming/isArray.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_ISARRAY_HPP -#define SHARE_METAPROGRAMMING_ISARRAY_HPP - -#include "metaprogramming/integralConstant.hpp" - -template struct IsArray: public FalseType {}; - -template struct IsArray: public TrueType {}; -template struct IsArray: public TrueType {}; - -#endif // SHARE_METAPROGRAMMING_ISARRAY_HPP diff --git a/src/hotspot/share/metaprogramming/isConst.hpp b/src/hotspot/share/metaprogramming/isConst.hpp deleted file mode 100644 index 744e9c665eccd..0000000000000 --- a/src/hotspot/share/metaprogramming/isConst.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_ISCONST_HPP -#define SHARE_METAPROGRAMMING_ISCONST_HPP - -#include "metaprogramming/integralConstant.hpp" - -template struct IsConst: public FalseType {}; -template struct IsConst: public TrueType {}; - -#endif // SHARE_METAPROGRAMMING_ISCONST_HPP diff --git a/src/hotspot/share/metaprogramming/isFloatingPoint.hpp b/src/hotspot/share/metaprogramming/isFloatingPoint.hpp deleted file mode 100644 index cbc2d838fe603..0000000000000 --- a/src/hotspot/share/metaprogramming/isFloatingPoint.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_ISFLOATINGPOINT_HPP -#define SHARE_METAPROGRAMMING_ISFLOATINGPOINT_HPP - -#include "metaprogramming/integralConstant.hpp" - -// This metafunction returns true iff the type T (irrespective of CV qualifiers) -// is a floating point type. - -template struct IsFloatingPoint: public FalseType {}; - -template <> struct IsFloatingPoint: public TrueType {}; -template <> struct IsFloatingPoint: public TrueType {}; -template <> struct IsFloatingPoint: public TrueType {}; -template <> struct IsFloatingPoint: public TrueType {}; - -template <> struct IsFloatingPoint: public TrueType {}; -template <> struct IsFloatingPoint: public TrueType {}; -template <> struct IsFloatingPoint: public TrueType {}; -template <> struct IsFloatingPoint: public TrueType {}; - -template <> struct IsFloatingPoint: public TrueType {}; -template <> struct IsFloatingPoint: public TrueType {}; -template <> struct IsFloatingPoint: public TrueType {}; -template <> struct IsFloatingPoint: public TrueType {}; - -#endif // SHARE_METAPROGRAMMING_ISFLOATINGPOINT_HPP diff --git a/src/hotspot/share/metaprogramming/isIntegral.hpp b/src/hotspot/share/metaprogramming/isIntegral.hpp deleted file mode 100644 index cbae6bd5efc64..0000000000000 --- a/src/hotspot/share/metaprogramming/isIntegral.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - - -#ifndef SHARE_METAPROGRAMMING_ISINTEGRAL_HPP -#define SHARE_METAPROGRAMMING_ISINTEGRAL_HPP - -#include "metaprogramming/integralConstant.hpp" -#include "metaprogramming/isSigned.hpp" -#include "metaprogramming/removeCV.hpp" -#include - -// This metafunction returns true iff the type T (irrespective of CV qualifiers) -// is an integral type. Note that this is false for enums. - -template -struct IsIntegral - : public IntegralConstant::type>::is_integer> -{}; - -// This metafunction returns true iff the type T (irrespective of CV qualifiers) -// is a signed integral type. Note that this is false for enums. - -template -struct IsSignedIntegral - : public IntegralConstant::value && IsSigned::value> -{}; - -// This metafunction returns true iff the type T (irrespective of CV qualifiers) -// is an unsigned integral type. Note that this is false for enums. - -template -struct IsUnsignedIntegral - : public IntegralConstant::value && !IsSigned::value> -{}; - -#endif // SHARE_METAPROGRAMMING_ISINTEGRAL_HPP diff --git a/src/hotspot/share/metaprogramming/isPointer.hpp b/src/hotspot/share/metaprogramming/isPointer.hpp deleted file mode 100644 index aa1bdefa16b53..0000000000000 --- a/src/hotspot/share/metaprogramming/isPointer.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_ISPOINTER_HPP -#define SHARE_METAPROGRAMMING_ISPOINTER_HPP - -#include "metaprogramming/integralConstant.hpp" - -// This metafunction returns true iff the type T is (irrespective of CV qualifiers) -// a pointer type. - -template class IsPointer: public FalseType {}; - -template class IsPointer: public TrueType {}; -template class IsPointer: public TrueType {}; -template class IsPointer: public TrueType {}; -template class IsPointer: public TrueType {}; - -#endif // SHARE_METAPROGRAMMING_ISPOINTER_HPP diff --git a/src/hotspot/share/metaprogramming/isSame.hpp b/src/hotspot/share/metaprogramming/isSame.hpp deleted file mode 100644 index aaaa621d1511b..0000000000000 --- a/src/hotspot/share/metaprogramming/isSame.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_ISSAME_HPP -#define SHARE_METAPROGRAMMING_ISSAME_HPP - -#include "metaprogramming/integralConstant.hpp" - -// This trait returns true iff the two types X and Y are the same - -template -struct IsSame: public FalseType {}; - -template -struct IsSame: public TrueType {}; - -#endif // SHARE_METAPROGRAMMING_ISSAME_HPP diff --git a/src/hotspot/share/metaprogramming/isSigned.hpp b/src/hotspot/share/metaprogramming/isSigned.hpp deleted file mode 100644 index f04065d2180a4..0000000000000 --- a/src/hotspot/share/metaprogramming/isSigned.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_ISSIGNED_HPP -#define SHARE_METAPROGRAMMING_ISSIGNED_HPP - -#include "metaprogramming/integralConstant.hpp" -#include "metaprogramming/removeCV.hpp" -#include - -template -struct IsSigned - : public IntegralConstant::type>::is_signed> -{}; - -#endif // SHARE_METAPROGRAMMING_ISSIGNED_HPP diff --git a/src/hotspot/share/metaprogramming/isVolatile.hpp b/src/hotspot/share/metaprogramming/isVolatile.hpp deleted file mode 100644 index cd51e3b53e5cd..0000000000000 --- a/src/hotspot/share/metaprogramming/isVolatile.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_ISVOLATILE_HPP -#define SHARE_METAPROGRAMMING_ISVOLATILE_HPP - -#include "metaprogramming/integralConstant.hpp" - -template struct IsVolatile: public FalseType {}; -template struct IsVolatile: public TrueType {}; - -#endif // SHARE_METAPROGRAMMING_ISVOLATILE_HPP diff --git a/src/hotspot/share/metaprogramming/removeCV.hpp b/src/hotspot/share/metaprogramming/removeCV.hpp deleted file mode 100644 index 0c0b2f47fa777..0000000000000 --- a/src/hotspot/share/metaprogramming/removeCV.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_REMOVECV_HPP -#define SHARE_METAPROGRAMMING_REMOVECV_HPP - -#include "memory/allocation.hpp" - -template -struct RemoveCV: AllStatic { - typedef T type; -}; - -template -struct RemoveCV: AllStatic { - typedef T type; -}; - -template -struct RemoveCV: AllStatic { - typedef T type; -}; - -template -struct RemoveCV: AllStatic { - typedef T type; -}; - -#endif // SHARE_METAPROGRAMMING_REMOVECV_HPP diff --git a/src/hotspot/share/metaprogramming/removeExtent.hpp b/src/hotspot/share/metaprogramming/removeExtent.hpp deleted file mode 100644 index f188e0db264e4..0000000000000 --- a/src/hotspot/share/metaprogramming/removeExtent.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_REMOVEEXTENT_HPP -#define SHARE_METAPROGRAMMING_REMOVEEXTENT_HPP - -#include "memory/allocation.hpp" - -template struct RemoveExtent: AllStatic { typedef T type; }; - -template struct RemoveExtent: AllStatic { typedef T type; }; -template struct RemoveExtent: AllStatic { typedef T type; }; - -#endif // SHARE_METAPROGRAMMING_REMOVEEXTENT_HPP diff --git a/src/hotspot/share/metaprogramming/removePointer.hpp b/src/hotspot/share/metaprogramming/removePointer.hpp deleted file mode 100644 index be0f5b6e6fb7a..0000000000000 --- a/src/hotspot/share/metaprogramming/removePointer.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_REMOVEPOINTER_HPP -#define SHARE_METAPROGRAMMING_REMOVEPOINTER_HPP - -#include "memory/allocation.hpp" - -// This metafunction returns for a type T either the underlying type behind -// the pointer iff T is a pointer type (irrespective of CV qualifiers), -// or the same type T if T is not a pointer type. - -template struct RemovePointer: AllStatic { typedef T type; }; - -template struct RemovePointer: AllStatic { typedef T type; }; -template struct RemovePointer: AllStatic { typedef T type; }; -template struct RemovePointer: AllStatic { typedef T type; }; -template struct RemovePointer: AllStatic { typedef T type; }; - -#endif // SHARE_METAPROGRAMMING_REMOVEPOINTER_HPP diff --git a/src/hotspot/share/metaprogramming/removeReference.hpp b/src/hotspot/share/metaprogramming/removeReference.hpp deleted file mode 100644 index 274c327a0ade0..0000000000000 --- a/src/hotspot/share/metaprogramming/removeReference.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_METAPROGRAMMING_REMOVEREFERENCE_HPP -#define SHARE_METAPROGRAMMING_REMOVEREFERENCE_HPP - -#include "memory/allocation.hpp" - -// This metafunction returns for a type T either the underlying type behind -// the reference iff T is a reference type, or the same type T if T is not -// a reference type. - -template struct RemoveReference: AllStatic { typedef T type; }; - -template struct RemoveReference: AllStatic { typedef T type; }; - -#endif // SHARE_METAPROGRAMMING_REMOVEREFERENCE_HPP diff --git a/src/hotspot/share/oops/accessBackend.hpp b/src/hotspot/share/oops/accessBackend.hpp index 4fa92c53c162b..d267e217734f2 100644 --- a/src/hotspot/share/oops/accessBackend.hpp +++ b/src/hotspot/share/oops/accessBackend.hpp @@ -27,21 +27,14 @@ #include "gc/shared/barrierSetConfig.hpp" #include "memory/allocation.hpp" -#include "metaprogramming/conditional.hpp" -#include "metaprogramming/decay.hpp" #include "metaprogramming/enableIf.hpp" -#include "metaprogramming/integralConstant.hpp" -#include "metaprogramming/isFloatingPoint.hpp" -#include "metaprogramming/isIntegral.hpp" -#include "metaprogramming/isPointer.hpp" -#include "metaprogramming/isSame.hpp" -#include "metaprogramming/isVolatile.hpp" #include "oops/accessDecorators.hpp" #include "oops/oopsHierarchy.hpp" #include "runtime/globals.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include // This metafunction returns either oop or narrowOop depending on whether // an access needs to use compressed oops or not. @@ -49,7 +42,7 @@ template struct HeapOopType: AllStatic { static const bool needs_oop_compress = HasDecorator::value && HasDecorator::value; - typedef typename Conditional::type type; + using type = std::conditional_t; }; namespace AccessInternal { @@ -67,18 +60,18 @@ namespace AccessInternal { }; template - struct MustConvertCompressedOop: public IntegralConstant::value && - IsSame::type, narrowOop>::value && - IsSame::value> {}; + std::is_same::type, narrowOop>::value && + std::is_same::value> {}; // This metafunction returns an appropriate oop type if the value is oop-like // and otherwise returns the same type T. template struct EncodedType: AllStatic { - typedef typename Conditional< - HasDecorator::value, - typename HeapOopType::type, T>::type type; + using type = std::conditional_t::value, + typename HeapOopType::type, + T>; }; template @@ -92,9 +85,9 @@ namespace AccessInternal { // locking to support wide atomics or not. template #ifdef SUPPORTS_NATIVE_CX8 - struct PossiblyLockedAccess: public IntegralConstant {}; + struct PossiblyLockedAccess: public std::false_type {}; #else - struct PossiblyLockedAccess: public IntegralConstant 4)> {}; + struct PossiblyLockedAccess: public std::integral_constant 4)> {}; #endif template @@ -425,7 +418,7 @@ namespace AccessInternal { // to compile, as desired. template struct OopOrNarrowOop: AllStatic { - typedef typename OopOrNarrowOopInternal::type>::type type; + typedef typename OopOrNarrowOopInternal>::type type; }; inline void* field_addr(oop base, ptrdiff_t byte_offset) { @@ -617,7 +610,7 @@ namespace AccessInternal { // not possible. struct PreRuntimeDispatch: AllStatic { template - struct CanHardwireRaw: public IntegralConstant< + struct CanHardwireRaw: public std::integral_constant< bool, !HasDecorator::value || // primitive access !HasDecorator::value || // don't care about compressed oops (oop* address) @@ -1081,20 +1074,20 @@ namespace AccessInternal { // If this fails to compile, then you have sent in something that is // not recognized as a valid primitive type to a primitive Access function. STATIC_ASSERT((HasDecorator::value || // oops have already been validated - (IsPointer::value || IsIntegral::value) || - IsFloatingPoint::value)); // not allowed primitive type + (std::is_pointer::value || std::is_integral::value) || + std::is_floating_point::value)); // not allowed primitive type } template inline void store(P* addr, T value) { verify_types(); - typedef typename Decay

    ::type DecayedP; - typedef typename Decay::type DecayedT; + using DecayedP = std::decay_t

    ; + using DecayedT = std::decay_t; DecayedT decayed_value = value; // If a volatile address is passed in but no memory ordering decorator, // set the memory ordering to MO_RELAXED by default. const DecoratorSet expanded_decorators = DecoratorFixup< - (IsVolatile

    ::value && !HasDecorator::value) ? + (std::is_volatile

    ::value && !HasDecorator::value) ? (MO_RELAXED | decorators) : decorators>::value; store_reduce_types(const_cast(addr), decayed_value); } @@ -1102,7 +1095,7 @@ namespace AccessInternal { template inline void store_at(oop base, ptrdiff_t offset, T value) { verify_types(); - typedef typename Decay::type DecayedT; + using DecayedT = std::decay_t; DecayedT decayed_value = value; const DecoratorSet expanded_decorators = DecoratorFixup::value ? @@ -1113,14 +1106,14 @@ namespace AccessInternal { template inline T load(P* addr) { verify_types(); - typedef typename Decay

    ::type DecayedP; - typedef typename Conditional::value, - typename OopOrNarrowOop::type, - typename Decay::type>::type DecayedT; + using DecayedP = std::decay_t

    ; + using DecayedT = std::conditional_t::value, + typename OopOrNarrowOop::type, + std::decay_t>; // If a volatile address is passed in but no memory ordering decorator, // set the memory ordering to MO_RELAXED by default. const DecoratorSet expanded_decorators = DecoratorFixup< - (IsVolatile

    ::value && !HasDecorator::value) ? + (std::is_volatile

    ::value && !HasDecorator::value) ? (MO_RELAXED | decorators) : decorators>::value; return load_reduce_types(const_cast(addr)); } @@ -1128,9 +1121,9 @@ namespace AccessInternal { template inline T load_at(oop base, ptrdiff_t offset) { verify_types(); - typedef typename Conditional::value, - typename OopOrNarrowOop::type, - typename Decay::type>::type DecayedT; + using DecayedT = std::conditional_t::value, + typename OopOrNarrowOop::type, + std::decay_t>; // Expand the decorators (figure out sensible defaults) // Potentially remember if we need compressed oop awareness const DecoratorSet expanded_decorators = DecoratorFixup inline T atomic_cmpxchg(P* addr, T compare_value, T new_value) { verify_types(); - typedef typename Decay

    ::type DecayedP; - typedef typename Decay::type DecayedT; + using DecayedP = std::decay_t

    ; + using DecayedT = std::decay_t; DecayedT new_decayed_value = new_value; DecayedT compare_decayed_value = compare_value; const DecoratorSet expanded_decorators = DecoratorFixup< @@ -1157,7 +1150,7 @@ namespace AccessInternal { template inline T atomic_cmpxchg_at(oop base, ptrdiff_t offset, T compare_value, T new_value) { verify_types(); - typedef typename Decay::type DecayedT; + using DecayedT = std::decay_t; DecayedT new_decayed_value = new_value; DecayedT compare_decayed_value = compare_value; // Determine default memory ordering @@ -1175,8 +1168,8 @@ namespace AccessInternal { template inline T atomic_xchg(P* addr, T new_value) { verify_types(); - typedef typename Decay

    ::type DecayedP; - typedef typename Decay::type DecayedT; + using DecayedP = std::decay_t

    ; + using DecayedT = std::decay_t; DecayedT new_decayed_value = new_value; // atomic_xchg is only available in SEQ_CST flavour. const DecoratorSet expanded_decorators = DecoratorFixup::value; @@ -1187,7 +1180,7 @@ namespace AccessInternal { template inline T atomic_xchg_at(oop base, ptrdiff_t offset, T new_value) { verify_types(); - typedef typename Decay::type DecayedT; + using DecayedT = std::decay_t; DecayedT new_decayed_value = new_value; // atomic_xchg is only available in SEQ_CST flavour. const DecoratorSet expanded_decorators = DecoratorFixup::value || - (IsSame::value || IsIntegral::value) || - IsFloatingPoint::value)); // arraycopy allows type erased void elements - typedef typename Decay::type DecayedT; + (std::is_same::value || std::is_integral::value) || + std::is_floating_point::value)); // arraycopy allows type erased void elements + using DecayedT = std::decay_t; const DecoratorSet expanded_decorators = DecoratorFixup::value; return arraycopy_reduce_types(src_obj, src_offset_in_bytes, const_cast(src_raw), dst_obj, dst_offset_in_bytes, const_cast(dst_raw), diff --git a/src/hotspot/share/oops/accessBackend.inline.hpp b/src/hotspot/share/oops/accessBackend.inline.hpp index 1dacb48427f6c..677af8115c3d2 100644 --- a/src/hotspot/share/oops/accessBackend.inline.hpp +++ b/src/hotspot/share/oops/accessBackend.inline.hpp @@ -34,6 +34,8 @@ #include "runtime/atomic.hpp" #include "runtime/orderAccess.hpp" +#include + template template inline typename EnableIf< @@ -251,7 +253,7 @@ RawAccessBarrier::atomic_cmpxchg_maybe_locked(void* addr, T compare_value, T } class RawAccessBarrierArrayCopy: public AllStatic { - template struct IsHeapWordSized: public IntegralConstant { }; + template struct IsHeapWordSized: public std::integral_constant { }; public: template static inline typename EnableIf< @@ -334,7 +336,7 @@ class RawAccessBarrierArrayCopy: public AllStatic { } }; -template<> struct RawAccessBarrierArrayCopy::IsHeapWordSized: public IntegralConstant { }; +template<> struct RawAccessBarrierArrayCopy::IsHeapWordSized: public std::false_type { }; template template diff --git a/src/hotspot/share/oops/accessDecorators.hpp b/src/hotspot/share/oops/accessDecorators.hpp index 21bcff9113d31..f7a3462f59a7c 100644 --- a/src/hotspot/share/oops/accessDecorators.hpp +++ b/src/hotspot/share/oops/accessDecorators.hpp @@ -27,9 +27,10 @@ #include "gc/shared/barrierSetConfig.hpp" #include "memory/allocation.hpp" -#include "metaprogramming/integralConstant.hpp" #include "utilities/globalDefinitions.hpp" +#include + // A decorator is an attribute or property that affects the way a memory access is performed in some way. // There are different groups of decorators. Some have to do with memory ordering, others to do with, // e.g. strength of references, strength of GC barriers, or whether compression should be applied or not. @@ -41,7 +42,7 @@ typedef uint64_t DecoratorSet; // The HasDecorator trait can help at compile-time determining whether a decorator set // has an intersection with a certain other decorator set template -struct HasDecorator: public IntegralConstant {}; +struct HasDecorator: public std::integral_constant {}; // == General Decorators == // * DECORATORS_NONE: This is the name for the empty decorator set (in absence of other decorators). diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp index 631afa0d32155..74d902c86f0e8 100644 --- a/src/hotspot/share/oops/generateOopMap.cpp +++ b/src/hotspot/share/oops/generateOopMap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1732,7 +1732,7 @@ void GenerateOopMap::ppop(CellTypeState *out) { } void GenerateOopMap::ppush1(CellTypeState in) { - assert(in.is_reference() | in.is_value(), "sanity check"); + assert(in.is_reference() || in.is_value(), "sanity check"); push(in); } diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index fdee66a8866e3..5e0ef10488cf1 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -3427,11 +3427,13 @@ void InstanceKlass::print_on(outputStream* st) const { } } st->print(BULLET"method ordering: "); method_ordering()->print_value_on(st); st->cr(); - st->print(BULLET"default_methods: "); default_methods()->print_value_on(st); st->cr(); - if (Verbose && default_methods() != NULL) { - Array* method_array = default_methods(); - for (int i = 0; i < method_array->length(); i++) { - st->print("%d : ", i); method_array->at(i)->print_value(); st->cr(); + if (default_methods() != nullptr) { + st->print(BULLET"default_methods: "); default_methods()->print_value_on(st); st->cr(); + if (Verbose) { + Array* method_array = default_methods(); + for (int i = 0; i < method_array->length(); i++) { + st->print("%d : ", i); method_array->at(i)->print_value(); st->cr(); + } } } if (default_vtable_indices() != NULL) { @@ -3941,18 +3943,18 @@ void InstanceKlass::set_init_state(ClassState state) { // Globally, there is at least one previous version of a class to walk // during class unloading, which is saved because old methods in the class // are still running. Otherwise the previous version list is cleaned up. -bool InstanceKlass::_has_previous_versions = false; +bool InstanceKlass::_should_clean_previous_versions = false; // Returns true if there are previous versions of a class for class // unloading only. Also resets the flag to false. purge_previous_version // will set the flag to true if there are any left, i.e., if there's any // work to do for next time. This is to avoid the expensive code cache // walk in CLDG::clean_deallocate_lists(). -bool InstanceKlass::has_previous_versions_and_reset() { - bool ret = _has_previous_versions; - log_trace(redefine, class, iklass, purge)("Class unloading: has_previous_versions = %s", +bool InstanceKlass::should_clean_previous_versions_and_reset() { + bool ret = _should_clean_previous_versions; + log_trace(redefine, class, iklass, purge)("Class unloading: should_clean_previous_versions = %s", ret ? "true" : "false"); - _has_previous_versions = false; + _should_clean_previous_versions = false; return ret; } @@ -4027,12 +4029,17 @@ void InstanceKlass::purge_previous_version_list() { version++; continue; } else { - log_trace(redefine, class, iklass, purge)("previous version " INTPTR_FORMAT " is alive", p2i(pv_node)); assert(pvcp->pool_holder() != NULL, "Constant pool with no holder"); guarantee (!loader_data->is_unloading(), "unloaded classes can't be on the stack"); live_count++; - // found a previous version for next time we do class unloading - _has_previous_versions = true; + if (pvcp->is_shared()) { + // Shared previous versions can never be removed so no cleaning is needed. + log_trace(redefine, class, iklass, purge)("previous version " PTR_FORMAT " is shared", p2i(pv_node)); + } else { + // Previous version alive, set that clean is needed for next time. + _should_clean_previous_versions = true; + log_trace(redefine, class, iklass, purge)("previous version " PTR_FORMAT " is alive", p2i(pv_node)); + } } // next previous version @@ -4132,13 +4139,19 @@ void InstanceKlass::add_previous_version(InstanceKlass* scratch_class, return; } - // Add previous version if any methods are still running. - // Set has_previous_version flag for processing during class unloading. - _has_previous_versions = true; - log_trace(redefine, class, iklass, add) ("scratch class added; one of its methods is on_stack."); + // Add previous version if any methods are still running or if this is + // a shared class which should never be removed. assert(scratch_class->previous_versions() == NULL, "shouldn't have a previous version"); scratch_class->link_previous_versions(previous_versions()); link_previous_versions(scratch_class); + if (cp_ref->is_shared()) { + log_trace(redefine, class, iklass, add) ("scratch class added; class is shared"); + } else { + // We only set clean_previous_versions flag for processing during class + // unloading for non-shared classes. + _should_clean_previous_versions = true; + log_trace(redefine, class, iklass, add) ("scratch class added; one of its methods is on_stack."); + } } // end add_previous_version() #endif // INCLUDE_JVMTI diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 23b773d0195bb..9f03bc50d4886 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -815,7 +815,7 @@ class InstanceKlass: public Klass { } private: - static bool _has_previous_versions; + static bool _should_clean_previous_versions; public: static void purge_previous_versions(InstanceKlass* ik) { if (ik->has_been_redefined()) { @@ -823,8 +823,8 @@ class InstanceKlass: public Klass { } } - static bool has_previous_versions_and_reset(); - static bool has_previous_versions() { return _has_previous_versions; } + static bool should_clean_previous_versions_and_reset(); + static bool should_clean_previous_versions() { return _should_clean_previous_versions; } // JVMTI: Support for caching a class file before it is modified by an agent that can do retransformation void set_cached_class_file(JvmtiCachedClassFileData *data) { @@ -844,7 +844,7 @@ class InstanceKlass: public Klass { #else // INCLUDE_JVMTI static void purge_previous_versions(InstanceKlass* ik) { return; }; - static bool has_previous_versions_and_reset() { return false; } + static bool should_clean_previous_versions_and_reset() { return false; } void set_cached_class_file(JvmtiCachedClassFileData *data) { assert(data == NULL, "unexpected call with JVMTI disabled"); diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp index 797cd3980936b..a597f76a2e69c 100644 --- a/src/hotspot/share/oops/markWord.hpp +++ b/src/hotspot/share/oops/markWord.hpp @@ -25,11 +25,12 @@ #ifndef SHARE_OOPS_MARKWORD_HPP #define SHARE_OOPS_MARKWORD_HPP -#include "metaprogramming/integralConstant.hpp" #include "metaprogramming/primitiveConversions.hpp" #include "oops/oopsHierarchy.hpp" #include "runtime/globals.hpp" +#include + // The markWord describes the header of an object. // // Bit-format of an object header (most significant first, big endian layout below): @@ -356,7 +357,7 @@ class markWord { // Support atomic operations. template<> -struct PrimitiveConversions::Translate : public TrueType { +struct PrimitiveConversions::Translate : public std::true_type { typedef markWord Value; typedef uintptr_t Decayed; diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index fa3a2f2dbccb1..cce34ff740933 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -2242,10 +2242,15 @@ bool Method::is_method_id(jmethodID mid) { Method* Method::checked_resolve_jmethod_id(jmethodID mid) { if (mid == NULL) return NULL; Method* o = resolve_jmethod_id(mid); - if (o == NULL || o == JNIMethodBlock::_free_method || !((Metadata*)o)->is_method()) { + if (o == NULL || o == JNIMethodBlock::_free_method) { return NULL; } - return o; + // Method should otherwise be valid. Assert for testing. + assert(is_valid_method(o), "should be valid jmethodid"); + // If the method's class holder object is unreferenced, but not yet marked as + // unloaded, we need to return NULL here too because after a safepoint, its memory + // will be reclaimed. + return o->method_holder()->is_loader_alive() ? o : NULL; }; void Method::set_on_stack(const bool value) { diff --git a/src/hotspot/share/oops/oopHandle.hpp b/src/hotspot/share/oops/oopHandle.hpp index fd4c9679219e7..96cbf609e5e23 100644 --- a/src/hotspot/share/oops/oopHandle.hpp +++ b/src/hotspot/share/oops/oopHandle.hpp @@ -28,6 +28,8 @@ #include "metaprogramming/primitiveConversions.hpp" #include "oops/oopsHierarchy.hpp" +#include + class OopStorage; // Simple classes for wrapping oop and atomically accessed oop pointers @@ -77,7 +79,7 @@ class OopHandle { // Convert OopHandle to oop* template<> -struct PrimitiveConversions::Translate : public TrueType { +struct PrimitiveConversions::Translate : public std::true_type { typedef OopHandle Value; typedef oop* Decayed; diff --git a/src/hotspot/share/oops/oopsHierarchy.hpp b/src/hotspot/share/oops/oopsHierarchy.hpp index a7f8d5ba6533d..7bbfd558472ff 100644 --- a/src/hotspot/share/oops/oopsHierarchy.hpp +++ b/src/hotspot/share/oops/oopsHierarchy.hpp @@ -25,10 +25,11 @@ #ifndef SHARE_OOPS_OOPSHIERARCHY_HPP #define SHARE_OOPS_OOPSHIERARCHY_HPP -#include "metaprogramming/integralConstant.hpp" #include "metaprogramming/primitiveConversions.hpp" #include "utilities/globalDefinitions.hpp" +#include + // OBJECT hierarchy // This hierarchy is a representation hierarchy, i.e. if A is a superclass // of B, A's representation is a prefix of B's representation. @@ -107,7 +108,7 @@ class oop { }; template<> -struct PrimitiveConversions::Translate : public TrueType { +struct PrimitiveConversions::Translate : public std::true_type { typedef oop Value; typedef oopDesc* Decayed; @@ -115,31 +116,31 @@ struct PrimitiveConversions::Translate : public TrueType { static Value recover(Decayed x) { return oop(x); } }; -#define DEF_OOP(type) \ - class type##OopDesc; \ - class type##Oop : public oop { \ - public: \ - type##Oop() : oop() {} \ - type##Oop(const type##Oop& o) : oop(o) {} \ - type##Oop(const oop& o) : oop(o) {} \ - type##Oop(type##OopDesc* o) : oop((oopDesc*)o) {} \ - operator type##OopDesc* () const { return (type##OopDesc*)obj(); } \ - type##OopDesc* operator->() const { \ - return (type##OopDesc*)obj(); \ - } \ - type##Oop& operator=(const type##Oop& o) { \ - oop::operator=(o); \ - return *this; \ - } \ - }; \ - \ - template<> \ - struct PrimitiveConversions::Translate : public TrueType { \ - typedef type##Oop Value; \ - typedef type##OopDesc* Decayed; \ - \ - static Decayed decay(Value x) { return (type##OopDesc*)x.obj(); } \ - static Value recover(Decayed x) { return type##Oop(x); } \ +#define DEF_OOP(type) \ + class type##OopDesc; \ + class type##Oop : public oop { \ + public: \ + type##Oop() : oop() {} \ + type##Oop(const type##Oop& o) : oop(o) {} \ + type##Oop(const oop& o) : oop(o) {} \ + type##Oop(type##OopDesc* o) : oop((oopDesc*)o) {} \ + operator type##OopDesc* () const { return (type##OopDesc*)obj(); } \ + type##OopDesc* operator->() const { \ + return (type##OopDesc*)obj(); \ + } \ + type##Oop& operator=(const type##Oop& o) { \ + oop::operator=(o); \ + return *this; \ + } \ + }; \ + \ + template<> \ + struct PrimitiveConversions::Translate : public std::true_type { \ + typedef type##Oop Value; \ + typedef type##OopDesc* Decayed; \ + \ + static Decayed decay(Value x) { return (type##OopDesc*)x.obj(); } \ + static Value recover(Decayed x) { return type##Oop(x); } \ }; DEF_OOP(instance); diff --git a/src/hotspot/share/oops/symbol.cpp b/src/hotspot/share/oops/symbol.cpp index 581a5f8f6dd83..5912dc5b9d637 100644 --- a/src/hotspot/share/oops/symbol.cpp +++ b/src/hotspot/share/oops/symbol.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ * */ - #include "precompiled.hpp" #include "cds/metaspaceShared.hpp" #include "classfile/altHashing.hpp" @@ -53,6 +52,7 @@ uint32_t Symbol::pack_hash_and_refcount(short hash, int refcount) { } Symbol::Symbol(const u1* name, int length, int refcount) { + assert(length <= max_length(), "SymbolTable should have caught this!"); _hash_and_refcount = pack_hash_and_refcount((short)os::random(), refcount); _length = length; // _body[0..1] are allocated in the header just by coincidence in the current @@ -387,11 +387,9 @@ void Symbol::print() const { print_on(tty); } // The print_value functions are present in all builds, to support the // disassembler and error reporting. void Symbol::print_value_on(outputStream* st) const { - st->print("'"); - for (int i = 0; i < utf8_length(); i++) { - st->print("%c", char_at(i)); - } - st->print("'"); + st->print_raw("'", 1); + st->print_raw((const char*)base(), utf8_length()); + st->print_raw("'", 1); } void Symbol::print_value() const { print_value_on(tty); } diff --git a/src/hotspot/share/oops/symbol.hpp b/src/hotspot/share/oops/symbol.hpp index f1f51fc2d1842..96562d08a8792 100644 --- a/src/hotspot/share/oops/symbol.hpp +++ b/src/hotspot/share/oops/symbol.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -130,6 +130,7 @@ class Symbol : public MetaspaceObj { return (int)heap_word_size(byte_size(length)); } + // Constructor is private for use only by SymbolTable. Symbol(const u1* name, int length, int refcount); void* operator new(size_t size, int len) throw(); void* operator new(size_t size, int len, Arena* arena) throw(); diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 959f059fa6b38..35e8e2bdc1ce1 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1257,6 +1257,138 @@ const Type *MinINode::add_ring( const Type *t0, const Type *t1 ) const { return TypeInt::make( MIN2(r0->_lo,r1->_lo), MIN2(r0->_hi,r1->_hi), MAX2(r0->_widen,r1->_widen) ); } +// Collapse the "addition with overflow-protection" pattern, and the symmetrical +// "subtraction with underflow-protection" pattern. These are created during the +// unrolling, when we have to adjust the limit by subtracting the stride, but want +// to protect against underflow: MaxL(SubL(limit, stride), min_jint). +// If we have more than one of those in a sequence: +// +// x con2 +// | | +// AddL clamp2 +// | | +// Max/MinL con1 +// | | +// AddL clamp1 +// | | +// Max/MinL (n) +// +// We want to collapse it to: +// +// x con1 con2 +// | | | +// | AddLNode (new_con) +// | | +// AddLNode clamp1 +// | | +// Max/MinL (n) +// +// Note: we assume that SubL was already replaced by an AddL, and that the stride +// has its sign flipped: SubL(limit, stride) -> AddL(limit, -stride). +Node* fold_subI_no_underflow_pattern(Node* n, PhaseGVN* phase) { + assert(n->Opcode() == Op_MaxL || n->Opcode() == Op_MinL, "sanity"); + // Check that the two clamps have the correct values. + jlong clamp = (n->Opcode() == Op_MaxL) ? min_jint : max_jint; + auto is_clamp = [&](Node* c) { + const TypeLong* t = phase->type(c)->isa_long(); + return t != nullptr && t->is_con() && + t->get_con() == clamp; + }; + // Check that the constants are negative if MaxL, and positive if MinL. + auto is_sub_con = [&](Node* c) { + const TypeLong* t = phase->type(c)->isa_long(); + return t != nullptr && t->is_con() && + t->get_con() < max_jint && t->get_con() > min_jint && + (t->get_con() < 0) == (n->Opcode() == Op_MaxL); + }; + // Verify the graph level by level: + Node* add1 = n->in(1); + Node* clamp1 = n->in(2); + if (add1->Opcode() == Op_AddL && is_clamp(clamp1)) { + Node* max2 = add1->in(1); + Node* con1 = add1->in(2); + if (max2->Opcode() == n->Opcode() && is_sub_con(con1)) { + Node* add2 = max2->in(1); + Node* clamp2 = max2->in(2); + if (add2->Opcode() == Op_AddL && is_clamp(clamp2)) { + Node* x = add2->in(1); + Node* con2 = add2->in(2); + if (is_sub_con(con2)) { + Node* new_con = phase->transform(new AddLNode(con1, con2)); + Node* new_sub = phase->transform(new AddLNode(x, new_con)); + n->set_req_X(1, new_sub, phase); + return n; + } + } + } + } + return nullptr; +} + +const Type* MaxLNode::add_ring(const Type* t0, const Type* t1) const { + const TypeLong* r0 = t0->is_long(); + const TypeLong* r1 = t1->is_long(); + + return TypeLong::make(MAX2(r0->_lo, r1->_lo), MAX2(r0->_hi, r1->_hi), MAX2(r0->_widen, r1->_widen)); +} + +Node* MaxLNode::Identity(PhaseGVN* phase) { + const TypeLong* t1 = phase->type(in(1))->is_long(); + const TypeLong* t2 = phase->type(in(2))->is_long(); + + // Can we determine maximum statically? + if (t1->_lo >= t2->_hi) { + return in(1); + } else if (t2->_lo >= t1->_hi) { + return in(2); + } + + return MaxNode::Identity(phase); +} + +Node* MaxLNode::Ideal(PhaseGVN* phase, bool can_reshape) { + Node* n = AddNode::Ideal(phase, can_reshape); + if (n != nullptr) { + return n; + } + if (can_reshape) { + return fold_subI_no_underflow_pattern(this, phase); + } + return nullptr; +} + +const Type* MinLNode::add_ring(const Type* t0, const Type* t1) const { + const TypeLong* r0 = t0->is_long(); + const TypeLong* r1 = t1->is_long(); + + return TypeLong::make(MIN2(r0->_lo, r1->_lo), MIN2(r0->_hi, r1->_hi), MIN2(r0->_widen, r1->_widen)); +} + +Node* MinLNode::Identity(PhaseGVN* phase) { + const TypeLong* t1 = phase->type(in(1))->is_long(); + const TypeLong* t2 = phase->type(in(2))->is_long(); + + // Can we determine minimum statically? + if (t1->_lo >= t2->_hi) { + return in(2); + } else if (t2->_lo >= t1->_hi) { + return in(1); + } + + return MaxNode::Identity(phase); +} + +Node* MinLNode::Ideal(PhaseGVN* phase, bool can_reshape) { + Node* n = AddNode::Ideal(phase, can_reshape); + if (n != nullptr) { + return n; + } + if (can_reshape) { + return fold_subI_no_underflow_pattern(this, phase); + } + return nullptr; +} + //------------------------------add_ring--------------------------------------- const Type *MinFNode::add_ring( const Type *t0, const Type *t1 ) const { const TypeF *r0 = t0->is_float_constant(); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 132d796aea407..a2867ad7aac22 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -330,28 +330,38 @@ class MinINode : public MaxNode { // MAXimum of 2 longs. class MaxLNode : public MaxNode { public: - MaxLNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + MaxLNode(Compile* C, Node* in1, Node* in2) : MaxNode(in1, in2) { + init_flags(Flag_is_macro); + C->add_macro_node(this); + } virtual int Opcode() const; - virtual const Type *add_ring(const Type*, const Type*) const { return TypeLong::LONG; } - virtual const Type *add_id() const { return TypeLong::make(min_jlong); } - virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual const Type* add_ring(const Type* t0, const Type* t1) const; + virtual const Type* add_id() const { return TypeLong::make(min_jlong); } + virtual const Type* bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } int max_opcode() const { return Op_MaxL; } int min_opcode() const { return Op_MinL; } + virtual Node* Identity(PhaseGVN* phase); + virtual Node* Ideal(PhaseGVN *phase, bool can_reshape); }; //------------------------------MinLNode--------------------------------------- // MINimum of 2 longs. class MinLNode : public MaxNode { public: - MinLNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + MinLNode(Compile* C, Node* in1, Node* in2) : MaxNode(in1, in2) { + init_flags(Flag_is_macro); + C->add_macro_node(this); + } virtual int Opcode() const; - virtual const Type *add_ring(const Type*, const Type*) const { return TypeLong::LONG; } - virtual const Type *add_id() const { return TypeLong::make(max_jlong); } - virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual const Type* add_ring(const Type* t0, const Type* t1) const; + virtual const Type* add_id() const { return TypeLong::make(max_jlong); } + virtual const Type* bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } int max_opcode() const { return Op_MaxL; } int min_opcode() const { return Op_MinL; } + virtual Node* Identity(PhaseGVN* phase); + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; //------------------------------MaxFNode--------------------------------------- diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index 837bbd91013e2..4025b78ade2fa 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -1177,13 +1177,14 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod* const int receiver_skip = target->is_static() ? 0 : 1; // Cast receiver to its type. if (!target->is_static()) { - Node* arg = kit.argument(0); - const TypeOopPtr* arg_type = arg->bottom_type()->isa_oopptr(); - const Type* sig_type = TypeOopPtr::make_from_klass(signature->accessing_klass()); - if (arg_type != nullptr && !arg_type->higher_equal(sig_type)) { - const Type* recv_type = arg_type->filter_speculative(sig_type); // keep speculative part - Node* cast_obj = gvn.transform(new CheckCastPPNode(kit.control(), arg, recv_type)); - kit.set_argument(0, cast_obj); + Node* recv = kit.argument(0); + Node* casted_recv = kit.maybe_narrow_object_type(recv, signature->accessing_klass()); + if (casted_recv->is_top()) { + print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(), + "argument types mismatch"); + return nullptr; // FIXME: effectively dead; issue a halt node instead + } else if (casted_recv != recv) { + kit.set_argument(0, casted_recv); } } // Cast reference arguments to its type. @@ -1191,12 +1192,13 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod* ciType* t = signature->type_at(i); if (t->is_klass()) { Node* arg = kit.argument(receiver_skip + j); - const TypeOopPtr* arg_type = arg->bottom_type()->isa_oopptr(); - const Type* sig_type = TypeOopPtr::make_from_klass(t->as_klass()); - if (arg_type != nullptr && !arg_type->higher_equal(sig_type)) { - const Type* narrowed_arg_type = arg_type->filter_speculative(sig_type); // keep speculative part - Node* cast_obj = gvn.transform(new CheckCastPPNode(kit.control(), arg, narrowed_arg_type)); - kit.set_argument(receiver_skip + j, cast_obj); + Node* casted_arg = kit.maybe_narrow_object_type(arg, t->as_klass()); + if (casted_arg->is_top()) { + print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(), + "argument types mismatch"); + return nullptr; // FIXME: effectively dead; issue a halt node instead + } else if (casted_arg != arg) { + kit.set_argument(receiver_skip + j, casted_arg); } } j += t->size(); // long and double take two slots diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index fa793454e83e3..f2d259131ba4b 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -361,7 +361,7 @@ static void format_helper( PhaseRegAlloc *regalloc, outputStream* st, Node *n, c if (regalloc->node_regs_max_index() > 0 && OptoReg::is_valid(regalloc->get_reg_first(n))) { // Check for undefined char buf[50]; - regalloc->dump_register(n,buf); + regalloc->dump_register(n,buf,sizeof(buf)); st->print(" %s%d]=%s",msg,i,buf); } else { // No register, but might be constant const Type *t = n->bottom_type(); diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 734eba0000e96..0c66de7f1fc94 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2816,7 +2816,7 @@ void BlackholeNode::format(PhaseRegAlloc* ra, outputStream* st) const { st->print(", "); } char buf[128]; - ra->dump_register(n, buf); + ra->dump_register(n, buf, sizeof(buf)); st->print("%s", buf); } } diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index d0d09ce619004..4a61e953d5cd4 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -2156,42 +2156,42 @@ void PhaseChaitin::dump_simplified() const { tty->cr(); } -static char *print_reg(OptoReg::Name reg, const PhaseChaitin* pc, char* buf) { +static char *print_reg(OptoReg::Name reg, const PhaseChaitin* pc, char* buf, size_t buf_size) { if ((int)reg < 0) - sprintf(buf, "", (int)reg); + os::snprintf_checked(buf, buf_size, "", (int)reg); else if (OptoReg::is_reg(reg)) strcpy(buf, Matcher::regName[reg]); else - sprintf(buf,"%s + #%d",OptoReg::regname(OptoReg::c_frame_pointer), + os::snprintf_checked(buf, buf_size, "%s + #%d",OptoReg::regname(OptoReg::c_frame_pointer), pc->reg2offset(reg)); return buf+strlen(buf); } // Dump a register name into a buffer. Be intelligent if we get called // before allocation is complete. -char *PhaseChaitin::dump_register(const Node* n, char* buf) const { +char *PhaseChaitin::dump_register(const Node* n, char* buf, size_t buf_size) const { if( _node_regs ) { // Post allocation, use direct mappings, no LRG info available - print_reg( get_reg_first(n), this, buf ); + print_reg( get_reg_first(n), this, buf, buf_size); } else { uint lidx = _lrg_map.find_const(n); // Grab LRG number if( !_ifg ) { - sprintf(buf,"L%d",lidx); // No register binding yet + os::snprintf_checked(buf, buf_size, "L%d",lidx); // No register binding yet } else if( !lidx ) { // Special, not allocated value strcpy(buf,"Special"); } else { if (lrgs(lidx)._is_vector) { if (lrgs(lidx).mask().is_bound_set(lrgs(lidx).num_regs())) - print_reg( lrgs(lidx).reg(), this, buf ); // a bound machine register + print_reg( lrgs(lidx).reg(), this, buf, buf_size); // a bound machine register else - sprintf(buf,"L%d",lidx); // No register binding yet + os::snprintf_checked(buf, buf_size, "L%d",lidx); // No register binding yet } else if( (lrgs(lidx).num_regs() == 1) ? lrgs(lidx).mask().is_bound1() : lrgs(lidx).mask().is_bound_pair() ) { // Hah! We have a bound machine register - print_reg( lrgs(lidx).reg(), this, buf ); + print_reg( lrgs(lidx).reg(), this, buf, buf_size); } else { - sprintf(buf,"L%d",lidx); // No register binding yet + os::snprintf_checked(buf, buf_size, "L%d",lidx); // No register binding yet } } } diff --git a/src/hotspot/share/opto/chaitin.hpp b/src/hotspot/share/opto/chaitin.hpp index 612b62186b975..61df334fcc601 100644 --- a/src/hotspot/share/opto/chaitin.hpp +++ b/src/hotspot/share/opto/chaitin.hpp @@ -802,7 +802,7 @@ class PhaseChaitin : public PhaseRegAlloc { public: void dump_frame() const; - char *dump_register(const Node* n, char* buf) const; + char *dump_register(const Node* n, char* buf, size_t buf_size) const; private: static void print_chaitin_statistics(); #endif // not PRODUCT diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 4e8e39ffa7452..c69208089f835 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3113,8 +3113,8 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f bool is_oop = t->isa_oopptr() != nullptr; bool is_klass = t->isa_klassptr() != nullptr; - if ((is_oop && Matcher::const_oop_prefer_decode() ) || - (is_klass && Matcher::const_klass_prefer_decode())) { + if ((is_oop && UseCompressedOops && Matcher::const_oop_prefer_decode() ) || + (is_klass && UseCompressedClassPointers && Matcher::const_klass_prefer_decode())) { Node* nn = nullptr; int op = is_oop ? Op_ConN : Op_ConNKlass; diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index 961e1f4a5f201..9fad77e5fa868 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -260,6 +260,20 @@ const Type* ConvI2LNode::Value(PhaseGVN* phase) const { return tl; } +Node* ConvI2LNode::Identity(PhaseGVN* phase) { + // If type is in "int" sub-range, we can + // convert I2L(L2I(x)) => x + // since the conversions have no effect. + if (in(1)->Opcode() == Op_ConvL2I) { + Node* x = in(1)->in(1); + const TypeLong* t = phase->type(x)->isa_long(); + if (t != nullptr && t->_lo >= min_jint && t->_hi <= max_jint) { + return x; + } + } + return this; +} + static inline bool long_ranges_overlap(jlong lo1, jlong hi1, jlong lo2, jlong hi2) { // Two ranges overlap iff one range's low point falls in the other range. diff --git a/src/hotspot/share/opto/convertnode.hpp b/src/hotspot/share/opto/convertnode.hpp index fb670191fc399..c28c084e4e25e 100644 --- a/src/hotspot/share/opto/convertnode.hpp +++ b/src/hotspot/share/opto/convertnode.hpp @@ -151,6 +151,7 @@ class ConvI2LNode : public TypeNode { virtual int Opcode() const; virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegL; } }; diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index 876e1d9f54f88..5dade637068a8 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -648,6 +648,20 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { // The anti-dependence constraints apply only to the fringe of this tree. Node* initial_mem = load->in(MemNode::Memory); + // We don't optimize the memory graph for pinned loads, so we may need to raise the + // root of our search tree through the corresponding slices of MergeMem nodes to + // get to the node that really creates the memory state for this slice. + if (load_alias_idx >= Compile::AliasIdxRaw) { + while (initial_mem->is_MergeMem()) { + MergeMemNode* mm = initial_mem->as_MergeMem(); + Node* p = mm->memory_at(load_alias_idx); + if (p != mm->base_memory()) { + initial_mem = p; + } else { + break; + } + } + } worklist_store.push(initial_mem); worklist_visited.push(initial_mem); worklist_mem.push(nullptr); diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index c7cfbf0ce9328..ecea558f5e9a8 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -4320,3 +4320,14 @@ Node* GraphKit::make_constant_from_field(ciField* field, Node* obj) { } return nullptr; } + +Node* GraphKit::maybe_narrow_object_type(Node* obj, ciKlass* type) { + const TypeOopPtr* obj_type = obj->bottom_type()->isa_oopptr(); + const TypeOopPtr* sig_type = TypeOopPtr::make_from_klass(type); + if (obj_type != nullptr && sig_type->klass()->is_loaded() && !obj_type->higher_equal(sig_type)) { + const Type* narrow_obj_type = obj_type->filter_speculative(sig_type); // keep speculative part + Node* casted_obj = gvn().transform(new CheckCastPPNode(control(), obj, narrow_obj_type)); + return casted_obj; + } + return obj; +} diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 742e88e0a70e9..4c0b8bd4b74dd 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -445,6 +445,8 @@ class GraphKit : public Phase { // Replace all occurrences of one node by another. void replace_in_map(Node* old, Node* neww); + Node* maybe_narrow_object_type(Node* obj, ciKlass* type); + void push(Node* n) { map_not_null(); _map->set_stack(_map->_jvms, _sp++ , n); } Node* pop() { map_not_null(); return _map->stack( _map->_jvms, --_sp ); } Node* peek(int off = 0) { map_not_null(); return _map->stack( _map->_jvms, _sp - off - 1 ); } diff --git a/src/hotspot/share/opto/idealGraphPrinter.cpp b/src/hotspot/share/opto/idealGraphPrinter.cpp index 757f7a9cee34b..e4ea12f72ba89 100644 --- a/src/hotspot/share/opto/idealGraphPrinter.cpp +++ b/src/hotspot/share/opto/idealGraphPrinter.cpp @@ -141,21 +141,24 @@ void IdealGraphPrinter::init(const char* file_name, bool use_multiple_files, boo _depth = 0; _current_method = nullptr; _network_stream = nullptr; + _append = append; if (file_name != nullptr) { - init_file_stream(file_name, use_multiple_files, append); + init_file_stream(file_name, use_multiple_files); } else { init_network_stream(); } _xml = new (ResourceObj::C_HEAP, mtCompiler) xmlStream(_output); - if (!append) { + if (!_append) { head(TOP_ELEMENT); } } // Destructor, close file or network stream IdealGraphPrinter::~IdealGraphPrinter() { - tail(TOP_ELEMENT); + if (!_append) { + tail(TOP_ELEMENT); + } // tty->print_cr("Walk time: %d", (int)_walk_time.milliseconds()); // tty->print_cr("Output time: %d", (int)_output_time.milliseconds()); @@ -508,7 +511,7 @@ void IdealGraphPrinter::visit_node(Node *n, bool edges, VectorSet* temp_set) { if (index >= 10) { print_prop(short_name, "PA"); } else { - sprintf(buffer, "P%d", index); + os::snprintf_checked(buffer, sizeof(buffer), "P%d", index); print_prop(short_name, buffer); } } else if (strcmp(node->Name(), "IfTrue") == 0) { @@ -524,7 +527,7 @@ void IdealGraphPrinter::visit_node(Node *n, bool edges, VectorSet* temp_set) { // max. 2 chars allowed if (value >= -9 && value <= 99) { - sprintf(buffer, "%d", value); + os::snprintf_checked(buffer, sizeof(buffer), "%d", value); print_prop(short_name, buffer); } else { print_prop(short_name, "I"); @@ -538,7 +541,7 @@ void IdealGraphPrinter::visit_node(Node *n, bool edges, VectorSet* temp_set) { // max. 2 chars allowed if (value >= -9 && value <= 99) { - sprintf(buffer, JLONG_FORMAT, value); + os::snprintf_checked(buffer, sizeof(buffer), JLONG_FORMAT, value); print_prop(short_name, buffer); } else { print_prop(short_name, "L"); @@ -601,7 +604,7 @@ void IdealGraphPrinter::visit_node(Node *n, bool edges, VectorSet* temp_set) { if (_chaitin && _chaitin != (PhaseChaitin *)((intptr_t)0xdeadbeef)) { buffer[0] = 0; - _chaitin->dump_register(node, buffer); + _chaitin->dump_register(node, buffer, sizeof(buffer)); print_prop("reg", buffer); uint lrg_id = 0; if (node->_idx < _chaitin->_lrg_map.size()) { @@ -729,10 +732,10 @@ void IdealGraphPrinter::print(const char *name, Node *node) { _xml->flush(); } -void IdealGraphPrinter::init_file_stream(const char* file_name, bool use_multiple_files, bool append) { +void IdealGraphPrinter::init_file_stream(const char* file_name, bool use_multiple_files) { ThreadCritical tc; if (use_multiple_files && _file_count != 0) { - assert(!append, "append should only be used for debugging with a single file"); + assert(!_append, "append should only be used for debugging with a single file"); ResourceMark rm; stringStream st; const char* dot = strrchr(file_name, '.'); @@ -744,10 +747,10 @@ void IdealGraphPrinter::init_file_stream(const char* file_name, bool use_multipl } _output = new (ResourceObj::C_HEAP, mtCompiler) fileStream(st.as_string(), "w"); } else { - _output = new (ResourceObj::C_HEAP, mtCompiler) fileStream(file_name, append ? "a" : "w"); + _output = new (ResourceObj::C_HEAP, mtCompiler) fileStream(file_name, _append ? "a" : "w"); } if (use_multiple_files) { - assert(!append, "append should only be used for debugging with a single file"); + assert(!_append, "append should only be used for debugging with a single file"); _file_count++; } } @@ -778,9 +781,16 @@ void IdealGraphPrinter::update_compiled_method(ciMethod* current_method) { assert(C != nullptr, "must already be set"); if (current_method != _current_method) { // If a different method, end the old and begin with the new one. - end_method(); - _current_method = nullptr; - begin_method(); + if (_append) { + // Do not call `end_method` if we are appending, just update `_current_method`, + // because `begin_method` is not called in the constructor in append mode. + _current_method = current_method; + } else { + // End the old method and begin a new one. + // Don't worry about `_current_method`, `end_method` will clear it. + end_method(); + begin_method(); + } } } diff --git a/src/hotspot/share/opto/idealGraphPrinter.hpp b/src/hotspot/share/opto/idealGraphPrinter.hpp index 78fdf01d62c62..e7e827974bb72 100644 --- a/src/hotspot/share/opto/idealGraphPrinter.hpp +++ b/src/hotspot/share/opto/idealGraphPrinter.hpp @@ -93,6 +93,7 @@ class IdealGraphPrinter : public CHeapObj { bool _traverse_outs; Compile *C; double _max_freq; + bool _append; void print_method(ciMethod *method, int bci, InlineTree *tree); void print_inline_tree(InlineTree *tree); @@ -110,7 +111,7 @@ class IdealGraphPrinter : public CHeapObj { void head(const char *name); void text(const char *s); void init(const char* file_name, bool use_multiple_files, bool append); - void init_file_stream(const char* file_name, bool use_multiple_files, bool append); + void init_file_stream(const char* file_name, bool use_multiple_files); void init_network_stream(); IdealGraphPrinter(); ~IdealGraphPrinter(); diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index c0181a5e813e6..71b7b7575c8ca 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -732,6 +732,7 @@ bool IfNode::cmpi_folds(PhaseIterGVN* igvn, bool fold_ne) { bool IfNode::is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn) { return ctrl != nullptr && ctrl->is_Proj() && + ctrl->outcnt() == 1 && // No side-effects ctrl->in(0) != nullptr && ctrl->in(0)->Opcode() == Op_If && ctrl->in(0)->outcnt() == 2 && @@ -1307,7 +1308,7 @@ Node* IfNode::fold_compares(PhaseIterGVN* igvn) { if (cmpi_folds(igvn)) { Node* ctrl = in(0); - if (is_ctrl_folds(ctrl, igvn) && ctrl->outcnt() == 1) { + if (is_ctrl_folds(ctrl, igvn)) { // A integer comparison immediately dominated by another integer // comparison ProjNode* success = nullptr; diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index ae2ed68c8f354..0ff5fbfa504f2 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -2162,7 +2162,7 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj new_limit = _igvn.intcon(limit->get_int() - stride_con); set_ctrl(new_limit, C->root()); } else { - // Limit is not constant. + // Limit is not constant. Int subtraction could lead to underflow. if (loop_head->unrolled_count() == 1) { // only for first unroll // Separate limit by Opaque node in case it is an incremented // variable from previous loop to avoid using pre-incremented @@ -2174,68 +2174,38 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj Node* opaq_ctrl = get_ctrl(opaq); limit = new Opaque2Node(C, limit); register_new_node(limit, opaq_ctrl); + + // The Opaque2 node created above (in the case of the first unrolling) hides the type of the loop limit. + // Propagate this precise type information. + limit = new CastIINode(limit, limit_type); + register_new_node(limit, opaq_ctrl); } - if ((stride_con > 0 && (java_subtract(limit_type->_lo, stride_con) < limit_type->_lo)) || - (stride_con < 0 && (java_subtract(limit_type->_hi, stride_con) > limit_type->_hi))) { - // No underflow. - new_limit = new SubINode(limit, stride); + // (1) Convert to long. + Node* limit_l = new ConvI2LNode(limit); + register_new_node(limit_l, get_ctrl(limit)); + Node* stride_l = _igvn.longcon(stride_con); + set_ctrl(stride_l, C->root()); + + // (2) Subtract: compute in long, to prevent underflow. + Node* new_limit_l = new SubLNode(limit_l, stride_l); + register_new_node(new_limit_l, ctrl); + + // (3) Clamp to int range, in case we had subtraction underflow. + Node* underflow_clamp_l = _igvn.longcon((stride_con > 0) ? min_jint : max_jint); + set_ctrl(underflow_clamp_l, C->root()); + Node* new_limit_no_underflow_l = nullptr; + if (stride_con > 0) { + // limit = MaxL(limit - stride, min_jint) + new_limit_no_underflow_l = new MaxLNode(C, new_limit_l, underflow_clamp_l); } else { - // (limit - stride) may underflow. - // Clamp the adjustment value with MININT or MAXINT: - // - // new_limit = limit-stride - // if (stride > 0) - // new_limit = (limit < new_limit) ? MININT : new_limit; - // else - // new_limit = (limit > new_limit) ? MAXINT : new_limit; - // - BoolTest::mask bt = loop_end->test_trip(); - assert(bt == BoolTest::lt || bt == BoolTest::gt, "canonical test is expected"); - Node* adj_max = _igvn.intcon((stride_con > 0) ? min_jint : max_jint); - set_ctrl(adj_max, C->root()); - Node* old_limit = nullptr; - Node* adj_limit = nullptr; - Node* bol = limit->is_CMove() ? limit->in(CMoveNode::Condition) : nullptr; - if (loop_head->unrolled_count() > 1 && - limit->is_CMove() && limit->Opcode() == Op_CMoveI && - limit->in(CMoveNode::IfTrue) == adj_max && - bol->as_Bool()->_test._test == bt && - bol->in(1)->Opcode() == Op_CmpI && - bol->in(1)->in(2) == limit->in(CMoveNode::IfFalse)) { - // Loop was unrolled before. - // Optimize the limit to avoid nested CMove: - // use original limit as old limit. - old_limit = bol->in(1)->in(1); - // Adjust previous adjusted limit. - adj_limit = limit->in(CMoveNode::IfFalse); - adj_limit = new SubINode(adj_limit, stride); - } else { - old_limit = limit; - adj_limit = new SubINode(limit, stride); - } - assert(old_limit != nullptr && adj_limit != nullptr, ""); - register_new_node(adj_limit, ctrl); // adjust amount - Node* adj_cmp = new CmpINode(old_limit, adj_limit); - register_new_node(adj_cmp, ctrl); - Node* adj_bool = new BoolNode(adj_cmp, bt); - register_new_node(adj_bool, ctrl); - new_limit = new CMoveINode(adj_bool, adj_limit, adj_max, TypeInt::INT); + // limit = MinL(limit - stride, max_jint) + new_limit_no_underflow_l = new MinLNode(C, new_limit_l, underflow_clamp_l); } + register_new_node(new_limit_no_underflow_l, ctrl); + + // (4) Convert back to int. + new_limit = new ConvL2INode(new_limit_no_underflow_l); register_new_node(new_limit, ctrl); - if (loop_head->unrolled_count() == 1) { - // The Opaque2 node created above (in the case of the first unrolling) hides the type of the loop limit. - // As a result, if the iv Phi constant folds (because it captured the iteration range), the exit test won't - // constant fold and the graph contains a broken counted loop. - const Type* new_limit_t; - if (stride_con > 0) { - new_limit_t = TypeInt::make(min_jint, limit_type->_hi, limit_type->_widen); - } else { - assert(stride_con < 0, "stride can't be 0"); - new_limit_t = TypeInt::make(limit_type->_lo, max_jint, limit_type->_widen); - } - new_limit = new CastIINode(new_limit, new_limit_t); - register_new_node(new_limit, ctrl); - } } assert(new_limit != nullptr, ""); @@ -2443,6 +2413,9 @@ void PhaseIdealLoop::mark_reductions(IdealLoopTree *loop) { //------------------------------adjust_limit----------------------------------- // Helper function that computes new loop limit as (rc_limit-offset)/scale Node* PhaseIdealLoop::adjust_limit(bool is_positive_stride, Node* scale, Node* offset, Node* rc_limit, Node* old_limit, Node* pre_ctrl, bool round) { + Node* old_limit_long = new ConvI2LNode(old_limit); + register_new_node(old_limit_long, pre_ctrl); + Node* sub = new SubLNode(rc_limit, offset); register_new_node(sub, pre_ctrl); Node* limit = new DivLNode(nullptr, sub, scale); @@ -2468,27 +2441,19 @@ Node* PhaseIdealLoop::adjust_limit(bool is_positive_stride, Node* scale, Node* o // - integer underflow of limit: MAXL chooses old_limit (>= MIN_INT > limit) // INT() is finally converting the limit back to an integer value. - // We use CMove nodes to implement long versions of min/max (MINL/MAXL). - // We use helper methods for inner MINL/MAXL which return CMoveL nodes to keep a long value for the outer MINL/MAXL comparison: - Node* inner_result_long; + Node* inner_result_long = nullptr; + Node* outer_result_long = nullptr; if (is_positive_stride) { - inner_result_long = MaxNode::signed_max(limit, _igvn.longcon(min_jint), TypeLong::LONG, _igvn); + inner_result_long = new MaxLNode(C, limit, _igvn.longcon(min_jint)); + outer_result_long = new MinLNode(C, inner_result_long, old_limit_long); } else { - inner_result_long = MaxNode::signed_min(limit, _igvn.longcon(max_jint), TypeLong::LONG, _igvn); + inner_result_long = new MinLNode(C, limit, _igvn.longcon(max_jint)); + outer_result_long = new MaxLNode(C, inner_result_long, old_limit_long); } - set_subtree_ctrl(inner_result_long, false); + register_new_node(inner_result_long, pre_ctrl); + register_new_node(outer_result_long, pre_ctrl); - // Outer MINL/MAXL: - // The comparison is done with long values but the result is the converted back to int by using CmovI. - Node* old_limit_long = new ConvI2LNode(old_limit); - register_new_node(old_limit_long, pre_ctrl); - Node* cmp = new CmpLNode(old_limit_long, limit); - register_new_node(cmp, pre_ctrl); - Node* bol = new BoolNode(cmp, is_positive_stride ? BoolTest::gt : BoolTest::lt); - register_new_node(bol, pre_ctrl); - Node* inner_result_int = new ConvL2INode(inner_result_long); // Could under-/overflow but that's fine as comparison was done with CmpL - register_new_node(inner_result_int, pre_ctrl); - limit = new CMoveINode(bol, old_limit, inner_result_int, TypeInt::INT); + limit = new ConvL2INode(outer_result_long); register_new_node(limit, pre_ctrl); return limit; } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index ce6ecc4d0129c..083179d491f39 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -1478,12 +1478,28 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_ // Since stride > 0 and limit_correction <= stride + 1, we can restate this with no over- or underflow into: // max_int - canonicalized_correction - limit_correction >= limit // Since canonicalized_correction and limit_correction are both constants, we can replace them with a new constant: - // final_correction = canonicalized_correction + limit_correction + // (v) final_correction = canonicalized_correction + limit_correction + // // which gives us: // // Final predicate condition: // max_int - final_correction >= limit // + // However, we need to be careful that (v) does not over- or underflow. + // We know that: + // canonicalized_correction = stride - 1 + // and + // limit_correction <= stride + 1 + // and thus + // canonicalized_correction + limit_correction <= 2 * stride + // To prevent an over- or underflow of (v), we must ensure that + // 2 * stride <= max_int + // which can safely be checked without over- or underflow with + // (vi) stride != min_int AND abs(stride) <= max_int / 2 + // + // We could try to further optimize the cases where (vi) does not hold but given that such large strides are + // very uncommon and the loop would only run for a very few iterations anyway, we simply bail out if (vi) fails. + // // (2) Loop Limit Check Predicate for (ii): // Using (ii): init < limit // @@ -1514,6 +1530,10 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_ // there is no overflow of the iv phi after the first iteration. In this case, we don't need to check (ii) // again and can skip the predicate. + // Check (vi) and bail out if the stride is too big. + if (stride_con == min_signed_integer(iv_bt) || (ABS(stride_con) > max_signed_integer(iv_bt) / 2)) { + return false; + } // Accounting for (LE3) and (LE4) where we use pre-incremented phis in the loop exit check. const jlong limit_correction_for_pre_iv_exit_check = (phi_incr != nullptr) ? stride_con : 0; @@ -2128,7 +2148,7 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) { const TypeInt* init_t = phase->type(in(Init) )->is_int(); const TypeInt* limit_t = phase->type(in(Limit))->is_int(); - int stride_p; + jlong stride_p; jlong lim, ini; julong max; if (stride_con > 0) { @@ -2137,10 +2157,10 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) { ini = init_t->_lo; max = (julong)max_jint; } else { - stride_p = -stride_con; + stride_p = -(jlong)stride_con; lim = init_t->_hi; ini = limit_t->_lo; - max = (julong)min_jint; + max = (julong)(juint)min_jint; // double cast to get 0x0000000080000000, not 0xffffffff80000000 } julong range = lim - ini + stride_p; if (range <= max) { @@ -3910,23 +3930,6 @@ bool PhaseIdealLoop::process_expensive_nodes() { return progress; } -#ifdef ASSERT -// Goes over all children of the root of the loop tree. Check if any of them have a path -// down to Root, that does not go via a NeverBranch exit. -bool PhaseIdealLoop::only_has_infinite_loops() { - ResourceMark rm; - Unique_Node_List worklist; - // start traversal at all loop heads of first-level loops - for (IdealLoopTree* l = _ltree_root->_child; l != nullptr; l = l->_next) { - Node* head = l->_head; - assert(head->is_Region(), ""); - worklist.push(head); - } - return RegionNode::are_all_nodes_in_infinite_subgraph(worklist); -} -#endif - - //============================================================================= //----------------------------build_and_optimize------------------------------- // Create a PhaseLoop. Build the ideal Loop tree. Map each Ideal Node to @@ -3985,13 +3988,9 @@ void PhaseIdealLoop::build_and_optimize() { return; } - // Verify that the has_loops() flag set at parse time is consistent - // with the just built loop tree. With infinite loops, it could be - // that one pass of loop opts only finds infinite loops, clears the - // has_loops() flag but adds NeverBranch nodes so the next loop opts - // verification pass finds a non empty loop tree. When the back edge + // Verify that the has_loops() flag set at parse time is consistent with the just built loop tree. When the back edge // is an exception edge, parsing doesn't set has_loops(). - assert(_ltree_root->_child == nullptr || C->has_loops() || only_has_infinite_loops() || C->has_exception_backedge(), "parsing found no loops but there are some"); + assert(_ltree_root->_child == nullptr || C->has_loops() || C->has_exception_backedge(), "parsing found no loops but there are some"); // No loops after all if( !_ltree_root->_child && !_verify_only ) C->set_has_loops(false); @@ -4739,7 +4738,7 @@ void PhaseIdealLoop::build_loop_tree() { if ( bltstack.length() == stack_size ) { // There were no additional children, post visit node now (void)bltstack.pop(); // Remove node from stack - pre_order = build_loop_tree_impl( n, pre_order ); + pre_order = build_loop_tree_impl(n, pre_order); // Check for bailout if (C->failing()) { return; @@ -4756,7 +4755,7 @@ void PhaseIdealLoop::build_loop_tree() { } //------------------------------build_loop_tree_impl--------------------------- -int PhaseIdealLoop::build_loop_tree_impl( Node *n, int pre_order ) { +int PhaseIdealLoop::build_loop_tree_impl(Node* n, int pre_order) { // ---- Post-pass Work ---- // Pre-walked but not post-walked nodes need a pre_order number. @@ -4767,56 +4766,51 @@ int PhaseIdealLoop::build_loop_tree_impl( Node *n, int pre_order ) { // for it. Then find the tightest enclosing loop for the self Node. for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node* m = n->fast_out(i); // Child - if( n == m ) continue; // Ignore control self-cycles - if( !m->is_CFG() ) continue;// Ignore non-CFG edges + if (n == m) continue; // Ignore control self-cycles + if (!m->is_CFG()) continue;// Ignore non-CFG edges IdealLoopTree *l; // Child's loop - if( !is_postvisited(m) ) { // Child visited but not post-visited? + if (!is_postvisited(m)) { // Child visited but not post-visited? // Found a backedge - assert( get_preorder(m) < pre_order, "should be backedge" ); + assert(get_preorder(m) < pre_order, "should be backedge"); // Check for the RootNode, which is already a LoopNode and is allowed // to have multiple "backedges". - if( m == C->root()) { // Found the root? + if (m == C->root()) { // Found the root? l = _ltree_root; // Root is the outermost LoopNode } else { // Else found a nested loop // Insert a LoopNode to mark this loop. l = new IdealLoopTree(this, m, n); } // End of Else found a nested loop - if( !has_loop(m) ) // If 'm' does not already have a loop set + if (!has_loop(m)) { // If 'm' does not already have a loop set set_loop(m, l); // Set loop header to loop now - + } } else { // Else not a nested loop if( !_nodes[m->_idx] ) continue; // Dead code has no loop - l = get_loop(m); // Get previously determined loop + IdealLoopTree* m_loop = get_loop(m); + l = m_loop; // Get previously determined loop // If successor is header of a loop (nest), move up-loop till it // is a member of some outer enclosing loop. Since there are no // shared headers (I've split them already) I only need to go up // at most 1 level. - while( l && l->_head == m ) // Successor heads loop? + while (l && l->_head == m) { // Successor heads loop? l = l->_parent; // Move up 1 for me + } // If this loop is not properly parented, then this loop // has no exit path out, i.e. its an infinite loop. - if( !l ) { + if (!l) { // Make loop "reachable" from root so the CFG is reachable. Basically // insert a bogus loop exit that is never taken. 'm', the loop head, // points to 'n', one (of possibly many) fall-in paths. There may be // many backedges as well. - // Here I set the loop to be the root loop. I could have, after - // inserting a bogus loop exit, restarted the recursion and found my - // new loop exit. This would make the infinite loop a first-class - // loop and it would then get properly optimized. What's the use of - // optimizing an infinite loop? - l = _ltree_root; // Oops, found infinite loop - if (!_verify_only) { // Insert the NeverBranch between 'm' and it's control user. NeverBranchNode *iff = new NeverBranchNode( m ); _igvn.register_new_node_with_optimizer(iff); - set_loop(iff, l); + set_loop(iff, m_loop); Node *if_t = new CProjNode( iff, 0 ); _igvn.register_new_node_with_optimizer(if_t); - set_loop(if_t, l); + set_loop(if_t, m_loop); Node* cfg = nullptr; // Find the One True Control User of m for (DUIterator_Fast jmax, j = m->fast_outs(jmax); j < jmax; j++) { @@ -4833,7 +4827,7 @@ int PhaseIdealLoop::build_loop_tree_impl( Node *n, int pre_order ) { // Now create the never-taken loop exit Node *if_f = new CProjNode( iff, 1 ); _igvn.register_new_node_with_optimizer(if_f); - set_loop(if_f, l); + set_loop(if_f, _ltree_root); // Find frame ptr for Halt. Relies on the optimizer // V-N'ing. Easier and quicker than searching through // the program structure. @@ -4842,10 +4836,27 @@ int PhaseIdealLoop::build_loop_tree_impl( Node *n, int pre_order ) { // Halt & Catch Fire Node* halt = new HaltNode(if_f, frame, "never-taken loop exit reached"); _igvn.register_new_node_with_optimizer(halt); - set_loop(halt, l); + set_loop(halt, _ltree_root); C->root()->add_req(halt); } set_loop(C->root(), _ltree_root); + // move to outer most loop with same header + l = m_loop; + while (true) { + IdealLoopTree* next = l->_parent; + if (next == nullptr || next->_head != m) { + break; + } + l = next; + } + // properly insert infinite loop in loop tree + sort(_ltree_root, l); + // fix child link from parent + IdealLoopTree* p = l->_parent; + l->_next = p->_child; + p->_child = l; + // code below needs enclosing loop + l = l->_parent; } } // Weeny check for irreducible. This child was already visited (this @@ -4885,7 +4896,7 @@ int PhaseIdealLoop::build_loop_tree_impl( Node *n, int pre_order ) { assert( get_loop(n) == innermost, "" ); IdealLoopTree *p = innermost->_parent; IdealLoopTree *l = innermost; - while( p && l->_head == n ) { + while (p && l->_head == n) { l->_next = p->_child; // Put self on parents 'next child' p->_child = l; // Make self as first child of parent l = p; // Now walk up the parent chain @@ -4899,7 +4910,7 @@ int PhaseIdealLoop::build_loop_tree_impl( Node *n, int pre_order ) { // Record tightest enclosing loop for self. Mark as post-visited. set_loop(n, innermost); // Also record has_call flag early on - if( innermost ) { + if (innermost) { if( n->is_Call() && !n->is_CallLeaf() && !n->is_macro() ) { // Do not count uncommon calls if( !n->is_CallStaticJava() || !n->as_CallStaticJava()->_name ) { diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 8bcdd96c9f4af..7ae58ac09f534 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -927,10 +927,6 @@ class PhaseIdealLoop : public PhaseTransform { void update_main_loop_skeleton_predicates(Node* ctrl, CountedLoopNode* loop_head, Node* init, int stride_con); void copy_skeleton_predicates_to_post_loop(LoopNode* main_loop_head, CountedLoopNode* post_loop_head, Node* init, Node* stride); void insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol); -#ifdef ASSERT - bool only_has_infinite_loops(); -#endif - void log_loop_tree(); public: @@ -1036,7 +1032,7 @@ class PhaseIdealLoop : public PhaseTransform { // Place 'n' in some loop nest, where 'n' is a CFG node void build_loop_tree(); - int build_loop_tree_impl( Node *n, int pre_order ); + int build_loop_tree_impl(Node* n, int pre_order); // Insert loop into the existing loop tree. 'innermost' is a leaf of the // loop tree, not the root. IdealLoopTree *sort( IdealLoopTree *loop, IdealLoopTree *innermost ); diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 293d630ded4dc..69596ac80cf5f 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2568,6 +2568,8 @@ void PhaseMacroExpand::eliminate_macro_nodes() { n->Opcode() == Op_Opaque2 || n->Opcode() == Op_Opaque3 || n->Opcode() == Op_Opaque4 || + n->Opcode() == Op_MaxL || + n->Opcode() == Op_MinL || BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(n), "unknown node type in macro list"); } @@ -2646,6 +2648,18 @@ bool PhaseMacroExpand::expand_macro_nodes() { n->as_OuterStripMinedLoop()->adjust_strip_mined_loop(&_igvn); C->remove_macro_node(n); success = true; + } else if (n->Opcode() == Op_MaxL) { + // Since MaxL and MinL are not implemented in the backend, we expand them to + // a CMoveL construct now. At least until here, the type could be computed + // precisely. CMoveL is not so smart, but we can give it at least the best + // type we know abouot n now. + Node* repl = MaxNode::signed_max(n->in(1), n->in(2), _igvn.type(n), _igvn); + _igvn.replace_node(n, repl); + success = true; + } else if (n->Opcode() == Op_MinL) { + Node* repl = MaxNode::signed_min(n->in(1), n->in(2), _igvn.type(n), _igvn); + _igvn.replace_node(n, repl); + success = true; } assert(!success || (C->macro_count() == (old_macro_count - 1)), "elimination must have deleted one node from macro list"); progress = progress || success; diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 890e8db077d74..ed5075a655d2d 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1515,8 +1515,8 @@ Node* Node::last_out(DUIterator_Last& i) const { class SimpleDUIterator : public StackObj { private: Node* node; - DUIterator_Fast i; DUIterator_Fast imax; + DUIterator_Fast i; public: SimpleDUIterator(Node* n): node(n), i(n->fast_outs(imax)) {} bool has_next() { return i < imax; } diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 8a1ed0d316089..43c46a0eb8fe4 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -176,6 +176,11 @@ class Scheduling { // Add a node to the current bundle void AddNodeToBundle(Node *n, const Block *bb); + // Return an integer less than, equal to, or greater than zero + // if the stack offset of the first argument is respectively + // less than, equal to, or greater than the second. + int compare_two_spill_nodes(Node* first, Node* second); + // Add a node to the list of available nodes void AddNodeToAvailableList(Node *n); @@ -2306,6 +2311,29 @@ Node * Scheduling::ChooseNodeToBundle() { return _available[0]; } +int Scheduling::compare_two_spill_nodes(Node* first, Node* second) { + assert(first->is_MachSpillCopy() && second->is_MachSpillCopy(), ""); + + OptoReg::Name first_src_lo = _regalloc->get_reg_first(first->in(1)); + OptoReg::Name first_dst_lo = _regalloc->get_reg_first(first); + OptoReg::Name second_src_lo = _regalloc->get_reg_first(second->in(1)); + OptoReg::Name second_dst_lo = _regalloc->get_reg_first(second); + + // Comparison between stack -> reg and stack -> reg + if (OptoReg::is_stack(first_src_lo) && OptoReg::is_stack(second_src_lo) && + OptoReg::is_reg(first_dst_lo) && OptoReg::is_reg(second_dst_lo)) { + return _regalloc->reg2offset(first_src_lo) - _regalloc->reg2offset(second_src_lo); + } + + // Comparison between reg -> stack and reg -> stack + if (OptoReg::is_stack(first_dst_lo) && OptoReg::is_stack(second_dst_lo) && + OptoReg::is_reg(first_src_lo) && OptoReg::is_reg(second_src_lo)) { + return _regalloc->reg2offset(first_dst_lo) - _regalloc->reg2offset(second_dst_lo); + } + + return 0; // Not comparable +} + void Scheduling::AddNodeToAvailableList(Node *n) { assert( !n->is_Proj(), "projections never directly made available" ); #ifndef PRODUCT @@ -2317,11 +2345,20 @@ void Scheduling::AddNodeToAvailableList(Node *n) { int latency = _current_latency[n->_idx]; - // Insert in latency order (insertion sort) + // Insert in latency order (insertion sort). If two MachSpillCopyNodes + // for stack spilling or unspilling have the same latency, we sort + // them in the order of stack offset. Some ports (e.g. aarch64) may also + // have more opportunities to do ld/st merging uint i; - for ( i=0; i < _available.size(); i++ ) - if (_current_latency[_available[i]->_idx] > latency) + for (i = 0; i < _available.size(); i++) { + if (_current_latency[_available[i]->_idx] > latency) { break; + } else if (_current_latency[_available[i]->_idx] == latency && + n->is_MachSpillCopy() && _available[i]->is_MachSpillCopy() && + compare_two_spill_nodes(n, _available[i]) > 0) { + break; + } + } // Special Check for compares following branches if( n->is_Mach() && _scheduled.size() > 0 ) { diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 8ce2bdeb71a30..1f95139b5a089 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1621,7 +1621,7 @@ void Parse::merge_new_path(int target_bci) { // The ex_oop must be pushed on the stack, unlike throw_to_exit. void Parse::merge_exception(int target_bci) { #ifdef ASSERT - if (target_bci < bci()) { + if (target_bci <= bci()) { C->set_exception_backedge(); } #endif diff --git a/src/hotspot/share/opto/postaloc.cpp b/src/hotspot/share/opto/postaloc.cpp index 52d6f01973761..b124a81128b69 100644 --- a/src/hotspot/share/opto/postaloc.cpp +++ b/src/hotspot/share/opto/postaloc.cpp @@ -403,7 +403,6 @@ bool PhaseChaitin::eliminate_copy_of_constant(Node* val, Node* n, // as they get encountered with the merge node and keep adding these defs to the merge inputs. void PhaseChaitin::merge_multidefs() { Compile::TracePhase tp("mergeMultidefs", &timers[_t_mergeMultidefs]); - ResourceMark rm; // Keep track of the defs seen in registers and collect their uses in the block. RegToDefUseMap reg2defuse(_max_reg, _max_reg, RegDefUse()); for (uint i = 0; i < _cfg.number_of_blocks(); i++) { diff --git a/src/hotspot/share/opto/regalloc.hpp b/src/hotspot/share/opto/regalloc.hpp index d7bbae1987102..ecdf2e2bee81f 100644 --- a/src/hotspot/share/opto/regalloc.hpp +++ b/src/hotspot/share/opto/regalloc.hpp @@ -127,7 +127,7 @@ class PhaseRegAlloc : public Phase { static int _max_framesize; virtual void dump_frame() const = 0; - virtual char *dump_register( const Node *n, char *buf ) const = 0; + virtual char *dump_register( const Node *n, char *buf, size_t buf_size) const = 0; static void print_statistics(); #endif }; diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index 98e0f5f2ed377..478c6a315bfa9 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -1483,44 +1483,86 @@ Node *BoolNode::Ideal(PhaseGVN *phase, bool can_reshape) { return new BoolNode( ncmp, _test.negate() ); } - // Change ((x & m) u<= m) or ((m & x) u<= m) to always true - // Same with ((x & m) u< m+1) and ((m & x) u< m+1) + // We use the following Lemmas/insights for the following two transformations (1) and (2): + // x & y <=u y, for any x and y (Lemma 1, masking always results in a smaller unsigned number) + // y Opcode() == Op_AddI && cmp2->in(2)->find_int_con(0) == 1) { - bound = cmp2->in(1); + // (1b) "(x & m) in(1); + const TypeInt* rhs_m_type = phase->type(rhs_m)->isa_int(); + if (rhs_m_type->_lo > -1 || rhs_m_type->_hi < -1) { + // Exclude any case where m == -1 is possible. + m = rhs_m; + } } - if (cmp1->in(2) == bound || cmp1->in(1) == bound) { + if (cmp1->in(2) == m || cmp1->in(1) == m) { return ConINode::make(1); } } - // Change ((x & (m - 1)) u< m) into (m > 0) - // This is the off-by-one variant of the above + // (2) Change ((x & (m - 1)) u 0) + // This is the off-by-one variant of the above. + // + // We now prove that this replacement is correct. This is the same as proving + // "m >u 0" if and only if "x & (m - 1) u 0 <=> x & (m - 1) m >u 0": + // We prove this by contradiction: + // Assume m <=u 0 which is equivalent to m == 0: + // and thus + // x & (m - 1) u 0 => x & (m - 1) u 0, no underflow of "m - 1" + // + // + // Note that the signed version of "m > 0": + // m > 0 <=> x & (m - 1) 0 + // is false which is a contradiction. if (cop == Op_CmpU && _test._test == BoolTest::lt && cmp1_op == Op_AndI) { - Node* l = cmp1->in(1); - Node* r = cmp1->in(2); - for (int repeat = 0; repeat < 2; repeat++) { - bool match = r->Opcode() == Op_AddI && r->in(2)->find_int_con(0) == -1 && - r->in(1) == cmp2; - if (match) { - // arraylength known to be non-negative, so a (arraylength != 0) is sufficient, - // but to be compatible with the array range check pattern, use (arraylength u> 0) - Node* ncmp = cmp2->Opcode() == Op_LoadRange - ? phase->transform(new CmpUNode(cmp2, phase->intcon(0))) - : phase->transform(new CmpINode(cmp2, phase->intcon(0))); - return new BoolNode(ncmp, BoolTest::gt); - } else { - // commute and try again - l = cmp1->in(2); - r = cmp1->in(1); + Node* m = cmp2; // RHS: m + for (int add_idx = 1; add_idx <= 2; add_idx++) { // LHS: "(m + (-1)) & x" or "x & (m + (-1))"? + Node* maybe_m_minus_1 = cmp1->in(add_idx); + if (maybe_m_minus_1->Opcode() == Op_AddI && + maybe_m_minus_1->in(2)->find_int_con(0) == -1 && + maybe_m_minus_1->in(1) == m) { + Node* m_cmpu_0 = phase->transform(new CmpUNode(m, phase->intcon(0))); + return new BoolNode(m_cmpu_0, BoolTest::gt); } } } diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 07b07571d6ec2..779e5b3cbe729 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -3590,20 +3590,39 @@ void SuperWord::align_initial_loop_index(MemNode* align_to_ref) { _igvn.register_new_node_with_optimizer(N); _phase->set_ctrl(N, pre_ctrl); + // The computation of the new pre-loop limit could overflow or underflow the int range. This is problematic in + // combination with Range Check Elimination (RCE), which determines a "safe" range where a RangeCheck will always + // succeed. RCE adjusts the pre-loop limit such that we only enter the main-loop once we have reached the "safe" + // range, and adjusts the main-loop limit so that we exit the main-loop before we leave the "safe" range. After RCE, + // the range of the main-loop can only be safely narrowed, and should never be widened. Hence, the pre-loop limit + // can only be increased (for stride > 0), but an add overflow might decrease it, or decreased (for stride < 0), but + // a sub underflow might increase it. To prevent that, we perform the Sub / Add and Max / Min with long operations. + lim0 = new ConvI2LNode(lim0); + N = new ConvI2LNode(N); + orig_limit = new ConvI2LNode(orig_limit); + _igvn.register_new_node_with_optimizer(lim0); + _igvn.register_new_node_with_optimizer(N); + _igvn.register_new_node_with_optimizer(orig_limit); + // substitute back into (1), so that new limit // lim = lim0 + N Node* lim; if (stride < 0) { - lim = new SubINode(lim0, N); + lim = new SubLNode(lim0, N); } else { - lim = new AddINode(lim0, N); + lim = new AddLNode(lim0, N); } _igvn.register_new_node_with_optimizer(lim); _phase->set_ctrl(lim, pre_ctrl); Node* constrained = - (stride > 0) ? (Node*) new MinINode(lim, orig_limit) - : (Node*) new MaxINode(lim, orig_limit); + (stride > 0) ? (Node*) new MinLNode(_phase->C, lim, orig_limit) + : (Node*) new MaxLNode(_phase->C, lim, orig_limit); + _igvn.register_new_node_with_optimizer(constrained); + + // We know that the result is in the int range, there is never truncation + constrained = new ConvL2INode(constrained); _igvn.register_new_node_with_optimizer(constrained); + _phase->set_ctrl(constrained, pre_ctrl); _igvn.replace_input_of(pre_opaq, 1, constrained); } @@ -3723,6 +3742,10 @@ SWPointer::SWPointer(MemNode* mem, SuperWord* slp, Node_Stack *nstack, bool anal _mem(mem), _slp(slp), _base(nullptr), _adr(nullptr), _scale(0), _offset(0), _invar(nullptr), _negate_invar(false), _invar_scale(nullptr), + _has_int_index_after_convI2L(false), + _int_index_after_convI2L_offset(0), + _int_index_after_convI2L_invar(nullptr), + _int_index_after_convI2L_scale(0), _nstack(nstack), _analyze_only(analyze_only), _stack_idx(0) #ifndef PRODUCT @@ -3781,6 +3804,11 @@ SWPointer::SWPointer(MemNode* mem, SuperWord* slp, Node_Stack *nstack, bool anal NOT_PRODUCT(if(_slp->is_trace_alignment()) _tracer.restore_depth();) NOT_PRODUCT(_tracer.ctor_6(mem);) + if (!is_safe_to_use_as_simple_form(base, adr)) { + assert(!valid(), "does not have simple form"); + return; + } + _base = base; _adr = adr; assert(valid(), "Usable"); @@ -3792,6 +3820,10 @@ SWPointer::SWPointer(SWPointer* p) : _mem(p->_mem), _slp(p->_slp), _base(nullptr), _adr(nullptr), _scale(0), _offset(0), _invar(nullptr), _negate_invar(false), _invar_scale(nullptr), + _has_int_index_after_convI2L(false), + _int_index_after_convI2L_offset(0), + _int_index_after_convI2L_invar(nullptr), + _int_index_after_convI2L_scale(0), _nstack(p->_nstack), _analyze_only(p->_analyze_only), _stack_idx(p->_stack_idx) #ifndef PRODUCT @@ -3799,6 +3831,354 @@ SWPointer::SWPointer(SWPointer* p) : #endif {} +// We would like to make decisions about aliasing (i.e. removing memory edges) and adjacency +// (i.e. which loads/stores can be packed) based on the simple form: +// +// s_pointer = adr + offset + invar + scale * ConvI2L(iv) +// +// However, we parse the compound-long-int form: +// +// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index) +// int_index = int_offset + int_invar + int_scale * iv +// +// In general, the simple and the compound-long-int form do not always compute the same pointer +// at runtime. For example, the simple form would give a different result due to an overflow +// in the int_index. +// +// Example: +// For both forms, we have: +// iv = 0 +// scale = 1 +// +// We now account the offset and invar once to the long part and once to the int part: +// Pointer 1 (long offset and long invar): +// long_offset = min_int +// long_invar = min_int +// int_offset = 0 +// int_invar = 0 +// +// Pointer 2 (int offset and int invar): +// long_offset = 0 +// long_invar = 0 +// int_offset = min_int +// int_invar = min_int +// +// This gives us the following pointers: +// Compound-long-int form pointers: +// Form: +// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + int_invar + int_scale * iv) +// +// Pointers: +// c_pointer1 = adr + min_int + min_int + 1 * ConvI2L(0 + 0 + 1 * 0) +// = adr + min_int + min_int +// = adr - 2^32 +// +// c_pointer2 = adr + 0 + 0 + 1 * ConvI2L(min_int + min_int + 1 * 0) +// = adr + ConvI2L(min_int + min_int) +// = adr + 0 +// = adr +// +// Simple form pointers: +// Form: +// s_pointer = adr + offset + invar + scale * ConvI2L(iv) +// s_pointer = adr + (long_offset + int_offset) + (long_invar + int_invar) + (long_scale * int_scale) * ConvI2L(iv) +// +// Pointers: +// s_pointer1 = adr + (min_int + 0 ) + (min_int + 0 ) + 1 * 0 +// = adr + min_int + min_int +// = adr - 2^32 +// s_pointer2 = adr + (0 + min_int ) + (0 + min_int ) + 1 * 0 +// = adr + min_int + min_int +// = adr - 2^32 +// +// We see that the two addresses are actually 2^32 bytes apart (derived from the c_pointers), but their simple form look identical. +// +// Hence, we need to determine in which cases it is safe to make decisions based on the simple +// form, rather than the compound-long-int form. If we cannot prove that using the simple form +// is safe (i.e. equivalent to the compound-long-int form), then we do not get a valid SWPointer, +// and the associated memop cannot be vectorized. +bool SWPointer::is_safe_to_use_as_simple_form(Node* base, Node* adr) const { +#ifndef _LP64 + // On 32-bit platforms, there is never an explicit int_index with ConvI2L for the iv. Thus, the + // parsed pointer form is always the simple form, with int operations: + // + // pointer = adr + offset + invar + scale * iv + // + assert(!_has_int_index_after_convI2L, "32-bit never has an int_index with ConvI2L for the iv"); + return true; +#else + + // Array accesses that are not Unsafe always have a RangeCheck which ensures that there is no + // int_index overflow. This implies that the conversion to long can be done separately: + // + // ConvI2L(int_index) = ConvI2L(int_offset) + ConvI2L(int_invar) + ConvI2L(scale) * ConvI2L(iv) + // + // And hence, the simple form is guaranteed to be identical to the compound-long-int form at + // runtime and the SWPointer is safe/valid to be used. + const TypeAryPtr* ary_ptr_t = _mem->adr_type()->isa_aryptr(); + if (ary_ptr_t != nullptr) { + if (!_mem->is_unsafe_access()) { + return true; + } + } + + // We did not find the int_index. Just to be safe, reject this SWPointer. + if (!_has_int_index_after_convI2L) { + return false; + } + + int int_offset = _int_index_after_convI2L_offset; + Node* int_invar = _int_index_after_convI2L_invar; + int int_scale = _int_index_after_convI2L_scale; + int long_scale = _scale / int_scale; + + // If "int_index = iv", then the simple form is identical to the compound-long-int form. + // + // int_index = int_offset + int_invar + int_scale * iv + // = 0 0 1 * iv + // = iv + if (int_offset == 0 && int_invar == nullptr && int_scale == 1) { + return true; + } + + // Intuition: What happens if the int_index overflows? Let us look at two pointers on the "overflow edge": + // + // pointer1 = adr + ConvI2L(int_index1) + // pointer2 = adr + ConvI2L(int_index2) + // + // int_index1 = max_int + 0 = max_int -> very close to but before the overflow + // int_index2 = max_int + 1 = min_int -> just enough to get the overflow + // + // When looking at the difference of pointer1 and pointer2, we notice that it is very large + // (almost 2^32). Since arrays have at most 2^31 elements, chances are high that pointer2 is + // an actual out-of-bounds access at runtime. These would normally be prevented by range checks + // at runtime. However, if the access was done by using Unsafe, where range checks are omitted, + // then an out-of-bounds access constitutes undefined behavior. This means that we are allowed to + // do anything, including changing the behavior. + // + // If we can set the right conditions, we have a guarantee that an overflow is either impossible + // (no overflow or range checks preventing that) or undefined behavior. In both cases, we are + // safe to do a vectorization. + // + // Approach: We want to prove a lower bound for the distance between these two pointers, and an + // upper bound for the size of a memory object. We can derive such an upper bound for + // arrays. We know they have at most 2^31 elements. If we know the size of the elements + // in bytes, we have: + // + // array_element_size_in_bytes * 2^31 >= max_possible_array_size_in_bytes + // >= array_size_in_bytes (ARR) + // + // If some small difference "delta" leads to an int_index overflow, we know that the + // int_index1 before overflow must have been close to max_int, and the int_index2 after + // the overflow must be close to min_int: + // + // pointer1 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index1) + // =approx adr + long_offset + long_invar + long_scale * max_int + // + // pointer2 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index2) + // =approx adr + long_offset + long_invar + long_scale * min_int + // + // We realize that the pointer difference is very large: + // + // difference =approx long_scale * 2^32 + // + // Hence, if we set the right condition for long_scale and array_element_size_in_bytes, + // we can prove that an overflow is impossible (or would imply undefined behaviour). + // + // We must now take this intuition, and develop a rigorous proof. We start by stating the problem + // more precisely, with the help of some definitions and the Statement we are going to prove. + // + // Definition: + // Two SWPointers are "comparable" (i.e. SWPointer::comparable is true, set with SWPointer::cmp()), + // iff all of these conditions apply for the simple form: + // 1) Both SWPointers are valid. + // 2) The adr are identical, or both are array bases of different arrays. + // 3) They have identical scale. + // 4) They have identical invar. + // 5) The difference in offsets is limited: abs(offset1 - offset2) < 2^31. (DIFF) + // + // For the Vectorization Optimization, we pair-wise compare SWPointers and determine if they are: + // 1) "not comparable": + // We do not optimize them (assume they alias, not assume adjacency). + // + // Whenever we chose this option based on the simple form, it is also correct based on the + // compound-long-int form, since we make no optimizations based on it. + // + // 2) "comparable" with different array bases at runtime: + // We assume they do not alias (remove memory edges), but not assume adjacency. + // + // Whenever we have two different array bases for the simple form, we also have different + // array bases for the compound-long-form. Since SWPointers provably point to different + // memory objects, they can never alias. + // + // 3) "comparable" with the same base address: + // We compute the relative pointer difference, and based on the load/store size we can + // compute aliasing and adjacency. + // + // We must find a condition under which the pointer difference of the simple form is + // identical to the pointer difference of the compound-long-form. We do this with the + // Statement below, which we then proceed to prove. + // + // Statement: + // If two SWPointers satisfy these 3 conditions: + // 1) They are "comparable". + // 2) They have the same base address. + // 3) Their long_scale is a multiple of the array element size in bytes: + // + // abs(long_scale) % array_element_size_in_bytes = 0 (A) + // + // Then their pointer difference of the simple form is identical to the pointer difference + // of the compound-long-int form. + // + // More precisely: + // Such two SWPointers by definition have identical adr, invar, and scale. + // Their simple form is: + // + // s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv) (B1) + // s_pointer2 = adr + offset2 + invar + scale * ConvI2L(iv) (B2) + // + // Thus, the pointer difference of the simple forms collapses to the difference in offsets: + // + // s_difference = s_pointer1 - s_pointer2 = offset1 - offset2 (C) + // + // Their compound-long-int form for these SWPointer is: + // + // c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1) (D1) + // int_index1 = int_offset1 + int_invar1 + int_scale1 * iv (D2) + // + // c_pointer2 = adr + long_offset2 + long_invar2 + long_scale2 * ConvI2L(int_index2) (D3) + // int_index2 = int_offset2 + int_invar2 + int_scale2 * iv (D4) + // + // And these are the offset1, offset2, invar and scale from the simple form (B1) and (B2): + // + // offset1 = long_offset1 + long_scale1 * ConvI2L(int_offset1) (D5) + // offset2 = long_offset2 + long_scale2 * ConvI2L(int_offset2) (D6) + // + // invar = long_invar1 + long_scale1 * ConvI2L(int_invar1) + // = long_invar2 + long_scale2 * ConvI2L(int_invar2) (D7) + // + // scale = long_scale1 * ConvI2L(int_scale1) + // = long_scale2 * ConvI2L(int_scale2) (D8) + // + // The pointer difference of the compound-long-int form is defined as: + // + // c_difference = c_pointer1 - c_pointer2 + // + // Thus, the statement claims that for the two SWPointer we have: + // + // s_difference = c_difference (Statement) + // + // We prove the Statement with the help of a Lemma: + // + // Lemma: + // There is some integer x, such that: + // + // c_difference = s_difference + array_element_size_in_bytes * x * 2^32 (Lemma) + // + // From condition (DIFF), we can derive: + // + // abs(s_difference) < 2^31 (E) + // + // Assuming the Lemma, we prove the Statement: + // If "x = 0" (intuitively: the int_index does not overflow), then: + // c_difference = s_difference + // and hence the simple form computes the same pointer difference as the compound-long-int form. + // If "x != 0" (intuitively: the int_index overflows), then: + // abs(c_difference) >= abs(s_difference + array_element_size_in_bytes * x * 2^32) + // >= array_element_size_in_bytes * 2^32 - abs(s_difference) + // -- apply (E) -- + // > array_element_size_in_bytes * 2^32 - 2^31 + // >= array_element_size_in_bytes * 2^31 + // -- apply (ARR) -- + // >= max_possible_array_size_in_bytes + // >= array_size_in_bytes + // + // This shows that c_pointer1 and c_pointer2 have a distance that exceeds the maximum array size. + // Thus, at least one of the two pointers must be outside of the array bounds. But we can assume + // that out-of-bounds accesses do not happen. If they still do, it is undefined behavior. Hence, + // we are allowed to do anything. We can also "safely" use the simple form in this case even though + // it might not match the compound-long-int form at runtime. + // QED Statement. + // + // We must now prove the Lemma. + // + // ConvI2L always truncates by some power of 2^32, i.e. there is some integer y such that: + // + // ConvI2L(y1 + y2) = ConvI2L(y1) + ConvI2L(y2) + 2^32 * y (F) + // + // It follows, that there is an integer y1 such that: + // + // ConvI2L(int_index1) = ConvI2L(int_offset1 + int_invar1 + int_scale1 * iv) + // -- apply (F) -- + // = ConvI2L(int_offset1) + // + ConvI2L(int_invar1) + // + ConvI2L(int_scale1) * ConvI2L(iv) + // + y1 * 2^32 (G) + // + // Thus, we can write the compound-long-int form (D1) as: + // + // c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1) + // -- apply (G) -- + // = adr + // + long_offset1 + // + long_invar1 + // + long_scale1 * ConvI2L(int_offset1) + // + long_scale1 * ConvI2L(int_invar1) + // + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv) + // + long_scale1 * y1 * 2^32 (H) + // + // And we can write the simple form as: + // + // s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv) + // -- apply (D5, D7, D8) -- + // = adr + // + long_offset1 + // + long_scale1 * ConvI2L(int_offset1) + // + long_invar1 + // + long_scale1 * ConvI2L(int_invar1) + // + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv) (K) + // + // We now compute the pointer difference between the simple (K) and compound-long-int form (H). + // Most terms cancel out immediately: + // + // sc_difference1 = c_pointer1 - s_pointer1 = long_scale1 * y1 * 2^32 (L) + // + // Rearranging the equation (L), we get: + // + // c_pointer1 = s_pointer1 + long_scale1 * y1 * 2^32 (M) + // + // And since long_scale1 is a multiple of array_element_size_in_bytes, there is some integer + // x1, such that (M) implies: + // + // c_pointer1 = s_pointer1 + array_element_size_in_bytes * x1 * 2^32 (N) + // + // With an analogue equation for c_pointer2, we can now compute the pointer difference for + // the compound-long-int form: + // + // c_difference = c_pointer1 - c_pointer2 + // -- apply (N) -- + // = s_pointer1 + array_element_size_in_bytes * x1 * 2^32 + // -(s_pointer2 + array_element_size_in_bytes * x2 * 2^32) + // -- where "x = x1 - x2" -- + // = s_pointer1 - s_pointer2 + array_element_size_in_bytes * x * 2^32 + // -- apply (C) -- + // = s_difference + array_element_size_in_bytes * x * 2^32 + // QED Lemma. + if (ary_ptr_t != nullptr) { + BasicType array_element_bt = ary_ptr_t->elem()->array_element_basic_type(); + if (is_java_primitive(array_element_bt)) { + int array_element_size_in_bytes = type2aelembytes(array_element_bt); + if (abs(long_scale) % array_element_size_in_bytes == 0) { + return true; + } + } + } + + // General case: we do not know if it is safe to use the simple form. + return false; +#endif +} + bool SWPointer::is_main_loop_member(Node* n) const { Node* n_c = phase()->get_ctrl(n); return lpt()->is_member(phase()->get_loop(n_c)); @@ -3848,11 +4228,42 @@ bool SWPointer::scaled_iv_plus_offset(Node* n) { } } else if (opc == Op_SubI) { if (offset_plus_k(n->in(2), true) && scaled_iv_plus_offset(n->in(1))) { + // (offset1 + invar1 + scale * iv) - (offset2) or + // (offset1 + scale * iv) - (offset2 + invar1) + // Subtraction handled via "negate" flag of "offset_plus_k". NOT_PRODUCT(_tracer.scaled_iv_plus_offset_6(n);) return true; } - if (offset_plus_k(n->in(1)) && scaled_iv_plus_offset(n->in(2))) { - _scale *= -1; + SWPointer tmp(this); + if (offset_plus_k(n->in(1)) && tmp.scaled_iv_plus_offset(n->in(2))) { + // (offset1 + invar1) - (offset2 + scale * iv) or + // (offset1) - (offset2 + invar1 + scale * iv) + // Subtraction handled explicitly below. + assert(_scale == 0, "shouldn't be set yet"); + // _scale = -tmp._scale + if (!try_MulI_no_overflow(-1, tmp._scale, _scale)) { + return false; // mul overflow. + } + // _offset -= tmp._offset + if (!try_SubI_no_overflow(_offset, tmp._offset, _offset)) { + return false; // sub overflow. + } + // _invar -= tmp._invar + if (tmp._invar != nullptr) { + if (_invar != nullptr) { + return false; + } + _invar = tmp._invar; + _invar_scale = tmp._invar_scale; + _negate_invar = !tmp._negate_invar; + } + + // SWPointer tmp does not have an integer part to be forwarded + // (tmp._has_int_index_after_convI2L is false) because n is a SubI, all + // nodes above must also be of integer type (ConvL2I is not handled + // to allow a long) and ConvI2L (the only node that can add an integer + // part) won't be present. + NOT_PRODUCT(_tracer.scaled_iv_plus_offset_7(n);) return true; } @@ -3895,10 +4306,57 @@ bool SWPointer::scaled_iv(Node* n) { } } else if (opc == Op_LShiftI) { if (n->in(1) == iv() && n->in(2)->is_Con()) { - _scale = 1 << n->in(2)->get_int(); + if (!try_LShiftI_no_overflow(1, n->in(2)->get_int(), _scale)) { + return false; // shift overflow. + } NOT_PRODUCT(_tracer.scaled_iv_6(n, _scale);) return true; } + } else if (opc == Op_ConvI2L && !has_iv()) { + // So far we have not found the iv yet, and are about to enter a ConvI2L subgraph, + // which may be the int index (that might overflow) for the memory access, of the form: + // + // int_index = int_offset + int_invar + int_scale * iv + // + // If we simply continue parsing with the current SWPointer, then the int_offset and + // int_invar simply get added to the long offset and invar. But for the checks in + // SWPointer::is_safe_to_use_as_simple_form() we need to have explicit access to the + // int_index. Thus, we must parse it explicitly here. For this, we use a temporary + // SWPointer, to pattern match the int_index sub-expression of the address. + + NOT_PRODUCT(Tracer::Depth dddd;) + SWPointer tmp(this); + NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);) + + if (tmp.scaled_iv_plus_offset(n->in(1)) && tmp.has_iv()) { + // We successfully matched an integer index, of the form: + // int_index = int_offset + int_invar + int_scale * iv + // Forward scale. + assert(_scale == 0 && tmp._scale != 0, "iv only found just now"); + _scale = tmp._scale; + // Accumulate offset. + if (!try_AddI_no_overflow(_offset, tmp._offset, _offset)) { + return false; // add overflow. + } + // Forward invariant if not already found. + if (tmp._invar != nullptr) { + if (_invar != nullptr) { + return false; + } + _invar = tmp._invar; + _invar_scale = tmp._invar_scale; + _negate_invar = tmp._negate_invar; + } + // Set info about the int_index: + assert(!_has_int_index_after_convI2L, "no previous int_index discovered"); + _has_int_index_after_convI2L = true; + _int_index_after_convI2L_offset = tmp._offset; + _int_index_after_convI2L_invar = tmp._invar; + _int_index_after_convI2L_scale = tmp._scale; + + NOT_PRODUCT(_tracer.scaled_iv_7(n);) + return true; + } } else if (opc == Op_ConvI2L || opc == Op_CastII) { if (scaled_iv_plus_offset(n->in(1))) { NOT_PRODUCT(_tracer.scaled_iv_7(n);) @@ -3914,14 +4372,33 @@ bool SWPointer::scaled_iv(Node* n) { NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);) if (tmp.scaled_iv_plus_offset(n->in(1))) { - int scale = n->in(2)->get_int(); - _scale = tmp._scale << scale; - _offset += tmp._offset << scale; + int shift = n->in(2)->get_int(); + // Accumulate scale. + if (!try_LShiftI_no_overflow(tmp._scale, shift, _scale)) { + return false; // shift overflow. + } + // Accumulate offset. + int shifted_offset = 0; + if (!try_LShiftI_no_overflow(tmp._offset, shift, shifted_offset)) { + return false; // shift overflow. + } + if (!try_AddI_no_overflow(_offset, shifted_offset, _offset)) { + return false; // add overflow. + } + // Accumulate invar. _invar = tmp._invar; if (_invar != nullptr) { _negate_invar = tmp._negate_invar; _invar_scale = n->in(2); } + + // Forward info about the int_index: + assert(!_has_int_index_after_convI2L, "no previous int_index discovered"); + _has_int_index_after_convI2L = tmp._has_int_index_after_convI2L; + _int_index_after_convI2L_offset = tmp._int_index_after_convI2L_offset; + _int_index_after_convI2L_invar = tmp._int_index_after_convI2L_invar; + _int_index_after_convI2L_scale = tmp._int_index_after_convI2L_scale; + NOT_PRODUCT(_tracer.scaled_iv_9(n, _scale, _offset, _invar, _negate_invar);) return true; } @@ -3940,7 +4417,9 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { int opc = n->Opcode(); if (opc == Op_ConI) { - _offset += negate ? -(n->get_int()) : n->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_2(n, _offset);) return true; } else if (opc == Op_ConL) { @@ -3949,7 +4428,9 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { if (t->higher_equal(TypeLong::INT)) { jlong loff = n->get_long(); jint off = (jint)loff; - _offset += negate ? -off : loff; + if (!try_AddSubI_no_overflow(_offset, off, negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_3(n, _offset);) return true; } @@ -3968,11 +4449,15 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { if (n->in(2)->is_Con() && invariant(n->in(1))) { _negate_invar = negate; _invar = n->in(1); - _offset += negate ? -(n->in(2)->get_int()) : n->in(2)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_6(n, _invar, _negate_invar, _offset);) return true; } else if (n->in(1)->is_Con() && invariant(n->in(2))) { - _offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } _negate_invar = negate; _invar = n->in(2); NOT_PRODUCT(_tracer.offset_plus_k_7(n, _invar, _negate_invar, _offset);) @@ -3983,11 +4468,15 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { if (n->in(2)->is_Con() && invariant(n->in(1))) { _negate_invar = negate; _invar = n->in(1); - _offset += !negate ? -(n->in(2)->get_int()) : n->in(2)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), !negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_8(n, _invar, _negate_invar, _offset);) return true; } else if (n->in(1)->is_Con() && invariant(n->in(2))) { - _offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } _negate_invar = !negate; _invar = n->in(2); NOT_PRODUCT(_tracer.offset_plus_k_9(n, _invar, _negate_invar, _offset);) @@ -4018,6 +4507,57 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { return false; } +bool SWPointer::try_AddI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_add((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_add((jint)(offset1), (jint)(offset2)); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool SWPointer::try_SubI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_subtract((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_subtract((jint)(offset1), (jint)(offset2)); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool SWPointer::try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result) { + if (is_sub) { + return try_SubI_no_overflow(offset1, offset2, result); + } else { + return try_AddI_no_overflow(offset1, offset2, result); + } +} + +bool SWPointer::try_LShiftI_no_overflow(int offset, int shift, int& result) { + if (shift < 0 || shift > 31) { + return false; + } + jlong long_offset = java_shift_left((jlong)(offset), (julong)((jlong)(shift))); + jint int_offset = java_shift_left((jint)(offset), (juint)((jint)(shift))); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool SWPointer::try_MulI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_multiply((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_multiply((jint)(offset1), (jint)(offset2)); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + //----------------------------print------------------------ void SWPointer::print() { #ifndef PRODUCT diff --git a/src/hotspot/share/opto/superword.hpp b/src/hotspot/share/opto/superword.hpp index f53a25c5bfb12..5d95a14cd0656 100644 --- a/src/hotspot/share/opto/superword.hpp +++ b/src/hotspot/share/opto/superword.hpp @@ -572,13 +572,51 @@ class SuperWord : public ResourceObj { //------------------------------SWPointer--------------------------- // Information about an address for dependence checking and vector alignment +// +// We parse and represent pointers of the simple form: +// +// pointer = adr + offset + invar + scale * ConvI2L(iv) +// +// Where: +// +// adr: the base address of an array (base = adr) +// OR +// some address to off-heap memory (base = TOP) +// +// offset: a constant offset +// invar: a runtime variable, which is invariant during the loop +// scale: scaling factor +// iv: loop induction variable +// +// But more precisely, we parse the composite-long-int form: +// +// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + inv_invar + int_scale * iv) +// +// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index) +// int_index = int_offset + int_invar + int_scale * iv +// +// However, for aliasing and adjacency checks (e.g. SWPointer::cmp()) we always use the simple form to make +// decisions. Hence, we must make sure to only create a "valid" SWPointer if the optimisations based on the +// simple form produce the same result as the compound-long-int form would. Intuitively, this depends on +// if the int_index overflows, but the precise conditions are given in SWPointer::is_safe_to_use_as_simple_form(). +// +// ConvI2L(int_index) = ConvI2L(int_offset + int_invar + int_scale * iv) +// = Convi2L(int_offset) + ConvI2L(int_invar) + ConvI2L(int_scale) * ConvI2L(iv) +// +// scale = long_scale * ConvI2L(int_scale) +// offset = long_offset + long_scale * ConvI2L(int_offset) +// invar = long_invar + long_scale * ConvI2L(int_invar) +// +// pointer = adr + offset + invar + scale * ConvI2L(iv) +// class SWPointer { protected: MemNode* _mem; // My memory reference node SuperWord* _slp; // SuperWord class - Node* _base; // null if unsafe nonheap reference - Node* _adr; // address pointer + // Components of the simple form: + Node* _base; // Base address of an array OR null if some off-heap memory. + Node* _adr; // Same as _base if an array pointer OR some off-heap memory pointer. int _scale; // multiplier for iv (in bytes), 0 if no loop iv int _offset; // constant offset (in bytes) @@ -586,6 +624,13 @@ class SWPointer { bool _negate_invar; // if true then use: (0 - _invar) Node* _invar_scale; // multiplier for invariant + // The int_index components of the compound-long-int form. Used to decide if it is safe to use the + // simple form rather than the compound-long-int form that was parsed. + bool _has_int_index_after_convI2L; + int _int_index_after_convI2L_offset; + Node* _int_index_after_convI2L_invar; + int _int_index_after_convI2L_scale; + Node_Stack* _nstack; // stack used to record a swpointer trace of variants bool _analyze_only; // Used in loop unrolling only for swpointer trace uint _stack_idx; // Used in loop unrolling only for swpointer trace @@ -604,6 +649,8 @@ class SWPointer { // Match: offset is (k [+/- invariant]) bool offset_plus_k(Node* n, bool negate = false); + bool is_safe_to_use_as_simple_form(Node* base, Node* adr) const; + public: enum CMP { Less = 1, @@ -639,10 +686,43 @@ class SWPointer { _negate_invar == q._negate_invar); } + // We compute if and how two SWPointers can alias at runtime, i.e. if the two addressed regions of memory can + // ever overlap. There are essentially 3 relevant return states: + // - NotComparable: Synonymous to "unknown aliasing". + // We have no information about how the two SWPointers can alias. They could overlap, refer + // to another location in the same memory object, or point to a completely different object. + // -> Memory edge required. Aliasing unlikely but possible. + // + // - Less / Greater: Synonymous to "never aliasing". + // The two SWPointers may point into the same memory object, but be non-aliasing (i.e. we + // know both address regions inside the same memory object, but these regions are non- + // overlapping), or the SWPointers point to entirely different objects. + // -> No memory edge required. Aliasing impossible. + // + // - Equal: Synonymous to "overlap, or point to different memory objects". + // The two SWPointers either overlap on the same memory object, or point to two different + // memory objects. + // -> Memory edge required. Aliasing likely. + // + // In a future refactoring, we can simplify to two states: + // - NeverAlias: instead of Less / Greater + // - MayAlias: instead of Equal / NotComparable + // + // Two SWPointer are "comparable" (Less / Greater / Equal), iff all of these conditions apply: + // 1) Both are valid, i.e. expressible in the compound-long-int or simple form. + // 2) The adr are identical, or both are array bases of different arrays. + // 3) They have identical scale. + // 4) They have identical invar. + // 5) The difference in offsets is limited: abs(offset0 - offset1) < 2^31. int cmp(SWPointer& q) { if (valid() && q.valid() && (_adr == q._adr || (_base == _adr && q._base == q._adr)) && _scale == q._scale && invar_equals(q)) { + jlong difference = abs(java_subtract((jlong)_offset, (jlong)q._offset)); + jlong max_diff = (jlong)1 << 31; + if (difference >= max_diff) { + return NotComparable; + } bool overlap = q._offset < _offset + memory_size() && _offset < q._offset + q.memory_size(); return overlap ? Equal : (_offset < q._offset ? Less : Greater); @@ -729,6 +809,13 @@ class SWPointer { } _tracer;//TRacer; #endif + + static bool try_AddI_no_overflow(int offset1, int offset2, int& result); + static bool try_SubI_no_overflow(int offset1, int offset2, int& result); + static bool try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result); + static bool try_LShiftI_no_overflow(int offset1, int offset2, int& result); + static bool try_MulI_no_overflow(int offset1, int offset2, int& result); + }; #endif // SHARE_OPTO_SUPERWORD_HPP diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index e99bf8eb8bb32..f13ffbc12afb4 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -1624,17 +1624,17 @@ bool TypeInt::is_finite() const { //------------------------------dump2------------------------------------------ // Dump TypeInt #ifndef PRODUCT -static const char* intname(char* buf, jint n) { +static const char* intname(char* buf, size_t buf_size, jint n) { if (n == min_jint) return "min"; else if (n < min_jint + 10000) - sprintf(buf, "min+" INT32_FORMAT, n - min_jint); + os::snprintf_checked(buf, buf_size, "min+" INT32_FORMAT, n - min_jint); else if (n == max_jint) return "max"; else if (n > max_jint - 10000) - sprintf(buf, "max-" INT32_FORMAT, max_jint - n); + os::snprintf_checked(buf, buf_size, "max-" INT32_FORMAT, max_jint - n); else - sprintf(buf, INT32_FORMAT, n); + os::snprintf_checked(buf, buf_size, INT32_FORMAT, n); return buf; } @@ -1643,7 +1643,7 @@ void TypeInt::dump2( Dict &d, uint depth, outputStream *st ) const { if (_lo == min_jint && _hi == max_jint) st->print("int"); else if (is_con()) - st->print("int:%s", intname(buf, get_con())); + st->print("int:%s", intname(buf, sizeof(buf), get_con())); else if (_lo == BOOL->_lo && _hi == BOOL->_hi) st->print("bool"); else if (_lo == BYTE->_lo && _hi == BYTE->_hi) @@ -1653,11 +1653,11 @@ void TypeInt::dump2( Dict &d, uint depth, outputStream *st ) const { else if (_lo == SHORT->_lo && _hi == SHORT->_hi) st->print("short"); else if (_hi == max_jint) - st->print("int:>=%s", intname(buf, _lo)); + st->print("int:>=%s", intname(buf, sizeof(buf), _lo)); else if (_lo == min_jint) - st->print("int:<=%s", intname(buf, _hi)); + st->print("int:<=%s", intname(buf, sizeof(buf), _hi)); else - st->print("int:%s..%s", intname(buf, _lo), intname(buf2, _hi)); + st->print("int:%s..%s", intname(buf, sizeof(buf), _lo), intname(buf2, sizeof(buf2), _hi)); if (_widen != 0 && this != TypeInt::INT) st->print(":%.*s", _widen, "wwww"); @@ -1888,37 +1888,37 @@ bool TypeLong::is_finite() const { //------------------------------dump2------------------------------------------ // Dump TypeLong #ifndef PRODUCT -static const char* longnamenear(jlong x, const char* xname, char* buf, jlong n) { +static const char* longnamenear(jlong x, const char* xname, char* buf, size_t buf_size, jlong n) { if (n > x) { if (n >= x + 10000) return nullptr; - sprintf(buf, "%s+" JLONG_FORMAT, xname, n - x); + os::snprintf_checked(buf, buf_size, "%s+" JLONG_FORMAT, xname, n - x); } else if (n < x) { if (n <= x - 10000) return nullptr; - sprintf(buf, "%s-" JLONG_FORMAT, xname, x - n); + os::snprintf_checked(buf, buf_size, "%s-" JLONG_FORMAT, xname, x - n); } else { return xname; } return buf; } -static const char* longname(char* buf, jlong n) { +static const char* longname(char* buf, size_t buf_size, jlong n) { const char* str; if (n == min_jlong) return "min"; else if (n < min_jlong + 10000) - sprintf(buf, "min+" JLONG_FORMAT, n - min_jlong); + os::snprintf_checked(buf, buf_size, "min+" JLONG_FORMAT, n - min_jlong); else if (n == max_jlong) return "max"; else if (n > max_jlong - 10000) - sprintf(buf, "max-" JLONG_FORMAT, max_jlong - n); - else if ((str = longnamenear(max_juint, "maxuint", buf, n)) != nullptr) + os::snprintf_checked(buf, buf_size, "max-" JLONG_FORMAT, max_jlong - n); + else if ((str = longnamenear(max_juint, "maxuint", buf, buf_size, n)) != nullptr) return str; - else if ((str = longnamenear(max_jint, "maxint", buf, n)) != nullptr) + else if ((str = longnamenear(max_jint, "maxint", buf, buf_size, n)) != nullptr) return str; - else if ((str = longnamenear(min_jint, "minint", buf, n)) != nullptr) + else if ((str = longnamenear(min_jint, "minint", buf, buf_size, n)) != nullptr) return str; else - sprintf(buf, JLONG_FORMAT, n); + os::snprintf_checked(buf, buf_size, JLONG_FORMAT, n); return buf; } @@ -1927,13 +1927,13 @@ void TypeLong::dump2( Dict &d, uint depth, outputStream *st ) const { if (_lo == min_jlong && _hi == max_jlong) st->print("long"); else if (is_con()) - st->print("long:%s", longname(buf, get_con())); + st->print("long:%s", longname(buf, sizeof(buf), get_con())); else if (_hi == max_jlong) - st->print("long:>=%s", longname(buf, _lo)); + st->print("long:>=%s", longname(buf, sizeof(buf), _lo)); else if (_lo == min_jlong) - st->print("long:<=%s", longname(buf, _hi)); + st->print("long:<=%s", longname(buf, sizeof(buf), _hi)); else - st->print("long:%s..%s", longname(buf, _lo), longname(buf2, _hi)); + st->print("long:%s..%s", longname(buf, sizeof(buf), _lo), longname(buf2,sizeof(buf2), _hi)); if (_widen != 0 && this != TypeLong::LONG) st->print(":%.*s", _widen, "wwww"); @@ -3720,24 +3720,24 @@ const TypeInstPtr *TypeInstPtr::xmeet_unloaded(const TypeInstPtr *tinst) const { // assert(loaded->ptr() != TypePtr::Null, "insanity check"); // - if( loaded->ptr() == TypePtr::TopPTR ) { return unloaded; } + if( loaded->ptr() == TypePtr::TopPTR ) { return unloaded->with_speculative(speculative); } else if (loaded->ptr() == TypePtr::AnyNull) { return TypeInstPtr::make(ptr, unloaded->klass(), false, nullptr, off, instance_id, speculative, depth); } - else if (loaded->ptr() == TypePtr::BotPTR ) { return TypeInstPtr::BOTTOM; } + else if (loaded->ptr() == TypePtr::BotPTR ) { return TypeInstPtr::BOTTOM->with_speculative(speculative); } else if (loaded->ptr() == TypePtr::Constant || loaded->ptr() == TypePtr::NotNull) { - if (unloaded->ptr() == TypePtr::BotPTR ) { return TypeInstPtr::BOTTOM; } - else { return TypeInstPtr::NOTNULL; } + if (unloaded->ptr() == TypePtr::BotPTR ) { return TypeInstPtr::BOTTOM->with_speculative(speculative); } + else { return TypeInstPtr::NOTNULL->with_speculative(speculative); } } - else if( unloaded->ptr() == TypePtr::TopPTR ) { return unloaded; } + else if( unloaded->ptr() == TypePtr::TopPTR ) { return unloaded->with_speculative(speculative); } - return unloaded->cast_to_ptr_type(TypePtr::AnyNull)->is_instptr(); + return unloaded->cast_to_ptr_type(TypePtr::AnyNull)->is_instptr()->with_speculative(speculative); } // Both are unloaded, not the same class, not Object // Or meet unloaded with a different loaded class, not java/lang/Object if( ptr != TypePtr::BotPTR ) { - return TypeInstPtr::NOTNULL; + return TypeInstPtr::NOTNULL->with_speculative(speculative); } - return TypeInstPtr::BOTTOM; + return TypeInstPtr::BOTTOM->with_speculative(speculative); } @@ -4124,6 +4124,10 @@ const Type *TypeInstPtr::remove_speculative() const { _instance_id, nullptr, _inline_depth); } +const TypeInstPtr* TypeInstPtr::with_speculative(const TypePtr* speculative) const { + return make(_ptr, klass(), klass_is_exact(), const_oop(), _offset, _instance_id, speculative, _inline_depth); +} + const TypePtr *TypeInstPtr::with_inline_depth(int depth) const { if (!UseInlineDepthForSpeculativeTypes) { return this; diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 302fd71ac6a8d..2549cae17a799 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -1191,6 +1191,7 @@ class TypeInstPtr : public TypeOopPtr { // Speculative type helper methods. virtual const Type* remove_speculative() const; + const TypeInstPtr* with_speculative(const TypePtr* speculative) const; virtual const TypePtr* with_inline_depth(int depth) const; virtual const TypePtr* with_instance_id(int instance_id) const; diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index 6c8adadec28ce..b8b914dddfd09 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -3527,7 +3527,15 @@ static void post_thread_start_event(const JavaThread* jt) { extern const struct JNIInvokeInterface_ jni_InvokeInterface; // Global invocation API vars -volatile int vm_created = 0; +enum VM_Creation_State { + NOT_CREATED = 0, + IN_PROGRESS, // Most JNI operations are permitted during this phase to + // allow for initialization actions by libraries and agents. + COMPLETE +}; + +volatile VM_Creation_State vm_created = NOT_CREATED; + // Indicate whether it is safe to recreate VM. Recreation is only // possible after a failed initial creation attempt in some cases. volatile int safe_to_recreate_vm = 1; @@ -3596,7 +3604,7 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) { // We use Atomic::xchg rather than Atomic::add/dec since on some platforms // the add/dec implementations are dependent on whether we are running // on a multiprocessor Atomic::xchg does not have this problem. - if (Atomic::xchg(&vm_created, 1) == 1) { + if (Atomic::xchg(&vm_created, IN_PROGRESS) != NOT_CREATED) { return JNI_EEXIST; // already created, or create attempt in progress } @@ -3609,8 +3617,6 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) { return JNI_ERR; } - assert(vm_created == 1, "vm_created is true during the creation"); - /** * Certain errors during initialization are recoverable and do not * prevent this method from being called again at a later time @@ -3627,9 +3633,11 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) { if (result == JNI_OK) { JavaThread *thread = JavaThread::current(); assert(!thread->has_pending_exception(), "should have returned not OK"); - /* thread is thread_in_vm here */ + // thread is thread_in_vm here *vm = (JavaVM *)(&main_vm); *(JNIEnv**)penv = thread->jni_environment(); + // mark creation complete for other JNI ops + Atomic::release_store(&vm_created, COMPLETE); #if INCLUDE_JVMCI if (EnableJVMCI) { @@ -3694,7 +3702,8 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) { *(JNIEnv**)penv = 0; // reset vm_created last to avoid race condition. Use OrderAccess to // control both compiler and architectural-based reordering. - Atomic::release_store(&vm_created, 0); + assert(vm_created == IN_PROGRESS, "must be"); + Atomic::release_store(&vm_created, NOT_CREATED); } // Flush stdout and stderr before exit. @@ -3723,7 +3732,7 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, v _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetCreatedJavaVMs(JavaVM **vm_buf, jsize bufLen, jsize *numVMs) { HOTSPOT_JNI_GETCREATEDJAVAVMS_ENTRY((void **) vm_buf, bufLen, (uintptr_t *) numVMs); - if (vm_created == 1) { + if (vm_created == COMPLETE) { if (numVMs != NULL) *numVMs = 1; if (bufLen > 0) *vm_buf = (JavaVM *)(&main_vm); } else { @@ -3743,7 +3752,7 @@ static jint JNICALL jni_DestroyJavaVM_inner(JavaVM *vm) { jint res = JNI_ERR; DT_RETURN_MARK(DestroyJavaVM, jint, (const jint&)res); - if (vm_created == 0) { + if (vm_created == NOT_CREATED) { res = JNI_ERR; return res; } @@ -3767,7 +3776,7 @@ static jint JNICALL jni_DestroyJavaVM_inner(JavaVM *vm) { ThreadStateTransition::transition_from_native(thread, _thread_in_vm); Threads::destroy_vm(); // Don't bother restoring thread state, VM is gone. - vm_created = 0; + vm_created = NOT_CREATED; return JNI_OK; } @@ -3904,7 +3913,8 @@ static jint attach_current_thread(JavaVM *vm, void **penv, void *_args, bool dae jint JNICALL jni_AttachCurrentThread(JavaVM *vm, void **penv, void *_args) { HOTSPOT_JNI_ATTACHCURRENTTHREAD_ENTRY(vm, penv, _args); - if (vm_created == 0) { + if (vm_created == NOT_CREATED) { + // Not sure how we could possibly get here. HOTSPOT_JNI_ATTACHCURRENTTHREAD_RETURN((uint32_t) JNI_ERR); return JNI_ERR; } @@ -3917,7 +3927,8 @@ jint JNICALL jni_AttachCurrentThread(JavaVM *vm, void **penv, void *_args) { jint JNICALL jni_DetachCurrentThread(JavaVM *vm) { HOTSPOT_JNI_DETACHCURRENTTHREAD_ENTRY(vm); - if (vm_created == 0) { + if (vm_created == NOT_CREATED) { + // Not sure how we could possibly get here. HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN(JNI_ERR); return JNI_ERR; } @@ -3980,7 +3991,7 @@ jint JNICALL jni_GetEnv(JavaVM *vm, void **penv, jint version) { jint ret = JNI_ERR; DT_RETURN_MARK(GetEnv, jint, (const jint&)ret); - if (vm_created == 0) { + if (vm_created == NOT_CREATED) { *penv = NULL; ret = JNI_EDETACHED; return ret; @@ -4031,8 +4042,9 @@ jint JNICALL jni_GetEnv(JavaVM *vm, void **penv, jint version) { jint JNICALL jni_AttachCurrentThreadAsDaemon(JavaVM *vm, void **penv, void *_args) { HOTSPOT_JNI_ATTACHCURRENTTHREADASDAEMON_ENTRY(vm, penv, _args); - if (vm_created == 0) { - HOTSPOT_JNI_ATTACHCURRENTTHREADASDAEMON_RETURN((uint32_t) JNI_ERR); + if (vm_created == NOT_CREATED) { + // Not sure how we could possibly get here. + HOTSPOT_JNI_ATTACHCURRENTTHREADASDAEMON_RETURN((uint32_t) JNI_ERR); return JNI_ERR; } diff --git a/src/hotspot/share/prims/jniCheck.cpp b/src/hotspot/share/prims/jniCheck.cpp index 52fa413fa1fba..490edb45318b5 100644 --- a/src/hotspot/share/prims/jniCheck.cpp +++ b/src/hotspot/share/prims/jniCheck.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2316,7 +2316,7 @@ struct JNINativeInterface_* jni_functions_check() { // make sure the last pointer in the checked table is not null, indicating // an addition to the JNINativeInterface_ structure without initializing // it in the checked table. - debug_only(int *lastPtr = (int *)((char *)&checked_jni_NativeInterface + \ + debug_only(intptr_t *lastPtr = (intptr_t *)((char *)&checked_jni_NativeInterface + \ sizeof(*unchecked_jni_NativeInterface) - sizeof(char *));) assert(*lastPtr != 0, "Mismatched JNINativeInterface tables, check for new entries"); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 9e29c3300cfce..7419921cd9ae4 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2823,7 +2823,7 @@ void jio_print(const char* s, size_t len) { jio_fprintf(defaultStream::output_stream(), "%.*s", (int)len, s); } else { // Make an unused local variable to avoid warning from gcc compiler. - size_t count = ::write(defaultStream::output_fd(), s, (int)len); + bool dummy = os::write(defaultStream::output_fd(), s, len); } } diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index adf9047bd1c52..a7590528ce687 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -719,11 +719,14 @@ void JvmtiEventControllerPrivate::set_event_callbacks(JvmtiEnvBase *env, flush_object_free_events(env); env->set_event_callbacks(callbacks, size_of_callbacks); - jlong enabled_bits = 0; + jlong enabled_bits = env->env_event_enable()->_event_callback_enabled.get_bits(); for (int ei = JVMTI_MIN_EVENT_TYPE_VAL; ei <= JVMTI_MAX_EVENT_TYPE_VAL; ++ei) { jvmtiEvent evt_t = (jvmtiEvent)ei; + jlong bit_for = JvmtiEventEnabled::bit_for(evt_t); if (env->has_callback(evt_t)) { - enabled_bits |= JvmtiEventEnabled::bit_for(evt_t); + enabled_bits |= bit_for; + } else { + enabled_bits &= ~bit_for; } } env->env_event_enable()->_event_callback_enabled.set_bits(enabled_bits); diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index 6807fca7180c8..968909deb6060 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -130,13 +130,9 @@ static inline void assert_field_offset_sane(oop p, jlong field_offset) { static inline void* index_oop_from_field_offset_long(oop p, jlong field_offset) { assert_field_offset_sane(p, field_offset); - jlong byte_offset = field_offset_to_byte_offset(field_offset); - - if (sizeof(char*) == sizeof(jint)) { // (this constant folds!) - return cast_from_oop

    (p) + (jint) byte_offset; - } else { - return cast_from_oop
    (p) + byte_offset; - } + uintptr_t base_address = cast_from_oop(p); + uintptr_t byte_offset = (uintptr_t)field_offset_to_byte_offset(field_offset); + return (void*)(base_address + byte_offset); } // Externally callable versions: diff --git a/src/hotspot/share/prims/wbtestmethods/parserTests.cpp b/src/hotspot/share/prims/wbtestmethods/parserTests.cpp index c6ef6cf82963f..9f9afaf27610d 100644 --- a/src/hotspot/share/prims/wbtestmethods/parserTests.cpp +++ b/src/hotspot/share/prims/wbtestmethods/parserTests.cpp @@ -174,7 +174,7 @@ WB_ENTRY(jobjectArray, WB_ParseCommandLine(JNIEnv* env, jobject o, jstring j_cmd if (arg) { arg->value_as_str(buf, sizeof(buf)); } else { - sprintf(buf, ""); + os::snprintf_checked(buf, sizeof(buf), ""); } oop parsedValue = java_lang_String::create_oop_from_str(buf, CHECK_NULL); returnvalue_array_ah->obj_at_put(i*2+1, parsedValue); diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 5466ff988a4aa..dd0cfdbbe5a58 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -1782,6 +1782,12 @@ WB_ENTRY(jint, WB_GetConstantPoolCacheLength(JNIEnv* env, jobject wb, jclass kla return cp->cache()->length(); WB_END +WB_ENTRY(jobjectArray, WB_GetResolvedReferences(JNIEnv* env, jobject wb, jclass klass)) + InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve(klass))); + objArrayOop resolved_refs= ik->constants()->resolved_references(); + return (jobjectArray)JNIHandles::make_local(THREAD, resolved_refs); +WB_END + WB_ENTRY(jint, WB_ConstantPoolRemapInstructionOperandFromCache(JNIEnv* env, jobject wb, jclass klass, jint index)) InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve(klass))); ConstantPool* cp = ik->constants(); @@ -1913,18 +1919,6 @@ WB_ENTRY(jboolean, WB_AreSharedStringsIgnored(JNIEnv* env)) return !HeapShared::closed_archive_heap_region_mapped(); WB_END -WB_ENTRY(jobject, WB_GetResolvedReferences(JNIEnv* env, jobject wb, jclass clazz)) - Klass *k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); - if (k->is_instance_klass()) { - InstanceKlass *ik = InstanceKlass::cast(k); - ConstantPool *cp = ik->constants(); - objArrayOop refs = cp->resolved_references(); - return (jobject)JNIHandles::make_local(THREAD, refs); - } else { - return NULL; - } -WB_END - WB_ENTRY(void, WB_LinkClass(JNIEnv* env, jobject wb, jclass clazz)) Klass *k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); if (!k->is_instance_klass()) { @@ -2553,6 +2547,7 @@ static JNINativeMethod methods[] = { {CC"getConstantPool0", CC"(Ljava/lang/Class;)J", (void*)&WB_GetConstantPool }, {CC"getConstantPoolCacheIndexTag0", CC"()I", (void*)&WB_GetConstantPoolCacheIndexTag}, {CC"getConstantPoolCacheLength0", CC"(Ljava/lang/Class;)I", (void*)&WB_GetConstantPoolCacheLength}, + {CC"getResolvedReferences0", CC"(Ljava/lang/Class;)[Ljava/lang/Object;", (void*)&WB_GetResolvedReferences}, {CC"remapInstructionOperandFromCPCache0", CC"(Ljava/lang/Class;I)I", (void*)&WB_ConstantPoolRemapInstructionOperandFromCache}, {CC"encodeConstantPoolIndyIndex0", @@ -2578,7 +2573,6 @@ static JNINativeMethod methods[] = { {CC"isShared", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsShared }, {CC"isSharedClass", CC"(Ljava/lang/Class;)Z", (void*)&WB_IsSharedClass }, {CC"areSharedStringsIgnored", CC"()Z", (void*)&WB_AreSharedStringsIgnored }, - {CC"getResolvedReferences", CC"(Ljava/lang/Class;)Ljava/lang/Object;", (void*)&WB_GetResolvedReferences}, {CC"linkClass", CC"(Ljava/lang/Class;)V", (void*)&WB_LinkClass}, {CC"areOpenArchiveHeapObjectsMapped", CC"()Z", (void*)&WB_AreOpenArchiveHeapObjectsMapped}, {CC"isCDSIncluded", CC"()Z", (void*)&WB_IsCDSIncluded }, diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 5683e4fe02527..c6acbbc39f57d 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -1996,6 +1996,14 @@ bool Arguments::check_vm_args_consistency() { } #endif +#if INCLUDE_JFR + if (status && (FlightRecorderOptions || StartFlightRecording)) { + if (!create_numbered_module_property("jdk.module.addmods", "jdk.jfr", addmods_count++)) { + return false; + } + } +#endif + #ifndef SUPPORT_RESERVED_STACK_AREA if (StackReservedPages != 0) { FLAG_SET_CMDLINE(StackReservedPages, 0); diff --git a/src/hotspot/share/runtime/atomic.hpp b/src/hotspot/share/runtime/atomic.hpp index df23967afc128..c4550ea2f1a83 100644 --- a/src/hotspot/share/runtime/atomic.hpp +++ b/src/hotspot/share/runtime/atomic.hpp @@ -26,18 +26,13 @@ #define SHARE_RUNTIME_ATOMIC_HPP #include "memory/allocation.hpp" -#include "metaprogramming/conditional.hpp" #include "metaprogramming/enableIf.hpp" -#include "metaprogramming/isIntegral.hpp" -#include "metaprogramming/isPointer.hpp" -#include "metaprogramming/isSame.hpp" #include "metaprogramming/primitiveConversions.hpp" -#include "metaprogramming/removeCV.hpp" -#include "metaprogramming/removePointer.hpp" #include "runtime/orderAccess.hpp" #include "utilities/align.hpp" #include "utilities/bytes.hpp" #include "utilities/macros.hpp" + #include enum atomic_memory_order { @@ -508,7 +503,7 @@ template struct Atomic::LoadImpl< T, PlatformOp, - typename EnableIf::value || IsPointer::value>::type> + typename EnableIf::value || std::is_pointer::value>::type> { T operator()(T const volatile* dest) const { // Forward to the platform handler for the size of T. @@ -560,7 +555,7 @@ template struct Atomic::StoreImpl< T, T, PlatformOp, - typename EnableIf::value>::type> + typename EnableIf::value>::type> { void operator()(T volatile* dest, T new_value) const { // Forward to the platform handler for the size of T. @@ -625,15 +620,15 @@ struct Atomic::PlatformStore { template inline void Atomic::inc(D volatile* dest, atomic_memory_order order) { - STATIC_ASSERT(IsPointer::value || IsIntegral::value); - typedef typename Conditional::value, ptrdiff_t, D>::type I; + STATIC_ASSERT(std::is_pointer::value || std::is_integral::value); + using I = std::conditional_t::value, ptrdiff_t, D>; Atomic::add(dest, I(1), order); } template inline void Atomic::dec(D volatile* dest, atomic_memory_order order) { - STATIC_ASSERT(IsPointer::value || IsIntegral::value); - typedef typename Conditional::value, ptrdiff_t, D>::type I; + STATIC_ASSERT(std::is_pointer::value || std::is_integral::value); + using I = std::conditional_t::value, ptrdiff_t, D>; // Assumes two's complement integer representation. #pragma warning(suppress: 4146) Atomic::add(dest, I(-1), order); @@ -641,14 +636,14 @@ inline void Atomic::dec(D volatile* dest, atomic_memory_order order) { template inline D Atomic::sub(D volatile* dest, I sub_value, atomic_memory_order order) { - STATIC_ASSERT(IsPointer::value || IsIntegral::value); - STATIC_ASSERT(IsIntegral::value); + STATIC_ASSERT(std::is_pointer::value || std::is_integral::value); + STATIC_ASSERT(std::is_integral::value); // If D is a pointer type, use [u]intptr_t as the addend type, // matching signedness of I. Otherwise, use D as the addend type. - typedef typename Conditional::value, intptr_t, uintptr_t>::type PI; - typedef typename Conditional::value, PI, D>::type AddendType; + using PI = std::conditional_t::value, intptr_t, uintptr_t>; + using AddendType = std::conditional_t::value, PI, D>; // Only allow conversions that can't change the value. - STATIC_ASSERT(IsSigned::value == IsSigned::value); + STATIC_ASSERT(std::is_signed::value == std::is_signed::value); STATIC_ASSERT(sizeof(I) <= sizeof(AddendType)); AddendType addend = sub_value; // Assumes two's complement integer representation. @@ -884,10 +879,10 @@ inline D Atomic::fetch_and_add(D volatile* dest, I add_value, template struct Atomic::AddImpl< D, I, - typename EnableIf::value && - IsIntegral::value && + typename EnableIf::value && + std::is_integral::value && (sizeof(I) <= sizeof(D)) && - (IsSigned::value == IsSigned::value)>::type> + (std::is_signed::value == std::is_signed::value)>::type> { static D add_and_fetch(D volatile* dest, I add_value, atomic_memory_order order) { D addend = add_value; @@ -902,14 +897,14 @@ struct Atomic::AddImpl< template struct Atomic::AddImpl< P*, I, - typename EnableIf::value && (sizeof(I) <= sizeof(P*))>::type> + typename EnableIf::value && (sizeof(I) <= sizeof(P*))>::type> { STATIC_ASSERT(sizeof(intptr_t) == sizeof(P*)); STATIC_ASSERT(sizeof(uintptr_t) == sizeof(P*)); // Type of the scaled addend. An integral type of the same size as a // pointer, and the same signedness as I. - using SI = typename Conditional::value, intptr_t, uintptr_t>::type; + using SI = std::conditional_t::value, intptr_t, uintptr_t>; // Type of the unscaled destination. A pointer type with pointee size == 1. using UP = const char*; @@ -977,7 +972,7 @@ inline bool Atomic::replace_if_null(D* volatile* dest, T* value, template struct Atomic::CmpxchgImpl< T, T, T, - typename EnableIf::value>::type> + typename EnableIf::value>::type> { T operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { @@ -1002,8 +997,8 @@ template struct Atomic::CmpxchgImpl< D*, U*, T*, typename EnableIf::value && - IsSame::type, - typename RemoveCV::type>::value>::type> + std::is_same, + std::remove_cv_t>::value>::type> { D* operator()(D* volatile* dest, U* compare_value, T* exchange_value, atomic_memory_order order) const { @@ -1112,7 +1107,7 @@ inline T Atomic::CmpxchgByteUsingInt::operator()(T volatile* dest, template struct Atomic::XchgImpl< T, T, - typename EnableIf::value>::type> + typename EnableIf::value>::type> { T operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { // Forward to the platform handler for the size of T. diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 46a90b678c3b8..a23905482bded 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -429,7 +429,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread #if COMPILER2_OR_JVMCI if ((jvmci_enabled COMPILER2_PRESENT( || ((DoEscapeAnalysis || EliminateNestedLocks) && EliminateLocks) )) && !EscapeBarrier::objs_are_deoptimized(current, deoptee.id())) { - bool unused; + bool unused = false; restore_eliminated_locks(current, chunk, realloc_failures, deoptee, exec_mode, unused); } #endif // COMPILER2_OR_JVMCI @@ -1612,7 +1612,7 @@ void Deoptimization::pop_frames_failed_reallocs(JavaThread* thread, vframeArray* ObjectSynchronizer::exit(src->obj(), src->lock(), thread); } } - array->element(i)->free_monitors(thread); + array->element(i)->free_monitors(); #ifdef ASSERT array->element(i)->set_removed_monitors(); #endif @@ -2657,7 +2657,7 @@ const char* Deoptimization::trap_reason_name(int reason) { if ((uint)reason < Reason_LIMIT) return _trap_reason_name[reason]; static char buf[20]; - sprintf(buf, "reason%d", reason); + os::snprintf_checked(buf, sizeof(buf), "reason%d", reason); return buf; } const char* Deoptimization::trap_action_name(int action) { @@ -2667,7 +2667,7 @@ const char* Deoptimization::trap_action_name(int action) { if ((uint)action < Action_LIMIT) return _trap_action_name[action]; static char buf[20]; - sprintf(buf, "action%d", action); + os::snprintf_checked(buf, sizeof(buf), "action%d", action); return buf; } @@ -2766,7 +2766,7 @@ void Deoptimization::print_statistics() { Bytecodes::Code bc = (Bytecodes::Code)(counter & LSB_MASK); if (bc_case == BC_CASE_LIMIT && (int)bc == 0) bc = Bytecodes::_illegal; - sprintf(name, "%s/%s/%s", + os::snprintf_checked(name, sizeof(name), "%s/%s/%s", trap_reason_name(reason), trap_action_name(action), Bytecodes::is_defined(bc)? Bytecodes::name(bc): "other"); diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 10909a253566f..aac0dc88482bf 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -477,6 +477,9 @@ const intx ObjectAlignmentInBytes = 8; develop(bool, ZapFillerObjects, trueInDebug, \ "Zap filler objects") \ \ + develop(bool, ZapTLAB, trueInDebug, \ + "Zap allocated TLABs") \ + \ product(bool, ExecutingUnitTests, false, \ "Whether the JVM is running unit tests or not") \ \ diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp index 6801e45e94e77..da28009c1b443 100644 --- a/src/hotspot/share/runtime/handshake.cpp +++ b/src/hotspot/share/runtime/handshake.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -245,6 +245,10 @@ class VM_HandshakeAllThreads: public VM_Operation { number_of_threads_issued++; } + // Separate the arming of the poll in add_operation() above from + // the read of JavaThread state in the try_process() call below. + OrderAccess::fence(); + if (number_of_threads_issued < 1) { log_handshake_info(start_time_ns, _op->name(), 0, 0, "no threads alive"); return; @@ -357,6 +361,10 @@ void Handshake::execute(HandshakeClosure* hs_cl, JavaThread* target) { return; } + // Separate the arming of the poll in add_operation() above from + // the read of JavaThread state in the try_process() call below. + OrderAccess::fence(); + // Keeps count on how many of own emitted handshakes // this thread execute. int emitted_handshakes_executed = 0; @@ -481,6 +489,10 @@ bool HandshakeState::process_by_self(bool allow_suspend) { // It by-passes the NSV by manually doing the transition. NoSafepointVerifier nsv; + // Separate all the writes above for other threads reading state + // set by this thread in case the operation is ThreadSuspendHandshake. + OrderAccess::fence(); + while (has_operation()) { MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index 5dc47c7c245ce..72b5829c41efd 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -77,6 +77,7 @@ #include "runtime/vm_version.hpp" #include "services/memTracker.hpp" #include "utilities/dtrace.hpp" +#include "utilities/events.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "utilities/vmError.hpp" @@ -410,6 +411,8 @@ void before_exit(JavaThread* thread, bool halt) { #define BEFORE_EXIT_DONE 2 static jint volatile _before_exit_status = BEFORE_EXIT_NOT_RUN; + Events::log(thread, "Before exit entered"); + // Note: don't use a Mutex to guard the entire before_exit(), as // JVMTI post_thread_end_event and post_vm_death_event will run native code. // A CAS or OSMutex would work just fine but then we need to manipulate diff --git a/src/hotspot/share/runtime/monitorChunk.cpp b/src/hotspot/share/runtime/monitorChunk.cpp index 32c5069087ef5..d18fc21d78d93 100644 --- a/src/hotspot/share/runtime/monitorChunk.cpp +++ b/src/hotspot/share/runtime/monitorChunk.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ MonitorChunk::MonitorChunk(int number_on_monitors) { _number_of_monitors = number_on_monitors; _monitors = NEW_C_HEAP_ARRAY(BasicObjectLock, number_on_monitors, mtSynchronizer); - _next = NULL; } diff --git a/src/hotspot/share/runtime/monitorChunk.hpp b/src/hotspot/share/runtime/monitorChunk.hpp index 0c2f2dfae023f..ce459fd838ac4 100644 --- a/src/hotspot/share/runtime/monitorChunk.hpp +++ b/src/hotspot/share/runtime/monitorChunk.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,26 +35,17 @@ class MonitorChunk: public CHeapObj { int _number_of_monitors; BasicObjectLock* _monitors; BasicObjectLock* monitors() const { return _monitors; } - MonitorChunk* _next; public: // Constructor MonitorChunk(int number_on_monitors); ~MonitorChunk(); - // link operations - MonitorChunk* next() const { return _next; } - void set_next(MonitorChunk* next) { _next = next; } - - // Tells whether the monitor chunk is linked into the JavaThread - bool is_linked() const { return next() != NULL; } - // Returns the number of monitors int number_of_monitors() const { return _number_of_monitors; } // Returns the index'th monitor BasicObjectLock* at(int index) { assert(index >= 0 && index < number_of_monitors(), "out of bounds check"); return &monitors()[index]; } - // Memory management void oops_do(OopClosure* f); diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 3b80b7f34deeb..c846408fb1b1b 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -94,6 +94,16 @@ int os::snprintf(char* buf, size_t len, const char* fmt, ...) { return result; } +int os::snprintf_checked(char* buf, size_t len, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int result = os::vsnprintf(buf, len, fmt, args); + va_end(args); + assert(result >= 0, "os::snprintf error"); + assert(static_cast(result) < len, "os::snprintf truncated"); + return result; +} + // Fill in buffer with current local time as an ISO-8601 string. // E.g., YYYY-MM-DDThh:mm:ss.mmm+zzzz. // Returns buffer, or NULL if it failed. @@ -975,7 +985,9 @@ void os::print_environment_variables(outputStream* st, const char** env_list) { if (envvar != NULL) { st->print("%s", env_list[i]); st->print("="); - st->print_cr("%s", envvar); + st->print("%s", envvar); + // Use separate cr() printing to avoid unnecessary buffer operations that might cause truncation. + st->cr(); } } } @@ -1294,7 +1306,7 @@ char* os::format_boot_path(const char* format_string, FILE* os::fopen(const char* path, const char* mode) { char modified_mode[20]; assert(strlen(mode) + 1 < sizeof(modified_mode), "mode chars plus one extra must fit in buffer"); - sprintf(modified_mode, "%s" LINUX_ONLY("e") BSD_ONLY("e") WINDOWS_ONLY("N"), mode); + os::snprintf_checked(modified_mode, sizeof(modified_mode), "%s" LINUX_ONLY("e") BSD_ONLY("e") WINDOWS_ONLY("N"), mode); FILE* file = ::fopen(path, modified_mode); #if !(defined LINUX || defined BSD || defined _WINDOWS) @@ -1348,6 +1360,30 @@ bool os::set_boot_path(char fileSep, char pathSep) { return false; } +bool os::file_exists(const char* filename) { + struct stat statbuf; + if (filename == NULL || strlen(filename) == 0) { + return false; + } + return os::stat(filename, &statbuf) == 0; +} + +bool os::write(int fd, const void *buf, size_t nBytes) { + ssize_t res; + + while (nBytes > 0) { + res = pd_write(fd, buf, nBytes); + if (res == OS_ERR) { + return false; + } + buf = (void *)((char *)buf + res); + nBytes -= res; + } + + return true; +} + + // Splits a path, based on its separator, the number of // elements is returned back in "elements". // file_name_length is used as a modifier for each path's diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 81713b0f6443e..582ade5fbfe88 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,6 @@ #define SHARE_RUNTIME_OS_HPP #include "jvm_md.h" -#include "metaprogramming/integralConstant.hpp" #include "utilities/exceptions.hpp" #include "utilities/ostream.hpp" #include "utilities/macros.hpp" @@ -169,6 +168,8 @@ class os: AllStatic { // Get summary strings for system information in buffer provided static void get_summary_cpu_info(char* buf, size_t buflen); static void get_summary_os_info(char* buf, size_t buflen); + // Returns number of bytes written on success, OS_ERR on failure. + static ssize_t pd_write(int fd, const void *buf, size_t nBytes); static void initialize_initial_active_processor_count(); @@ -560,6 +561,7 @@ class os: AllStatic { static FILE* fopen(const char* path, const char* mode); static int close(int fd); static jlong lseek(int fd, jlong offset, int whence); + static bool file_exists(const char* file); // This function, on Windows, canonicalizes a given path (see os_windows.cpp for details). // On Posix, this function is a noop: it does not change anything and just returns // the input pointer. @@ -579,7 +581,8 @@ class os: AllStatic { static ssize_t read(int fd, void *buf, unsigned int nBytes); static ssize_t read_at(int fd, void *buf, unsigned int nBytes, jlong offset); - static size_t write(int fd, const void *buf, unsigned int nBytes); + // Writes the bytes completely. Returns true on success, false otherwise. + static bool write(int fd, const void *buf, size_t nBytes); // Reading directories. static DIR* opendir(const char* dirname); @@ -679,6 +682,10 @@ class os: AllStatic { static int vsnprintf(char* buf, size_t len, const char* fmt, va_list args) ATTRIBUTE_PRINTF(3, 0); static int snprintf(char* buf, size_t len, const char* fmt, ...) ATTRIBUTE_PRINTF(3, 4); + // Performs snprintf and asserts the result is non-negative (so there was not + // an encoding error) and that the output was not truncated. + static int snprintf_checked(char* buf, size_t len, const char* fmt, ...) ATTRIBUTE_PRINTF(3, 4); + // Get host name in buffer provided static bool get_host_name(char* buf, size_t buflen); @@ -807,7 +814,6 @@ class os: AllStatic { static int send(int fd, char* buf, size_t nBytes, uint flags); static int raw_send(int fd, char* buf, size_t nBytes, uint flags); static int connect(int fd, struct sockaddr* him, socklen_t len); - static struct hostent* get_host_by_name(char* name); // Support for signals (see JVM_RaiseSignal, JVM_RegisterSignal) static void initialize_jdk_signal_support(TRAPS); diff --git a/src/hotspot/share/runtime/perfData.cpp b/src/hotspot/share/runtime/perfData.cpp index 9ce8f3fff93e1..741e7e73e9c59 100644 --- a/src/hotspot/share/runtime/perfData.cpp +++ b/src/hotspot/share/runtime/perfData.cpp @@ -84,7 +84,8 @@ PerfData::PerfData(CounterNS ns, const char* name, Units u, Variability v) const char* prefix = PerfDataManager::ns_to_string(ns); - _name = NEW_C_HEAP_ARRAY(char, strlen(name) + strlen(prefix) + 2, mtInternal); + const size_t _name_size = strlen(name) + strlen(prefix) + 2; + _name = NEW_C_HEAP_ARRAY(char, _name_size, mtInternal); assert(strlen(name) != 0, "invalid name"); if (ns == NULL_NS) { @@ -100,7 +101,7 @@ PerfData::PerfData(CounterNS ns, const char* name, Units u, Variability v) } } else { - sprintf(_name, "%s.%s", prefix, name); + os::snprintf_checked(_name, _name_size, "%s.%s", prefix, name); // set the F_Supported flag based on the given namespace. if (PerfDataManager::is_stable_supported(ns) || PerfDataManager::is_unstable_supported(ns)) { @@ -363,7 +364,7 @@ char* PerfDataManager::counter_name(const char* ns, const char* name) { size_t len = strlen(ns) + strlen(name) + 2; char* result = NEW_RESOURCE_ARRAY(char, len); - sprintf(result, "%s.%s", ns, name); + os::snprintf_checked(result, len, "%s.%s", ns, name); return result; } diff --git a/src/hotspot/share/runtime/reflection.cpp b/src/hotspot/share/runtime/reflection.cpp index a7e232124b17d..56b3d86911df1 100644 --- a/src/hotspot/share/runtime/reflection.cpp +++ b/src/hotspot/share/runtime/reflection.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -728,8 +728,15 @@ static objArrayHandle get_parameter_types(const methodHandle& method, int parameter_count, oop* return_type, TRAPS) { - // Allocate array holding parameter types (java.lang.Class instances) - objArrayOop m = oopFactory::new_objArray(vmClasses::Class_klass(), parameter_count, CHECK_(objArrayHandle())); + objArrayOop m; + if (parameter_count == 0) { + // Avoid allocating an array for the empty case + // Still need to parse the signature for the return type below + m = Universe::the_empty_class_array(); + } else { + // Allocate array holding parameter types (java.lang.Class instances) + m = oopFactory::new_objArray(vmClasses::Class_klass(), parameter_count, CHECK_(objArrayHandle())); + } objArrayHandle mirrors(THREAD, m); int index = 0; // Collect parameter types diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index 6f6dba9af51c7..8675850a584bc 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -961,7 +961,8 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) { } // Fall thru so we only have one place that installs the hash in // the ObjectMonitor. - } else if (current->is_lock_owned((address)mark.locker())) { + } else if (current->is_Java_thread() + && current->as_Java_thread()->is_lock_owned((address)mark.locker())) { // This is a stack lock owned by the calling thread so fetch the // displaced markWord from the BasicLock on the stack. temp = mark.displaced_mark_helper(); diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index ece43e350a4b8..3905336d40f57 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -699,15 +699,6 @@ void Thread::print_owned_locks_on(outputStream* st) const { } #endif // ASSERT -// We had to move these methods here, because vm threads get into ObjectSynchronizer::enter -// However, there is a note in JavaThread::is_lock_owned() about the VM threads not being -// used for compilation in the future. If that change is made, the need for these methods -// should be revisited, and they should be removed if possible. - -bool Thread::is_lock_owned(address adr) const { - return is_in_full_stack(adr); -} - bool Thread::set_as_starting_thread() { assert(_starting_thread == NULL, "already initialized: " "_starting_thread=" INTPTR_FORMAT, p2i(_starting_thread)); @@ -1035,8 +1026,6 @@ JavaThread::JavaThread() : _current_waiting_monitor(NULL), _Stalled(0), - _monitor_chunks(nullptr), - _suspend_flags(0), _async_exception_condition(_no_async_condition), _pending_async_exception(nullptr), @@ -1573,13 +1562,7 @@ JavaThread* JavaThread::active() { } bool JavaThread::is_lock_owned(address adr) const { - if (Thread::is_lock_owned(adr)) return true; - - for (MonitorChunk* chunk = monitor_chunks(); chunk != NULL; chunk = chunk->next()) { - if (chunk->contains(adr)) return true; - } - - return false; + return is_in_full_stack(adr); } oop JavaThread::exception_oop() const { @@ -1590,23 +1573,6 @@ void JavaThread::set_exception_oop(oop o) { Atomic::store(&_exception_oop, o); } -void JavaThread::add_monitor_chunk(MonitorChunk* chunk) { - chunk->set_next(monitor_chunks()); - set_monitor_chunks(chunk); -} - -void JavaThread::remove_monitor_chunk(MonitorChunk* chunk) { - guarantee(monitor_chunks() != NULL, "must be non empty"); - if (monitor_chunks() == chunk) { - set_monitor_chunks(chunk->next()); - } else { - MonitorChunk* prev = monitor_chunks(); - while (prev->next() != chunk) prev = prev->next(); - prev->set_next(chunk->next()); - } -} - - // Asynchronous exceptions support // // Note: this function shouldn't block if it's called in @@ -1994,13 +1960,6 @@ void JavaThread::oops_do_no_frames(OopClosure* f, CodeBlobClosure* cf) { DEBUG_ONLY(verify_frame_info();) - if (has_last_Java_frame()) { - // Traverse the monitor chunks - for (MonitorChunk* chunk = monitor_chunks(); chunk != NULL; chunk = chunk->next()) { - chunk->oops_do(f); - } - } - assert(vframe_array_head() == NULL, "deopt in progress at a safepoint!"); // If we have deferred set_locals there might be oops waiting to be // written @@ -3399,10 +3358,10 @@ void JavaThread::invoke_shutdown_hooks() { // Threads::destroy_vm() is normally called from jni_DestroyJavaVM() when // the program falls off the end of main(). Another VM exit path is through -// vm_exit() when the program calls System.exit() to return a value or when -// there is a serious error in VM. The two shutdown paths are not exactly -// the same, but they share Shutdown.shutdown() at Java level and before_exit() -// and VM_Exit op at VM level. +// vm_exit(), when the program calls System.exit() to return a value, or when +// there is a serious error in VM. +// These two separate shutdown paths are not exactly the same, but they share +// Shutdown.shutdown() at Java level and before_exit() and VM_Exit op at VM level. // // Shutdown sequence: // + Shutdown native memory tracking if it is on @@ -3577,7 +3536,7 @@ void Threads::add(JavaThread* p, bool force_daemon) { ObjectSynchronizer::inc_in_use_list_ceiling(); // Possible GC point. - Events::log(p, "Thread added: " INTPTR_FORMAT, p2i(p)); + Events::log(Thread::current(), "Thread added: " INTPTR_FORMAT, p2i(p)); // Make new thread known to active EscapeBarrier EscapeBarrier::thread_added(p); @@ -3625,7 +3584,7 @@ void Threads::remove(JavaThread* p, bool is_daemon) { ObjectSynchronizer::dec_in_use_list_ceiling(); // Since Events::log uses a lock, we grab it outside the Threads_lock - Events::log(p, "Thread exited: " INTPTR_FORMAT, p2i(p)); + Events::log(Thread::current(), "Thread exited: " INTPTR_FORMAT, p2i(p)); } // Operations on the Threads list for GC. These are not explicitly locked, @@ -3828,14 +3787,7 @@ void Threads::print_on(outputStream* st, bool print_stacks, } PrintOnClosure cl(st); - cl.do_thread(VMThread::vm_thread()); - Universe::heap()->gc_threads_do(&cl); - if (StringDedup::is_enabled()) { - StringDedup::threads_do(&cl); - } - cl.do_thread(WatcherThread::watcher_thread()); - cl.do_thread(AsyncLogWriter::instance()); - + non_java_threads_do(&cl); st->flush(); } diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 825b04cc272ca..9c949cb3b4157 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -497,9 +497,6 @@ class Thread: public ThreadShadow { } public: - // Used by fast lock support - virtual bool is_lock_owned(address adr) const; - // Check if address is within the given range of this thread's // stack: stack_base() > adr >= limit bool is_in_stack_range_incl(address adr, address limit) const { @@ -668,15 +665,17 @@ class Thread: public ThreadShadow { class ThreadInAsgct { private: Thread* _thread; + bool _saved_in_asgct; public: ThreadInAsgct(Thread* thread) : _thread(thread) { assert(thread != nullptr, "invariant"); - assert(!thread->in_asgct(), "invariant"); + // Allow AsyncGetCallTrace to be reentrant - save the previous state. + _saved_in_asgct = thread->in_asgct(); thread->set_in_asgct(true); } ~ThreadInAsgct() { assert(_thread->in_asgct(), "invariant"); - _thread->set_in_asgct(false); + _thread->set_in_asgct(_saved_in_asgct); } }; @@ -801,10 +800,6 @@ class JavaThread: public Thread { } private: - MonitorChunk* _monitor_chunks; // Contains the off stack monitors - // allocated during deoptimization - // and by JNI_MonitorEnter/Exit - enum SuspendFlags { // NOTE: avoid using the sign-bit as cc generates different test code // when the sign-bit is used, and sometimes incorrectly - see CR 6398077 @@ -1209,7 +1204,7 @@ class JavaThread: public Thread { (_suspend_flags & (_obj_deopt JFR_ONLY(| _trace_flag))) != 0; } - // Fast-locking support + // Stack-locking support bool is_lock_owned(address adr) const; // Accessors for vframe array top @@ -1386,13 +1381,7 @@ class JavaThread: public Thread { int depth_first_number() { return _depth_first_number; } void set_depth_first_number(int dfn) { _depth_first_number = dfn; } - private: - void set_monitor_chunks(MonitorChunk* monitor_chunks) { _monitor_chunks = monitor_chunks; } - public: - MonitorChunk* monitor_chunks() const { return _monitor_chunks; } - void add_monitor_chunk(MonitorChunk* chunk); - void remove_monitor_chunk(MonitorChunk* chunk); bool in_deopt_handler() const { return _in_deopt_handler > 0; } void inc_in_deopt_handler() { _in_deopt_handler++; } void dec_in_deopt_handler() { diff --git a/src/hotspot/share/runtime/vframeArray.cpp b/src/hotspot/share/runtime/vframeArray.cpp index 75c091841a8f9..afb789764eeb9 100644 --- a/src/hotspot/share/runtime/vframeArray.cpp +++ b/src/hotspot/share/runtime/vframeArray.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/monitorChunk.hpp" #include "runtime/sharedRuntime.hpp" +#include "runtime/synchronizer.hpp" #include "runtime/vframe.hpp" #include "runtime/vframeArray.hpp" #include "runtime/vframe_hp.hpp" @@ -48,11 +49,10 @@ int vframeArrayElement:: bci(void) const { return (_bci == SynchronizationEntryBCI ? 0 : _bci); } -void vframeArrayElement::free_monitors(JavaThread* jt) { +void vframeArrayElement::free_monitors() { if (_monitors != NULL) { MonitorChunk* chunk = _monitors; _monitors = NULL; - jt->remove_monitor_chunk(chunk); delete chunk; } } @@ -72,7 +72,7 @@ void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { int index; { - Thread* current_thread = Thread::current(); + JavaThread* current_thread = JavaThread::current(); ResourceMark rm(current_thread); HandleMark hm(current_thread); @@ -85,7 +85,6 @@ void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { // Allocate monitor chunk _monitors = new MonitorChunk(list->length()); - vf->thread()->add_monitor_chunk(_monitors); // Migrate the BasicLocks from the stack to the monitor chunk for (index = 0; index < list->length(); index++) { @@ -95,9 +94,16 @@ void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { if (monitor->owner_is_scalar_replaced()) { dest->set_obj(NULL); } else { - assert(monitor->owner() == NULL || (!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern()), "object must be null or locked, and unbiased"); + assert(monitor->owner() != nullptr, "monitor owner must not be null"); + assert(!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern(), "object must be locked, and unbiased"); dest->set_obj(monitor->owner()); + assert(ObjectSynchronizer::current_thread_holds_lock(current_thread, Handle(current_thread, dest->obj())), + "should be held, before move_to"); + monitor->lock()->move_to(monitor->owner(), dest->lock()); + + assert(ObjectSynchronizer::current_thread_holds_lock(current_thread, Handle(current_thread, dest->obj())), + "should be held, after move_to"); } } } @@ -308,7 +314,11 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, top = iframe()->previous_monitor_in_interpreter_frame(top); BasicObjectLock* src = _monitors->at(index); top->set_obj(src->obj()); + assert(src->obj() != nullptr || ObjectSynchronizer::current_thread_holds_lock(thread, Handle(thread, src->obj())), + "should be held, before move_to"); src->lock()->move_to(src->obj(), top->lock()); + assert(src->obj() != nullptr || ObjectSynchronizer::current_thread_holds_lock(thread, Handle(thread, src->obj())), + "should be held, after move_to"); } if (ProfileInterpreter) { iframe()->interpreter_frame_set_mdp(0); // clear out the mdp. @@ -615,9 +625,8 @@ void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode, int caller } void vframeArray::deallocate_monitor_chunks() { - JavaThread* jt = JavaThread::current(); for (int index = 0; index < frames(); index++ ) { - element(index)->free_monitors(jt); + element(index)->free_monitors(); } } diff --git a/src/hotspot/share/runtime/vframeArray.hpp b/src/hotspot/share/runtime/vframeArray.hpp index fa2ab054186b3..bb41edbd890f7 100644 --- a/src/hotspot/share/runtime/vframeArray.hpp +++ b/src/hotspot/share/runtime/vframeArray.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -77,7 +77,7 @@ class vframeArrayElement { MonitorChunk* monitors(void) const { return _monitors; } - void free_monitors(JavaThread* jt); + void free_monitors(); StackValueCollection* locals(void) const { return _locals; } diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 7bf40685cf63e..33de84a68c155 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -2557,7 +2557,7 @@ typedef HashtableEntry KlassHashtableEntry; /* Calling convention constants */ \ /********************************/ \ \ - declare_constant(RegisterImpl::number_of_registers) \ + NOT_S390(NOT_PPC64(declare_constant(RegisterImpl::number_of_registers)))\ declare_constant(ConcreteRegisterImpl::number_of_registers) \ declare_preprocessor_constant("REG_COUNT", REG_COUNT) \ declare_c2_preprocessor_constant("SAVED_ON_ENTRY_REG_COUNT", SAVED_ON_ENTRY_REG_COUNT) \ diff --git a/src/hotspot/share/services/classLoadingService.cpp b/src/hotspot/share/services/classLoadingService.cpp index 9da4496971c7e..524851dd6e15a 100644 --- a/src/hotspot/share/services/classLoadingService.cpp +++ b/src/hotspot/share/services/classLoadingService.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,7 @@ #include "utilities/defaultStream.hpp" #include "logging/log.hpp" #include "logging/logConfiguration.hpp" +#include "logging/logFileStreamOutput.hpp" #ifdef DTRACE_ENABLED @@ -186,6 +187,22 @@ bool ClassLoadingService::set_verbose(bool verbose) { return verbose; } +bool ClassLoadingService::get_verbose() { + for (LogTagSet* ts = LogTagSet::first(); ts != nullptr; ts = ts->next()) { + // set_verbose looks for a non-exact match for class+load, + // so look for all tag sets that match class+load* + if (ts->contains(LogTag::_class) && + ts->contains(LogTag::_load)) { + LogLevelType l = ts->level_for(&StdoutLog); + if (l != LogLevel::Info && l != LogLevel::Debug && l != LogLevel::Trace) { + return false; + } + } + } + + return true; +} + // Caller to this function must own Management_lock void ClassLoadingService::reset_trace_class_unloading() { assert(Management_lock->owned_by_self(), "Must own the Management_lock"); diff --git a/src/hotspot/share/services/classLoadingService.hpp b/src/hotspot/share/services/classLoadingService.hpp index db422cbf11ff6..a2137ccd6ab12 100644 --- a/src/hotspot/share/services/classLoadingService.hpp +++ b/src/hotspot/share/services/classLoadingService.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,8 +55,8 @@ class ClassLoadingService : public AllStatic { public: static void init(); - static bool get_verbose() { return log_is_enabled(Info, class, load); } static bool set_verbose(bool verbose); + static bool get_verbose() NOT_MANAGEMENT_RETURN_(false); static void reset_trace_class_unloading() NOT_MANAGEMENT_RETURN; static jlong loaded_class_count() { diff --git a/src/hotspot/share/services/heapDumperCompression.cpp b/src/hotspot/share/services/heapDumperCompression.cpp index 222c6e383f2ff..92a11c7b940ec 100644 --- a/src/hotspot/share/services/heapDumperCompression.cpp +++ b/src/hotspot/share/services/heapDumperCompression.cpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -54,14 +55,8 @@ char const* FileWriter::write_buf(char* buf, ssize_t size) { assert(_fd >= 0, "Must be open"); assert(size > 0, "Must write at least one byte"); - while (size > 0) { - ssize_t n = os::write(_fd, buf, (uint) size); - if (n <= 0) { - return os::strerror(errno); - } - - buf += n; - size -= n; + if (!os::write(_fd, buf, (size_t)size)) { + return os::strerror(errno); } return NULL; diff --git a/src/hotspot/share/services/mallocTracker.hpp b/src/hotspot/share/services/mallocTracker.hpp index abeab0944f048..82f396beb3e53 100644 --- a/src/hotspot/share/services/mallocTracker.hpp +++ b/src/hotspot/share/services/mallocTracker.hpp @@ -166,11 +166,6 @@ class MallocMemorySnapshot : public ResourceObj { // Total malloc'd memory used by arenas size_t total_arena() const; - inline size_t thread_count() const { - MallocMemorySnapshot* s = const_cast(this); - return s->by_type(mtThreadStack)->malloc_count(); - } - void copy_to(MallocMemorySnapshot* s) { // Need to make sure that mtChunks don't get deallocated while the // copy is going on, because their size is adjusted using this diff --git a/src/hotspot/share/services/memBaseline.cpp b/src/hotspot/share/services/memBaseline.cpp index 7a04ee2a6a482..58da3f831a55b 100644 --- a/src/hotspot/share/services/memBaseline.cpp +++ b/src/hotspot/share/services/memBaseline.cpp @@ -145,11 +145,10 @@ class VirtualMemoryAllocationWalker : public VirtualMemoryWalker { }; -bool MemBaseline::baseline_summary() { +void MemBaseline::baseline_summary() { MallocMemorySummary::snapshot(&_malloc_memory_snapshot); VirtualMemorySummary::snapshot(&_virtual_memory_snapshot); _metaspace_stats = MetaspaceUtils::get_combined_statistics(); - return true; } bool MemBaseline::baseline_allocation_sites() { @@ -186,15 +185,13 @@ bool MemBaseline::baseline_allocation_sites() { return true; } -bool MemBaseline::baseline(bool summaryOnly) { +void MemBaseline::baseline(bool summaryOnly) { reset(); _instance_class_count = ClassLoaderDataGraph::num_instance_classes(); _array_class_count = ClassLoaderDataGraph::num_array_classes(); - - if (!baseline_summary()) { - return false; - } + _thread_count = ThreadStackTracker::thread_count(); + baseline_summary(); _baseline_type = Summary_baselined; @@ -205,7 +202,6 @@ bool MemBaseline::baseline(bool summaryOnly) { _baseline_type = Detail_baselined; } - return true; } int compare_allocation_site(const VirtualMemoryAllocationSite& s1, diff --git a/src/hotspot/share/services/memBaseline.hpp b/src/hotspot/share/services/memBaseline.hpp index 45b3774f27d3e..136baf7f9eb97 100644 --- a/src/hotspot/share/services/memBaseline.hpp +++ b/src/hotspot/share/services/memBaseline.hpp @@ -66,6 +66,7 @@ class MemBaseline { size_t _instance_class_count; size_t _array_class_count; + size_t _thread_count; // Allocation sites information // Malloc allocation sites @@ -86,11 +87,11 @@ class MemBaseline { public: // create a memory baseline MemBaseline(): - _instance_class_count(0), _array_class_count(0), + _instance_class_count(0), _array_class_count(0), _thread_count(0), _baseline_type(Not_baselined) { } - bool baseline(bool summaryOnly = true); + void baseline(bool summaryOnly = true); BaselineType baseline_type() const { return _baseline_type; } @@ -173,7 +174,7 @@ class MemBaseline { size_t thread_count() const { assert(baseline_type() != Not_baselined, "Not yet baselined"); - return _malloc_memory_snapshot.thread_count(); + return _thread_count; } // reset the baseline for reuse @@ -182,6 +183,7 @@ class MemBaseline { // _malloc_memory_snapshot and _virtual_memory_snapshot are copied over. _instance_class_count = 0; _array_class_count = 0; + _thread_count = 0; _malloc_sites.clear(); _virtual_memory_sites.clear(); @@ -190,7 +192,7 @@ class MemBaseline { private: // Baseline summary information - bool baseline_summary(); + void baseline_summary(); // Baseline allocation sites (detail tracking only) bool baseline_allocation_sites(); diff --git a/src/hotspot/share/services/memReporter.cpp b/src/hotspot/share/services/memReporter.cpp index 50ffd1feaddb4..af342493217f8 100644 --- a/src/hotspot/share/services/memReporter.cpp +++ b/src/hotspot/share/services/memReporter.cpp @@ -225,7 +225,6 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag, MallocMemory* thread_stack_memory = _malloc_snapshot->by_type(mtThreadStack); const char* scale = current_scale(); // report thread count - assert(ThreadStackTracker::thread_count() == 0, "Not used"); out->print_cr("%27s (thread #" SIZE_FORMAT ")", " ", thread_stack_memory->malloc_count()); out->print("%27s (Stack: " SIZE_FORMAT "%s", " ", amount_in_current_scale(thread_stack_memory->malloc_size()), scale); diff --git a/src/hotspot/share/services/memTracker.cpp b/src/hotspot/share/services/memTracker.cpp index 343703213ad6a..28aaa63d565fd 100644 --- a/src/hotspot/share/services/memTracker.cpp +++ b/src/hotspot/share/services/memTracker.cpp @@ -129,18 +129,17 @@ void MemTracker::final_report(outputStream* output) { void MemTracker::report(bool summary_only, outputStream* output, size_t scale) { assert(output != NULL, "No output stream"); MemBaseline baseline; - if (baseline.baseline(summary_only)) { - if (summary_only) { - MemSummaryReporter rpt(baseline, output, scale); - rpt.report(); - } else { - MemDetailReporter rpt(baseline, output, scale); - rpt.report(); - output->print("Metaspace:"); - // The basic metaspace report avoids any locking and should be safe to - // be called at any time. - MetaspaceUtils::print_basic_report(output, scale); - } + baseline.baseline(summary_only); + if (summary_only) { + MemSummaryReporter rpt(baseline, output, scale); + rpt.report(); + } else { + MemDetailReporter rpt(baseline, output, scale); + rpt.report(); + output->print("Metaspace:"); + // The basic metaspace report avoids any locking and should be safe to + // be called at any time. + MetaspaceUtils::print_basic_report(output, scale); } } diff --git a/src/hotspot/share/services/memoryService.cpp b/src/hotspot/share/services/memoryService.cpp index b9edf887b8373..b3d57ea25de7a 100644 --- a/src/hotspot/share/services/memoryService.cpp +++ b/src/hotspot/share/services/memoryService.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #include "classfile/vmSymbols.hpp" #include "gc/shared/collectedHeap.hpp" #include "logging/logConfiguration.hpp" +#include "logging/logFileStreamOutput.hpp" #include "memory/heap.hpp" #include "memory/memRegion.hpp" #include "memory/resourceArea.hpp" @@ -201,6 +202,21 @@ bool MemoryService::set_verbose(bool verbose) { return verbose; } +bool MemoryService::get_verbose() { + for (LogTagSet* ts = LogTagSet::first(); ts != nullptr; ts = ts->next()) { + // set_verbose only sets gc and not gc*, so check for an exact match + const bool is_gc_exact_match = ts->contains(LogTag::_gc) && ts->ntags() == 1; + if (is_gc_exact_match) { + LogLevelType l = ts->level_for(&StdoutLog); + if (l == LogLevel::Info || l == LogLevel::Debug || l == LogLevel::Trace) { + return true; + } + } + } + + return false; +} + Handle MemoryService::create_MemoryUsage_obj(MemoryUsage usage, TRAPS) { InstanceKlass* ik = Management::java_lang_management_MemoryUsage_klass(CHECK_NH); diff --git a/src/hotspot/share/services/memoryService.hpp b/src/hotspot/share/services/memoryService.hpp index a00e49dd0a772..78214548e9195 100644 --- a/src/hotspot/share/services/memoryService.hpp +++ b/src/hotspot/share/services/memoryService.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -106,8 +106,8 @@ class MemoryService : public AllStatic { GCCause::Cause cause, bool allMemoryPoolsAffected, const char* notificationMessage = nullptr); - static bool get_verbose() { return log_is_enabled(Info, gc); } static bool set_verbose(bool verbose); + static bool get_verbose(); // Create an instance of java/lang/management/MemoryUsage static Handle create_MemoryUsage_obj(MemoryUsage usage, TRAPS); diff --git a/src/hotspot/share/services/nmtDCmd.cpp b/src/hotspot/share/services/nmtDCmd.cpp index 6c87a664f7952..e78f1b0605eeb 100644 --- a/src/hotspot/share/services/nmtDCmd.cpp +++ b/src/hotspot/share/services/nmtDCmd.cpp @@ -121,11 +121,8 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) { report(false, scale_unit); } else if (_baseline.value()) { MemBaseline& baseline = MemTracker::get_baseline(); - if (!baseline.baseline(MemTracker::tracking_level() != NMT_detail)) { - output()->print_cr("Baseline failed"); - } else { - output()->print_cr("Baseline succeeded"); - } + baseline.baseline(MemTracker::tracking_level() != NMT_detail); + output()->print_cr("Baseline taken"); } else if (_summary_diff.value()) { MemBaseline& baseline = MemTracker::get_baseline(); if (baseline.baseline_type() >= MemBaseline::Summary_baselined) { @@ -157,14 +154,13 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) { void NMTDCmd::report(bool summaryOnly, size_t scale_unit) { MemBaseline baseline; - if (baseline.baseline(summaryOnly)) { - if (summaryOnly) { - MemSummaryReporter rpt(baseline, output(), scale_unit); - rpt.report(); - } else { - MemDetailReporter rpt(baseline, output(), scale_unit); - rpt.report(); - } + baseline.baseline(summaryOnly); + if (summaryOnly) { + MemSummaryReporter rpt(baseline, output(), scale_unit); + rpt.report(); + } else { + MemDetailReporter rpt(baseline, output(), scale_unit); + rpt.report(); } } @@ -176,14 +172,13 @@ void NMTDCmd::report_diff(bool summaryOnly, size_t scale_unit) { "Not a detail baseline"); MemBaseline baseline; - if (baseline.baseline(summaryOnly)) { - if (summaryOnly) { - MemSummaryDiffReporter rpt(early_baseline, baseline, output(), scale_unit); - rpt.report_diff(); - } else { - MemDetailDiffReporter rpt(early_baseline, baseline, output(), scale_unit); - rpt.report_diff(); - } + baseline.baseline(summaryOnly); + if (summaryOnly) { + MemSummaryDiffReporter rpt(early_baseline, baseline, output(), scale_unit); + rpt.report_diff(); + } else { + MemDetailDiffReporter rpt(early_baseline, baseline, output(), scale_unit); + rpt.report_diff(); } } diff --git a/src/hotspot/share/services/threadStackTracker.cpp b/src/hotspot/share/services/threadStackTracker.cpp index 5caad66bb89f5..fb279b7c44e61 100644 --- a/src/hotspot/share/services/threadStackTracker.cpp +++ b/src/hotspot/share/services/threadStackTracker.cpp @@ -49,40 +49,38 @@ int ThreadStackTracker::compare_thread_stack_base(const SimpleThreadStackSite& s void ThreadStackTracker::new_thread_stack(void* base, size_t size, const NativeCallStack& stack) { assert(MemTracker::tracking_level() >= NMT_summary, "Must be"); assert(base != NULL, "Should have been filtered"); + ThreadCritical tc; if (track_as_vm()) { - ThreadCritical tc; VirtualMemoryTracker::add_reserved_region((address)base, size, stack, mtThreadStack); - _thread_count ++; } else { // Use a slot in mallocMemorySummary for thread stack bookkeeping MallocMemorySummary::record_malloc(size, mtThreadStack); if (MemTracker::tracking_level() == NMT_detail) { - ThreadCritical tc; assert(_simple_thread_stacks != NULL, "Must be initialized"); SimpleThreadStackSite site((address)base, size, stack); _simple_thread_stacks->add(site); } } + _thread_count++; } void ThreadStackTracker::delete_thread_stack(void* base, size_t size) { assert(MemTracker::tracking_level() >= NMT_summary, "Must be"); assert(base != NULL, "Should have been filtered"); + ThreadCritical tc; if(track_as_vm()) { - ThreadCritical tc; VirtualMemoryTracker::remove_released_region((address)base, size); - _thread_count--; } else { // Use a slot in mallocMemorySummary for thread stack bookkeeping MallocMemorySummary::record_free(size, mtThreadStack); if (MemTracker::tracking_level() == NMT_detail) { - ThreadCritical tc; assert(_simple_thread_stacks != NULL, "Must be initialized"); SimpleThreadStackSite site((address)base, size, NativeCallStack::empty_stack()); // Fake object just to serve as compare target for delete bool removed = _simple_thread_stacks->remove(site); assert(removed, "Must exist"); } } + _thread_count--; } bool ThreadStackTracker::walk_simple_thread_stack_site(MallocSiteWalker* walker) { diff --git a/src/hotspot/share/utilities/chunkedList.hpp b/src/hotspot/share/utilities/chunkedList.hpp index 1a899ee2bfb2e..13f05cd3a85ac 100644 --- a/src/hotspot/share/utilities/chunkedList.hpp +++ b/src/hotspot/share/utilities/chunkedList.hpp @@ -44,7 +44,7 @@ template class ChunkedList : public CHeapObj { } public: - ChunkedList() : _top(_values), _next_used(NULL), _next_free(NULL) {} + ChunkedList() : _top(_values), _next_used(NULL), _next_free(NULL) {} bool is_full() const { return _top == end(); diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp index 8e8d3a7f26857..35652d5db4439 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp @@ -35,6 +35,8 @@ #include "utilities/numberSeq.hpp" #include "utilities/spinYield.hpp" +#include + // 2^30 = 1G buckets #define SIZE_BIG_LOG2 30 // 2^5 = 32 buckets @@ -499,7 +501,7 @@ inline void ConcurrentHashTable:: Bucket* prefetch_bucket = (bucket_it+1) < stop_idx ? table->get_bucket(bucket_it+1) : NULL; - if (!HaveDeletables::value, EVALUATE_FUNC>:: + if (!HaveDeletables::value, EVALUATE_FUNC>:: have_deletable(bucket, eval_f, prefetch_bucket)) { // Nothing to remove in this bucket. continue; @@ -1197,23 +1199,30 @@ template inline TableStatistics ConcurrentHashTable:: statistics_calculate(Thread* thread, VALUE_SIZE_FUNC& vs_f) { + constexpr size_t batch_size = 128; NumberSeq summary; size_t literal_bytes = 0; InternalTable* table = get_table(); - for (size_t bucket_it = 0; bucket_it < table->_size; bucket_it++) { + size_t num_batches = table->_size / batch_size; + for (size_t batch_start = 0; batch_start < _table->_size; batch_start += batch_size) { + // We batch the use of ScopedCS here as it has been found to be quite expensive to + // invoke it for every single bucket. + size_t batch_end = MIN2(batch_start + batch_size, _table->_size); ScopedCS cs(thread, this); - size_t count = 0; - Bucket* bucket = table->get_bucket(bucket_it); - if (bucket->have_redirect() || bucket->is_locked()) { - continue; - } - Node* current_node = bucket->first(); - while (current_node != NULL) { - ++count; - literal_bytes += vs_f(current_node->value()); - current_node = current_node->next(); + for (size_t bucket_it = batch_start; bucket_it < batch_end; bucket_it++) { + size_t count = 0; + Bucket* bucket = table->get_bucket(bucket_it); + if (bucket->have_redirect() || bucket->is_locked()) { + continue; + } + Node* current_node = bucket->first(); + while (current_node != nullptr) { + ++count; + literal_bytes += vs_f(current_node->value()); + current_node = current_node->next(); + } + summary.add((double)count); } - summary.add((double)count); } return TableStatistics(_stats_rate, summary, literal_bytes, sizeof(Bucket), sizeof(Node)); diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index ddb551dd0048e..d58296eaaf14c 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -440,7 +440,7 @@ extern "C" JNIEXPORT void disnm(intptr_t p) { extern "C" JNIEXPORT void printnm(intptr_t p) { char buffer[256]; - sprintf(buffer, "printnm: " INTPTR_FORMAT, p); + os::snprintf_checked(buffer, sizeof(buffer), "printnm: " INTPTR_FORMAT, p); Command c(buffer); CodeBlob* cb = CodeCache::find_blob((address) p); if (cb->is_nmethod()) { @@ -743,10 +743,22 @@ void disarm_assert_poison() { static void store_context(const void* context) { memcpy(&g_stored_assertion_context, context, sizeof(ucontext_t)); -#if defined(LINUX) && defined(PPC64) +#if defined(LINUX) // on Linux ppc64, ucontext_t contains pointers into itself which have to be patched up // after copying the context (see comment in sys/ucontext.h): +#if defined(PPC64) *((void**) &g_stored_assertion_context.uc_mcontext.regs) = &(g_stored_assertion_context.uc_mcontext.gp_regs); +#elif defined(AMD64) + // In the copied version, fpregs should point to the copied contents. + // Sanity check: fpregs should point into the context. + if ((address)((const ucontext_t*)context)->uc_mcontext.fpregs > (address)context) { + size_t fpregs_offset = pointer_delta(((const ucontext_t*)context)->uc_mcontext.fpregs, context, 1); + if (fpregs_offset < sizeof(ucontext_t)) { + // Preserve the offset. + *((void**) &g_stored_assertion_context.uc_mcontext.fpregs) = (void*)((address)(void*)&g_stored_assertion_context + fpregs_offset); + } + } +#endif #endif } diff --git a/src/hotspot/share/utilities/events.cpp b/src/hotspot/share/utilities/events.cpp index 100acb3e90f55..5a8281eb088ee 100644 --- a/src/hotspot/share/utilities/events.cpp +++ b/src/hotspot/share/utilities/events.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,8 @@ EventLog* Events::_logs = NULL; StringEventLog* Events::_messages = NULL; +StringEventLog* Events::_memprotect_messages = NULL; +StringEventLog* Events::_nmethod_flush_messages = NULL; StringEventLog* Events::_vm_operations = NULL; ExceptionsEventLog* Events::_exceptions = NULL; StringEventLog* Events::_redefinitions = NULL; @@ -94,6 +96,8 @@ void Events::print() { void Events::init() { if (LogEvents) { _messages = new StringEventLog("Events", "events"); + _nmethod_flush_messages = new StringEventLog("Nmethod flushes", "nmethodflushes"); + _memprotect_messages = new StringEventLog("Memory protections", "memprotects"); _vm_operations = new StringEventLog("VM Operations", "vmops"); _exceptions = new ExceptionsEventLog("Internal exceptions", "exc"); _redefinitions = new StringEventLog("Classes redefined", "redef"); diff --git a/src/hotspot/share/utilities/events.hpp b/src/hotspot/share/utilities/events.hpp index 8ddf466f206e4..ef4e69a94ea8b 100644 --- a/src/hotspot/share/utilities/events.hpp +++ b/src/hotspot/share/utilities/events.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -99,7 +99,7 @@ template class EventLogBase : public EventLog { EventRecord* _records; public: - EventLogBase(const char* name, const char* handle, int length = LogEventsBufferEntries): + EventLogBase(const char* name, const char* handle, int length = LogEventsBufferEntries): _mutex(Mutex::event, name, true, Mutex::_safepoint_check_never), _name(name), _handle(handle), @@ -220,6 +220,12 @@ class Events : AllStatic { // A log for generic messages that aren't well categorized. static StringEventLog* _messages; + // A log for memory protection related messages + static StringEventLog* _memprotect_messages; + + // A log for nmethod flush operations + static StringEventLog* _nmethod_flush_messages; + // A log for VM Operations static StringEventLog* _vm_operations; @@ -256,6 +262,10 @@ class Events : AllStatic { // Logs a generic message with timestamp and format as printf. static void log(Thread* thread, const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + static void log_memprotect(Thread* thread, const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + + static void log_nmethod_flush(Thread* thread, const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + static void log_vm_operation(Thread* thread, const char* format, ...) ATTRIBUTE_PRINTF(2, 3); // Log exception related message @@ -285,6 +295,24 @@ inline void Events::log(Thread* thread, const char* format, ...) { } } +inline void Events::log_memprotect(Thread* thread, const char* format, ...) { + if (LogEvents && _memprotect_messages != nullptr) { + va_list ap; + va_start(ap, format); + _memprotect_messages->logv(thread, format, ap); + va_end(ap); + } +} + +inline void Events::log_nmethod_flush(Thread* thread, const char* format, ...) { + if (LogEvents && _nmethod_flush_messages != nullptr) { + va_list ap; + va_start(ap, format); + _nmethod_flush_messages->logv(thread, format, ap); + va_end(ap); + } +} + inline void Events::log_vm_operation(Thread* thread, const char* format, ...) { if (LogEvents && _vm_operations != NULL) { va_list ap; diff --git a/src/hotspot/share/utilities/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index d65f5bcfafdb1..cf9291f6dcf52 100644 --- a/src/hotspot/share/utilities/exceptions.cpp +++ b/src/hotspot/share/utilities/exceptions.cpp @@ -44,6 +44,9 @@ #include "utilities/events.hpp" #include "utilities/exceptions.hpp" +// Limit exception message components to 64K (the same max as Symbols) +#define MAX_LEN 65535 + // Implementation of ThreadShadow void check_ThreadShadow() { const ByteSize offset1 = byte_offset_of(ThreadShadow, _pending_exception); @@ -147,10 +150,11 @@ void Exceptions::_throw(JavaThread* thread, const char* file, int line, Handle h // tracing (do this up front - so it works during boot strapping) // Note, the print_value_string() argument is not called unless logging is enabled! - log_info(exceptions)("Exception <%s%s%s> (" INTPTR_FORMAT ") \n" + log_info(exceptions)("Exception <%.*s%s%.*s> (" INTPTR_FORMAT ") \n" "thrown [%s, line %d]\nfor thread " INTPTR_FORMAT, - h_exception->print_value_string(), - message ? ": " : "", message ? message : "", + MAX_LEN, h_exception->print_value_string(), + message ? ": " : "", + MAX_LEN, message ? message : "", p2i(h_exception()), file, line, p2i(thread)); // for AbortVMOnException flag @@ -565,13 +569,13 @@ void Exceptions::log_exception(Handle exception, const char* message) { ResourceMark rm; const char* detail_message = java_lang_Throwable::message_as_utf8(exception()); if (detail_message != nullptr) { - log_info(exceptions)("Exception <%s: %s>\n thrown in %s", - exception->print_value_string(), - detail_message, - message); + log_info(exceptions)("Exception <%.*s: %.*s>\n thrown in %.*s", + MAX_LEN, exception->print_value_string(), + MAX_LEN, detail_message, + MAX_LEN, message); } else { - log_info(exceptions)("Exception <%s>\n thrown in %s", - exception->print_value_string(), - message); + log_info(exceptions)("Exception <%.*s>\n thrown in %.*s", + MAX_LEN, exception->print_value_string(), + MAX_LEN, message); } } diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 6480b082c81f1..98d7fbc674c82 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -94,10 +94,12 @@ class oopDesc; #define BOOL_TO_STR(_b_) ((_b_) ? "true" : "false") // Format 32-bit quantities. -#define INT32_FORMAT "%" PRId32 -#define UINT32_FORMAT "%" PRIu32 -#define INT32_FORMAT_W(width) "%" #width PRId32 -#define UINT32_FORMAT_W(width) "%" #width PRIu32 +#define INT32_FORMAT "%" PRId32 +#define INT32_FORMAT_X_0 "0x%08" PRIx32 +#define INT32_FORMAT_W(width) "%" #width PRId32 +#define UINT32_FORMAT "%" PRIu32 +#define UINT32_FORMAT_X_0 "0x%08" PRIx32 +#define UINT32_FORMAT_W(width) "%" #width PRIu32 #define PTR32_FORMAT "0x%08" PRIx32 #define PTR32_FORMAT_W(width) "0x%" #width PRIx32 diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index 84d4c1b3ba291..a9c7e7e3f96b0 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -118,7 +118,7 @@ class GrowableArrayView : public GrowableArrayBase { protected: E* _data; // data array - GrowableArrayView(E* data, int initial_max, int initial_len) : + GrowableArrayView(E* data, int initial_max, int initial_len) : GrowableArrayBase(initial_max, initial_len), _data(data) {} ~GrowableArrayView() {} @@ -126,7 +126,7 @@ class GrowableArrayView : public GrowableArrayBase { public: const static GrowableArrayView EMPTY; - bool operator==(const GrowableArrayView& rhs) const { + bool operator==(const GrowableArrayView& rhs) const { if (_len != rhs._len) return false; for (int i = 0; i < _len; i++) { @@ -137,7 +137,7 @@ class GrowableArrayView : public GrowableArrayBase { return true; } - bool operator!=(const GrowableArrayView& rhs) const { + bool operator!=(const GrowableArrayView& rhs) const { return !(*this == rhs); } @@ -270,10 +270,12 @@ class GrowableArrayView : public GrowableArrayBase { } void sort(int f(E*, E*)) { + if (_data == nullptr) return; qsort(_data, length(), sizeof(E), (_sort_Fn)f); } // sort by fixed-stride sub arrays: void sort(int f(E*, E*), int stride) { + if (_data == nullptr) return; qsort(_data, length() / stride, sizeof(E) * stride, (_sort_Fn)f); } @@ -467,7 +469,7 @@ class GrowableArrayWithAllocator : public GrowableArrayView { return this->at(location); } - void swap(GrowableArrayWithAllocator* other) { + void swap(GrowableArrayWithAllocator* other) { ::swap(this->_data, other->_data); ::swap(this->_len, other->_len); ::swap(this->_max, other->_max); @@ -618,8 +620,8 @@ class GrowableArrayMetadata { // See: init_checks. template -class GrowableArray : public GrowableArrayWithAllocator > { - friend class GrowableArrayWithAllocator >; +class GrowableArray : public GrowableArrayWithAllocator> { + friend class GrowableArrayWithAllocator; friend class GrowableArrayTest; static E* allocate(int max) { @@ -669,7 +671,7 @@ class GrowableArray : public GrowableArrayWithAllocator > { public: GrowableArray(int initial_max = 2, MEMFLAGS memflags = mtNone) : - GrowableArrayWithAllocator >( + GrowableArrayWithAllocator( allocate(initial_max, memflags), initial_max), _metadata(memflags) { @@ -677,7 +679,7 @@ class GrowableArray : public GrowableArrayWithAllocator > { } GrowableArray(int initial_max, int initial_len, const E& filler, MEMFLAGS memflags = mtNone) : - GrowableArrayWithAllocator >( + GrowableArrayWithAllocator( allocate(initial_max, memflags), initial_max, initial_len, filler), _metadata(memflags) { @@ -685,7 +687,7 @@ class GrowableArray : public GrowableArrayWithAllocator > { } GrowableArray(Arena* arena, int initial_max, int initial_len, const E& filler) : - GrowableArrayWithAllocator >( + GrowableArrayWithAllocator( allocate(initial_max, arena), initial_max, initial_len, filler), _metadata(arena) { @@ -766,15 +768,15 @@ class GrowableArrayIterator : public StackObj { public: GrowableArrayIterator() : _array(NULL), _position(0) { } - GrowableArrayIterator& operator++() { ++_position; return *this; } - E operator*() { return _array->at(_position); } + GrowableArrayIterator& operator++() { ++_position; return *this; } + E operator*() { return _array->at(_position); } - bool operator==(const GrowableArrayIterator& rhs) { + bool operator==(const GrowableArrayIterator& rhs) { assert(_array == rhs._array, "iterator belongs to different array"); return _position == rhs._position; } - bool operator!=(const GrowableArrayIterator& rhs) { + bool operator!=(const GrowableArrayIterator& rhs) { assert(_array == rhs._array, "iterator belongs to different array"); return _position != rhs._position; } diff --git a/src/hotspot/share/utilities/linkedlist.hpp b/src/hotspot/share/utilities/linkedlist.hpp index 16ee6a844fa1a..2c5ffe6cb4808 100644 --- a/src/hotspot/share/utilities/linkedlist.hpp +++ b/src/hotspot/share/utilities/linkedlist.hpp @@ -82,7 +82,7 @@ template class LinkedListNode : public ResourceObj { template class LinkedList : public ResourceObj { protected: LinkedListNode* _head; - NONCOPYABLE(LinkedList); + NONCOPYABLE(LinkedList); public: LinkedList() : _head(NULL) { } diff --git a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp index 04995064fe3a9..4d7af94d0c369 100644 --- a/src/hotspot/share/utilities/ostream.cpp +++ b/src/hotspot/share/utilities/ostream.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -614,7 +614,7 @@ void fileStream::flush() { void fdStream::write(const char* s, size_t len) { if (_fd != -1) { // Make an unused local variable to avoid warning from gcc compiler. - size_t count = ::write(_fd, s, (int)len); + ssize_t count = ::write(_fd, s, (int)len); update_position(s, len); } } @@ -1071,7 +1071,7 @@ bufferedStream::~bufferedStream() { #include #include #elif defined(_WINDOWS) -#include +#include #endif // Network access @@ -1112,25 +1112,31 @@ void networkStream::close() { } } -bool networkStream::connect(const char *ip, short port) { +// host could be IP address, or a host name +bool networkStream::connect(const char *host, short port) { - struct sockaddr_in server; - server.sin_family = AF_INET; - server.sin_port = htons(port); + char s_port[6]; // 5 digits max plus terminator + int ret = os::snprintf(s_port, sizeof(s_port), "%hu", (unsigned short) port); + assert(ret > 0, "snprintf failed: %d", ret); - server.sin_addr.s_addr = inet_addr(ip); - if (server.sin_addr.s_addr == (uint32_t)-1) { - struct hostent* host = os::get_host_by_name((char*)ip); - if (host != NULL) { - memcpy(&server.sin_addr, host->h_addr_list[0], host->h_length); - } else { - return false; - } - } + struct addrinfo* addr_info = nullptr; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; // Allow IPv4 only + hints.ai_socktype = SOCK_STREAM; // TCP only + + // getaddrinfo can resolve both an IP address and a host name + ret = getaddrinfo(host, s_port, &hints, &addr_info); + if (ret != 0) { + warning("networkStream::connect getaddrinfo for host %s and port %s failed: %s", + host, s_port, gai_strerror(ret)); + return false; + } - int result = os::connect(_socket, (struct sockaddr*)&server, sizeof(struct sockaddr_in)); - return (result >= 0); + ret = os::connect(_socket, addr_info->ai_addr, (socklen_t)addr_info->ai_addrlen); + freeaddrinfo(addr_info); + return (ret >= 0); } #endif diff --git a/src/hotspot/share/utilities/population_count.hpp b/src/hotspot/share/utilities/population_count.hpp index a88a27246fc7a..499574e1974bd 100644 --- a/src/hotspot/share/utilities/population_count.hpp +++ b/src/hotspot/share/utilities/population_count.hpp @@ -25,13 +25,12 @@ #ifndef SHARE_UTILITIES_POPULATION_COUNT_HPP #define SHARE_UTILITIES_POPULATION_COUNT_HPP -#include "metaprogramming/conditional.hpp" #include "metaprogramming/enableIf.hpp" -#include "metaprogramming/isIntegral.hpp" -#include "metaprogramming/isSigned.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include + // Returns the population count of x, i.e., the number of bits set in x. // // Adapted from Hacker's Delight, 2nd Edition, Figure 5-2 and the text that @@ -47,12 +46,12 @@ template inline unsigned population_count(T x) { STATIC_ASSERT(BitsPerWord <= 128); STATIC_ASSERT(BitsPerByte == 8); - STATIC_ASSERT(IsIntegral::value); - STATIC_ASSERT(!IsSigned::value); + STATIC_ASSERT(std::is_integral::value); + STATIC_ASSERT(!std::is_signed::value); // We need to take care with implicit integer promotion when dealing with // integers < 32-bit. We chose to do this by explicitly widening constants // to unsigned - typedef typename Conditional<(sizeof(T) < sizeof(unsigned)), unsigned, T>::type P; + using P = std::conditional_t<(sizeof(T) < sizeof(unsigned)), unsigned, T>; const T all = ~T(0); // 0xFF..FF const P fives = all/3; // 0x55..55 const P threes = (all/15) * 3; // 0x33..33 diff --git a/src/hotspot/share/utilities/utf8.cpp b/src/hotspot/share/utilities/utf8.cpp index 6ebeb9a6c9b21..a162405859254 100644 --- a/src/hotspot/share/utilities/utf8.cpp +++ b/src/hotspot/share/utilities/utf8.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "utilities/utf8.hpp" +#include "runtime/os.hpp" // Assume the utf8 string is in legal form and has been // checked in the class file parser/format checker. @@ -220,7 +221,7 @@ void UTF8::as_quoted_ascii(const char* utf8_str, int utf8_length, char* buf, int *p++ = (char)c; } else { if (p + 6 >= end) break; // string is truncated - sprintf(p, "\\u%04x", c); + os::snprintf_checked(p, 7, "\\u%04x", c); // counting terminating zero in p += 6; } } @@ -427,12 +428,16 @@ int UNICODE::utf8_size(jbyte c) { template int UNICODE::utf8_length(const T* base, int length) { - int result = 0; + size_t result = 0; for (int index = 0; index < length; index++) { T c = base[index]; - result += utf8_size(c); + int sz = utf8_size(c); + if (result + sz > INT_MAX-1) { + break; + } + result += sz; } - return result; + return checked_cast(result); } template @@ -514,7 +519,7 @@ void UNICODE::as_quoted_ascii(const T* base, int length, char* buf, int buflen) *p++ = (char)c; } else { if (p + 6 >= end) break; // string is truncated - sprintf(p, "\\u%04x", c); + os::snprintf_checked(p, 7, "\\u%04x", c); p += 6; } } diff --git a/src/hotspot/share/utilities/vmassert_reinstall.hpp b/src/hotspot/share/utilities/vmassert_reinstall.hpp new file mode 100644 index 0000000000000..32d31ac0c4f79 --- /dev/null +++ b/src/hotspot/share/utilities/vmassert_reinstall.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +// Intentionally no #include guard. May be included multiple times for effect. + +// See vmassert_uninstall.hpp for usage. + +// Remove possible stdlib assert macro (or any others, for that matter). +#undef assert + +// Reinstall HotSpot's assert macro, if previously defined. +#ifdef vmassert +#define assert(p, ...) vmassert(p, __VA_ARGS__) +#endif + diff --git a/src/hotspot/share/utilities/vmassert_uninstall.hpp b/src/hotspot/share/utilities/vmassert_uninstall.hpp new file mode 100644 index 0000000000000..dd6d51633dd70 --- /dev/null +++ b/src/hotspot/share/utilities/vmassert_uninstall.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +// Intentionally no #include guard. May be included multiple times for effect. + +// The files vmassert_uninstall.hpp and vmassert_reinstall.hpp provide a +// workaround for the name collision between HotSpot's assert macro and the +// Standard Library's assert macro. When including a 3rd-party header that +// uses (and so includes) the standard assert macro, wrap that inclusion with +// includes of these two files, e.g. +// +// #include "utilities/vmassert_uninstall.hpp" +// #include
    +// #include "utilities/vmassert_reinstall.hpp" +// +// This removes the HotSpot macro definition while pre-processing the +// 3rd-party header, then reinstates the HotSpot macro (if previously defined) +// for following code. + +// Remove HotSpot's assert macro, if present. +#ifdef vmassert +#undef assert +#endif // vmassert + diff --git a/src/hotspot/share/utilities/waitBarrier_generic.cpp b/src/hotspot/share/utilities/waitBarrier_generic.cpp index b5d9ff67eb782..dbf4db336c2f5 100644 --- a/src/hotspot/share/utilities/waitBarrier_generic.cpp +++ b/src/hotspot/share/utilities/waitBarrier_generic.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,66 +30,228 @@ #include "utilities/waitBarrier_generic.hpp" #include "utilities/spinYield.hpp" +// Implements the striped semaphore wait barrier. +// +// To guarantee progress and safety, we need to make sure that new barrier tag +// starts with the completely empty set of waiters and free semaphore. This +// requires either waiting for all threads to leave wait() for current barrier +// tag on disarm(), or waiting for all threads to leave the previous tag before +// reusing the semaphore in arm(). +// +// When there are multiple threads, it is normal for some threads to take +// significant time to leave the barrier. Waiting for these threads introduces +// stalls on barrier reuse. +// +// If we wait on disarm(), this stall is nearly guaranteed to happen if some threads +// are de-scheduled by prior wait(). It would be especially bad if there are more +// waiting threads than CPUs: every thread would need to wake up and register itself +// as leaving, before we can unblock from disarm(). +// +// If we wait on arm(), we can get lucky that most threads would be able to catch up, +// exit wait(), and so we arrive to arm() with semaphore ready for reuse. However, +// that is still insufficient in practice. +// +// Therefore, this implementation goes a step further and implements the _striped_ +// semaphores. We maintain several semaphores in cells. The barrier tags are assigned +// to cells in some simple manner. Most of the current uses have sequential barrier +// tags, so simple modulo works well. We then operate on a cell like we would operate +// on a single semaphore: we wait at arm() for all threads to catch up before reusing +// the cell. For the cost of maintaining just a few cells, we have enough window for +// threads to catch up. +// +// The correctness is guaranteed by using a single atomic state variable per cell, +// with updates always done with CASes: +// +// [.......... barrier tag ..........][.......... waiters ..........] +// 63 31 0 +// +// Cell starts with zero tag and zero waiters. Arming the cell swings barrier tag from +// zero to some tag, while checking that no waiters have appeared. Disarming swings +// the barrier tag back from tag to zero. Every waiter registers itself by incrementing +// the "waiters", while checking that barrier tag is still the same. Every completing waiter +// decrements the "waiters". When all waiters complete, a cell ends up in initial state, +// ready to be armed again. This allows accurate tracking of how many signals +// to issue and does not race with disarm. +// +// The implementation uses the strongest (default) barriers for extra safety, even +// when not strictly required to do so for correctness. Extra barrier overhead is +// dominated by the actual wait/notify latency anyway. +// + void GenericWaitBarrier::arm(int barrier_tag) { - assert(_barrier_tag == 0, "Already armed"); - assert(_waiters == 0, "We left a thread hanging"); - _barrier_tag = barrier_tag; - _waiters = 0; + assert(barrier_tag != 0, "Pre arm: Should be arming with armed value"); + assert(Atomic::load(&_barrier_tag) == 0, + "Pre arm: Should not be already armed. Tag: %d", + Atomic::load(&_barrier_tag)); + Atomic::release_store(&_barrier_tag, barrier_tag); + + Cell &cell = tag_to_cell(barrier_tag); + cell.arm(barrier_tag); + + // API specifies arm() must provide a trailing fence. OrderAccess::fence(); } -int GenericWaitBarrier::wake_if_needed() { - assert(_barrier_tag == 0, "Not disarmed"); - int w = _waiters; - if (w == 0) { - // Load of _barrier_threads in caller must not pass the load of _waiters. - OrderAccess::loadload(); - return 0; - } - assert(w > 0, "Bad counting"); - // We need an exact count which never goes below zero, - // otherwise the semaphore may be signalled too many times. - if (Atomic::cmpxchg(&_waiters, w, w - 1) == w) { - _sem_barrier.signal(); - return w - 1; - } - return w; +void GenericWaitBarrier::disarm() { + int barrier_tag = Atomic::load_acquire(&_barrier_tag); + assert(barrier_tag != 0, "Pre disarm: Should be armed. Tag: %d", barrier_tag); + Atomic::release_store(&_barrier_tag, 0); + + Cell &cell = tag_to_cell(barrier_tag); + cell.disarm(barrier_tag); + + // API specifies disarm() must provide a trailing fence. + OrderAccess::fence(); } -void GenericWaitBarrier::disarm() { - assert(_barrier_tag != 0, "Not armed"); - _barrier_tag = 0; - // Loads of _barrier_threads/_waiters must not float above disarm store and - // disarm store must not sink below. +void GenericWaitBarrier::wait(int barrier_tag) { + assert(barrier_tag != 0, "Pre wait: Should be waiting on armed value"); + + Cell &cell = tag_to_cell(barrier_tag); + cell.wait(barrier_tag); + + // API specifies wait() must provide a trailing fence. OrderAccess::fence(); - int left; +} + +void GenericWaitBarrier::Cell::arm(int32_t requested_tag) { + // Before we continue to arm, we need to make sure that all threads + // have left the previous cell. + + int64_t state; + SpinYield sp; - do { - left = GenericWaitBarrier::wake_if_needed(); - if (left == 0 && _barrier_threads > 0) { - // There is no thread to wake but we still have barrier threads. + while (true) { + state = Atomic::load_acquire(&_state); + assert(decode_tag(state) == 0, + "Pre arm: Should not be armed. " + "Tag: " INT32_FORMAT "; Waiters: " INT32_FORMAT, + decode_tag(state), decode_waiters(state)); + if (decode_waiters(state) == 0) { + break; + } + sp.wait(); + } + + // Try to swing cell to armed. This should always succeed after the check above. + int64_t new_state = encode(requested_tag, 0); + int64_t prev_state = Atomic::cmpxchg(&_state, state, new_state); + if (prev_state != state) { + fatal("Cannot arm the wait barrier. " + "Tag: " INT32_FORMAT "; Waiters: " INT32_FORMAT, + decode_tag(prev_state), decode_waiters(prev_state)); + } +} + +int GenericWaitBarrier::Cell::signal_if_needed(int max) { + int signals = 0; + while (true) { + int cur = Atomic::load_acquire(&_outstanding_wakeups); + if (cur == 0) { + // All done, no more waiters. + return 0; + } + assert(cur > 0, "Sanity"); + + int prev = Atomic::cmpxchg(&_outstanding_wakeups, cur, cur - 1); + if (prev != cur) { + // Contention, return to caller for early return or backoff. + return prev; + } + + // Signal! + _sem.signal(); + + if (++signals >= max) { + // Signalled requested number of times, break out. + return prev; + } + } +} + +void GenericWaitBarrier::Cell::disarm(int32_t expected_tag) { + int32_t waiters; + + while (true) { + int64_t state = Atomic::load_acquire(&_state); + int32_t tag = decode_tag(state); + waiters = decode_waiters(state); + + assert((tag == expected_tag) && (waiters >= 0), + "Mid disarm: Should be armed with expected tag and have sane waiters. " + "Tag: " INT32_FORMAT "; Waiters: " INT32_FORMAT, + tag, waiters); + + int64_t new_state = encode(0, waiters); + if (Atomic::cmpxchg(&_state, state, new_state) == state) { + // Successfully disarmed. + break; + } + } + + // Wake up waiters, if we have at least one. + // Allow other threads to assist with wakeups, if possible. + if (waiters > 0) { + Atomic::release_store(&_outstanding_wakeups, waiters); + SpinYield sp; + while (signal_if_needed(INT_MAX) > 0) { sp.wait(); } - // We must loop here until there are no waiters or potential waiters. - } while (left > 0 || _barrier_threads > 0); - // API specifies disarm() must provide a trailing fence. - OrderAccess::fence(); + } + assert(Atomic::load(&_outstanding_wakeups) == 0, "Post disarm: Should not have outstanding wakeups"); } -void GenericWaitBarrier::wait(int barrier_tag) { - assert(barrier_tag != 0, "Trying to wait on disarmed value"); - if (barrier_tag != _barrier_tag) { - // API specifies wait() must provide a trailing fence. - OrderAccess::fence(); - return; +void GenericWaitBarrier::Cell::wait(int32_t expected_tag) { + // Try to register ourselves as pending waiter. + while (true) { + int64_t state = Atomic::load_acquire(&_state); + int32_t tag = decode_tag(state); + if (tag != expected_tag) { + // Cell tag had changed while waiting here. This means either the cell had + // been disarmed, or we are late and the cell was armed with a new tag. + // Exit without touching anything else. + return; + } + int32_t waiters = decode_waiters(state); + + assert((tag == expected_tag) && (waiters >= 0 && waiters < INT32_MAX), + "Before wait: Should be armed with expected tag and waiters are in range. " + "Tag: " INT32_FORMAT "; Waiters: " INT32_FORMAT, + tag, waiters); + + int64_t new_state = encode(tag, waiters + 1); + if (Atomic::cmpxchg(&_state, state, new_state) == state) { + // Success! Proceed to wait. + break; + } } - Atomic::add(&_barrier_threads, 1); - if (barrier_tag != 0 && barrier_tag == _barrier_tag) { - Atomic::add(&_waiters, 1); - _sem_barrier.wait(); - // We help out with posting, but we need to do so before we decrement the - // _barrier_threads otherwise we might wake threads up in next wait. - GenericWaitBarrier::wake_if_needed(); + + // Wait for notification. + _sem.wait(); + + // Unblocked! We help out with waking up two siblings. This allows to avalanche + // the wakeups for many threads, even if some threads are lagging behind. + // Note that we can only do this *before* reporting back as completed waiter, + // otherwise we might prematurely wake up threads for another barrier tag. + // Current arm() sequence protects us from this trouble by waiting until all waiters + // leave. + signal_if_needed(2); + + // Register ourselves as completed waiter before leaving. + while (true) { + int64_t state = Atomic::load_acquire(&_state); + int32_t tag = decode_tag(state); + int32_t waiters = decode_waiters(state); + + assert((tag == 0) && (waiters > 0), + "After wait: Should be not armed and have non-complete waiters. " + "Tag: " INT32_FORMAT "; Waiters: " INT32_FORMAT, + tag, waiters); + + int64_t new_state = encode(tag, waiters - 1); + if (Atomic::cmpxchg(&_state, state, new_state) == state) { + // Success! + break; + } } - Atomic::add(&_barrier_threads, -1); } diff --git a/src/hotspot/share/utilities/waitBarrier_generic.hpp b/src/hotspot/share/utilities/waitBarrier_generic.hpp index 50bfea6aebfb7..d3a45b33b82ef 100644 --- a/src/hotspot/share/utilities/waitBarrier_generic.hpp +++ b/src/hotspot/share/utilities/waitBarrier_generic.hpp @@ -26,29 +26,79 @@ #define SHARE_UTILITIES_WAITBARRIER_GENERIC_HPP #include "memory/allocation.hpp" +#include "memory/padded.hpp" #include "runtime/semaphore.hpp" #include "utilities/globalDefinitions.hpp" -// In addition to the barrier tag, it uses two counters to keep the semaphore -// count correct and not leave any late thread waiting. class GenericWaitBarrier : public CHeapObj { +private: + class Cell : public CHeapObj { + private: + // Pad out the cells to avoid interference between the cells. + // This would insulate from stalls when adjacent cells have returning + // workers and contend over the cache line for current latency-critical + // cell. + DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0); + + Semaphore _sem; + + // Cell state, tracks the arming + waiters status + volatile int64_t _state; + + // Wakeups to deliver for current waiters + volatile int _outstanding_wakeups; + + int signal_if_needed(int max); + + static int64_t encode(int32_t barrier_tag, int32_t waiters) { + int64_t val = (((int64_t) barrier_tag) << 32) | + (((int64_t) waiters) & 0xFFFFFFFF); + assert(decode_tag(val) == barrier_tag, "Encoding is reversible"); + assert(decode_waiters(val) == waiters, "Encoding is reversible"); + return val; + } + + static int32_t decode_tag(int64_t value) { + return (int32_t)(value >> 32); + } + + static int32_t decode_waiters(int64_t value) { + return (int32_t)(value & 0xFFFFFFFF); + } + + public: + Cell() : _sem(0), _state(encode(0, 0)), _outstanding_wakeups(0) {} + NONCOPYABLE(Cell); + + void arm(int32_t requested_tag); + void disarm(int32_t expected_tag); + void wait(int32_t expected_tag); + }; + + // Should be enough for most uses without exploding the footprint. + static constexpr int CELLS_COUNT = 16; + + Cell _cells[CELLS_COUNT]; + + // Trailing padding to protect the last cell. + DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0); + volatile int _barrier_tag; - // The number of threads waiting on or about to wait on the semaphore. - volatile int _waiters; - // The number of threads in the wait path, before or after the tag check. - // These threads can become waiters. - volatile int _barrier_threads; - Semaphore _sem_barrier; + + // Trailing padding to insulate the rest of the barrier from adjacent + // data structures. The leading padding is not needed, as cell padding + // handles this for us. + DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, 0); NONCOPYABLE(GenericWaitBarrier); - int wake_if_needed(); + Cell& tag_to_cell(int tag) { return _cells[tag & (CELLS_COUNT - 1)]; } - public: - GenericWaitBarrier() : _barrier_tag(0), _waiters(0), _barrier_threads(0), _sem_barrier(0) {} +public: + GenericWaitBarrier() : _cells(), _barrier_tag(0) {} ~GenericWaitBarrier() {} - const char* description() { return "semaphore"; } + const char* description() { return "striped semaphore"; } void arm(int barrier_tag); void disarm(); diff --git a/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c b/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c index 30fa8eea2069a..b9f3428c1fa72 100644 --- a/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c +++ b/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c @@ -89,25 +89,35 @@ jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, } } - // Get buffer size needed to read all processes - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; - if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) { - JNU_ThrowByNameWithLastError(env, - "java/lang/RuntimeException", "sysctl failed"); - return -1; - } + int errsysctl; + int maxRetries = 100; + void *buffer = NULL; + do { + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; + if (buffer != NULL) free(buffer); + // Get buffer size needed to read all processes + if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) { + JNU_ThrowByNameWithMessageAndLastError(env, + "java/lang/RuntimeException", "sysctl failed"); + return -1; + } - // Allocate buffer big enough for all processes - void *buffer = malloc(bufSize); - if (buffer == NULL) { - JNU_ThrowOutOfMemoryError(env, "malloc failed"); - return -1; - } + // Allocate buffer big enough for all processes; add a little + // bit of space to be able to hold a few more proc infos + // for processes started right after the first sysctl call + buffer = malloc(bufSize + 4 * sizeof(struct kinfo_proc)); + if (buffer == NULL) { + JNU_ThrowOutOfMemoryError(env, "malloc failed"); + return -1; + } - // Read process info for all processes - if (sysctl(mib, 4, buffer, &bufSize, NULL, 0) < 0) { - JNU_ThrowByNameWithLastError(env, - "java/lang/RuntimeException", "sysctl failed"); + // Read process info for all processes + errsysctl = sysctl(mib, 4, buffer, &bufSize, NULL, 0); + } while (errsysctl < 0 && errno == ENOMEM && maxRetries-- > 0); + + if (errsysctl < 0) { + JNU_ThrowByNameWithMessageAndLastError(env, + "java/lang/RuntimeException", "sysctl failed to get info about all processes"); free(buffer); return -1; } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java index b584ac415f75d..b232469f66bd0 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,6 +57,7 @@ final class DHPrivateKey implements PrivateKey, private BigInteger x; // the key bytes, without the algorithm information + // cannot be final as it's re-assigned for deserialization private byte[] key; // the encoded key @@ -71,6 +72,135 @@ final class DHPrivateKey implements PrivateKey, // the private-value length (optional) private int l; + private static class DHComponents { + final BigInteger x; + final BigInteger p; + final BigInteger g; + final int l; + final byte[] key; + + DHComponents(BigInteger x, BigInteger p, BigInteger g, int l, + byte[] key) { + this.x = x; + this.p = p; + this.g = g; + this.l = l; + this.key = key; + } + } + + // parses the specified encoding into a DHComponents object + private static DHComponents decode(byte[] encodedKey) + throws IOException { + DerValue val = null; + + try { + val = new DerValue(encodedKey); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Key not a SEQUENCE"); + } + + // version + BigInteger parsedVersion = val.data.getBigInteger(); + if (!parsedVersion.equals(PKCS8_VERSION)) { + throw new IOException("version mismatch: (supported: " + + PKCS8_VERSION + ", parsed: " + parsedVersion); + } + + // privateKeyAlgorithm + DerValue algid = val.data.getDerValue(); + if (algid.tag != DerValue.tag_Sequence) { + throw new IOException("AlgId is not a SEQUENCE"); + } + DerInputStream derInStream = algid.toDerInputStream(); + ObjectIdentifier oid = derInStream.getOID(); + if (oid == null) { + throw new IOException("Null OID"); + } + if (derInStream.available() == 0) { + throw new IOException("Parameters missing"); + } + // parse the parameters + DerValue params = derInStream.getDerValue(); + if (params.tag == DerValue.tag_Null) { + throw new IOException("Null parameters"); + } + if (params.tag != DerValue.tag_Sequence) { + throw new IOException("Parameters not a SEQUENCE"); + } + params.data.reset(); + BigInteger p = params.data.getBigInteger(); + BigInteger g = params.data.getBigInteger(); + // Private-value length is OPTIONAL + int l = (params.data.available() != 0 ? + params.data.getInteger() : 0); + // should have no trailing data + if (params.data.available() != 0) { + throw new IOException("Extra parameter data"); + } + + // privateKey + byte[] key = val.data.getOctetString(); + DerInputStream in = new DerInputStream(key); + BigInteger x = in.getBigInteger(); + + // should have no trailing data + if (val.data.available() != 0) { + throw new IOException("Excess trailing data"); + } + return new DHComponents(x, p, g, l, key); + } catch (NumberFormatException e) { + throw new IOException("Error parsing key encoding", e); + } finally { + if (val != null) { + val.clear(); + } + } + } + + // Generates the ASN.1 encoding + private static byte[] encode(BigInteger p, BigInteger g, int l, + byte[] key) { + try { + DerOutputStream tmp = new DerOutputStream(); + + // version + tmp.putInteger(PKCS8_VERSION); + + // privateKeyAlgorithm + DerOutputStream algid = new DerOutputStream(); + + // store OID + algid.putOID(DHPublicKey.DH_OID); + // encode parameters + DerOutputStream params = new DerOutputStream(); + params.putInteger(p); + params.putInteger(g); + if (l != 0) { + params.putInteger(l); + } + // wrap parameters into SEQUENCE + DerValue paramSequence = new DerValue(DerValue.tag_Sequence, + params.toByteArray()); + // store parameter SEQUENCE in algid + algid.putDerValue(paramSequence); + // wrap algid into SEQUENCE + tmp.write(DerValue.tag_Sequence, algid); + + // privateKey + tmp.putOctetString(key); + + // make it a SEQUENCE + DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp); + byte[] encoded = val.toByteArray(); + val.clear(); + + return encoded; + } catch (IOException e) { + throw new AssertionError(e); + } + } + /** * Make a DH private key out of a private value x, a prime * modulus p, and a base generator g. @@ -82,7 +212,7 @@ final class DHPrivateKey implements PrivateKey, * @throws ProviderException if the key cannot be encoded */ DHPrivateKey(BigInteger x, BigInteger p, BigInteger g) - throws InvalidKeyException { + throws InvalidKeyException { this(x, p, g, 0); } @@ -103,16 +233,18 @@ final class DHPrivateKey implements PrivateKey, this.p = p; this.g = g; this.l = l; + + byte[] xbytes = x.toByteArray(); + DerValue val = new DerValue(DerValue.tag_Integer, xbytes); try { - byte[] xbytes = x.toByteArray(); - DerValue val = new DerValue(DerValue.tag_Integer, xbytes); this.key = val.toByteArray(); - val.clear(); - Arrays.fill(xbytes, (byte)0); - encode(); } catch (IOException e) { throw new ProviderException("Cannot produce ASN.1 encoding", e); + } finally { + val.clear(); + Arrays.fill(xbytes, (byte) 0); } + this.encodedKey = encode(p, g, l, key); } /** @@ -124,71 +256,18 @@ final class DHPrivateKey implements PrivateKey, * a Diffie-Hellman private key */ DHPrivateKey(byte[] encodedKey) throws InvalidKeyException { - DerValue val = null; + this.encodedKey = encodedKey.clone(); + DHComponents dc; try { - val = new DerValue(encodedKey); - if (val.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException ("Key not a SEQUENCE"); - } - - // - // version - // - BigInteger parsedVersion = val.data.getBigInteger(); - if (!parsedVersion.equals(PKCS8_VERSION)) { - throw new IOException("version mismatch: (supported: " + - PKCS8_VERSION + ", parsed: " + - parsedVersion); - } - - // - // privateKeyAlgorithm - // - DerValue algid = val.data.getDerValue(); - if (algid.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("AlgId is not a SEQUENCE"); - } - DerInputStream derInStream = algid.toDerInputStream(); - ObjectIdentifier oid = derInStream.getOID(); - if (oid == null) { - throw new InvalidKeyException("Null OID"); - } - if (derInStream.available() == 0) { - throw new InvalidKeyException("Parameters missing"); - } - // parse the parameters - DerValue params = derInStream.getDerValue(); - if (params.tag == DerValue.tag_Null) { - throw new InvalidKeyException("Null parameters"); - } - if (params.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("Parameters not a SEQUENCE"); - } - params.data.reset(); - this.p = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); - // Private-value length is OPTIONAL - if (params.data.available() != 0) { - this.l = params.data.getInteger(); - } - if (params.data.available() != 0) { - throw new InvalidKeyException("Extra parameter data"); - } - - // - // privateKey - // - this.key = val.data.getOctetString(); - parseKeyBits(); - - this.encodedKey = encodedKey.clone(); - } catch (IOException | NumberFormatException e) { - throw new InvalidKeyException("Error parsing key encoding", e); - } finally { - if (val != null) { - val.clear(); - } + dc = decode(this.encodedKey); + } catch (IOException e) { + throw new InvalidKeyException("Invalid encoding", e); } + this.x = dc.x; + this.p = dc.p; + this.g = dc.g; + this.l = dc.l; + this.key = dc.key; } /** @@ -209,59 +288,9 @@ public String getAlgorithm() { * Get the encoding of the key. */ public synchronized byte[] getEncoded() { - encode(); return encodedKey.clone(); } - /** - * Generate the encodedKey field if it has not been calculated. - * Could generate null. - */ - private void encode() { - if (this.encodedKey == null) { - try { - DerOutputStream tmp = new DerOutputStream(); - - // - // version - // - tmp.putInteger(PKCS8_VERSION); - - // - // privateKeyAlgorithm - // - DerOutputStream algid = new DerOutputStream(); - - // store OID - algid.putOID(DHPublicKey.DH_OID); - // encode parameters - DerOutputStream params = new DerOutputStream(); - params.putInteger(this.p); - params.putInteger(this.g); - if (this.l != 0) { - params.putInteger(this.l); - } - // wrap parameters into SEQUENCE - DerValue paramSequence = new DerValue(DerValue.tag_Sequence, - params.toByteArray()); - // store parameter SEQUENCE in algid - algid.putDerValue(paramSequence); - // wrap algid into SEQUENCE - tmp.write(DerValue.tag_Sequence, algid); - - // privateKey - tmp.putOctetString(this.key); - - // make it a SEQUENCE - DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp); - this.encodedKey = val.toByteArray(); - val.clear(); - } catch (IOException e) { - throw new AssertionError(e); - } - } - } - /** * Returns the private value, x. * @@ -284,18 +313,6 @@ public DHParameterSpec getParams() { } } - private void parseKeyBits() throws InvalidKeyException { - try { - DerInputStream in = new DerInputStream(this.key); - this.x = in.getBigInteger(); - } catch (IOException e) { - InvalidKeyException ike = new InvalidKeyException( - "Error parsing key encoding: " + e.getMessage()); - ike.initCause(e); - throw ike; - } - } - /** * Calculates a hash code value for the object. * Objects that are equal will also have the same hashcode. @@ -328,10 +345,7 @@ public boolean equals(Object obj) { */ @java.io.Serial private Object writeReplace() throws java.io.ObjectStreamException { - encode(); - return new KeyRep(KeyRep.Type.PRIVATE, - getAlgorithm(), - getFormat(), + return new KeyRep(KeyRep.Type.PRIVATE, getAlgorithm(), getFormat(), encodedKey); } @@ -351,11 +365,30 @@ private void readObject(ObjectInputStream stream) if ((key == null) || (key.length == 0)) { throw new InvalidObjectException("key not deserializable"); } - this.key = key.clone(); if ((encodedKey == null) || (encodedKey.length == 0)) { throw new InvalidObjectException( "encoded key not deserializable"); } - this.encodedKey = encodedKey.clone(); + // check if the "encodedKey" value matches the deserialized fields + DHComponents c; + byte[] encodedKeyIntern = encodedKey.clone(); + try { + c = decode(encodedKeyIntern); + } catch (IOException e) { + InvalidObjectException ioe = new InvalidObjectException("Invalid encoding"); + ioe.initCause(e); + throw ioe; + } + if (!Arrays.equals(c.key, key) || !c.x.equals(x) || !c.p.equals(p) + || !c.g.equals(g) || c.l != l) { + throw new InvalidObjectException( + "encoded key not matching internal fields"); + } + // zero out external arrays + Arrays.fill(key, (byte)0x00); + Arrays.fill(encodedKey, (byte)0x00); + // use self-created internal copies + this.key = c.key; + this.encodedKey = encodedKeyIntern; } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java index 622aa2a512d98..3cbb49be16282 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package com.sun.crypto.provider; import java.io.*; +import java.util.Arrays; import java.util.Objects; import java.math.BigInteger; import java.security.KeyRep; @@ -71,6 +72,116 @@ final class DHPublicKey implements PublicKey, static ObjectIdentifier DH_OID = ObjectIdentifier.of(KnownOIDs.DiffieHellman); + private static class DHComponents { + final BigInteger y; + final BigInteger p; + final BigInteger g; + final int l; + final byte[] key; + + DHComponents(BigInteger y, BigInteger p, BigInteger g, int l, + byte[] key) { + this.y = y; + this.p = p; + this.g = g; + this.l = l; + this.key = key; + } + } + + // parses the specified encoding into a DHComponents object + private static DHComponents decode(byte[] encodedKey) + throws IOException { + DerValue val = null; + + try { + val = new DerValue(encodedKey); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid key format"); + } + + // algorithm identifier + DerValue algid = val.data.getDerValue(); + if (algid.tag != DerValue.tag_Sequence) { + throw new IOException("AlgId is not a SEQUENCE"); + } + DerInputStream derInStream = algid.toDerInputStream(); + ObjectIdentifier oid = derInStream.getOID(); + if (oid == null) { + throw new IOException("Null OID"); + } + if (derInStream.available() == 0) { + throw new IOException("Parameters missing"); + } + + // parse the parameters + DerValue params = derInStream.getDerValue(); + if (params.tag == DerValue.tag_Null) { + throw new IOException("Null parameters"); + } + if (params.tag != DerValue.tag_Sequence) { + throw new IOException("Parameters not a SEQUENCE"); + } + params.data.reset(); + + BigInteger p = params.data.getBigInteger(); + BigInteger g = params.data.getBigInteger(); + // Private-value length is OPTIONAL + int l = (params.data.available() != 0 ? params.data.getInteger() : + 0); + if (params.data.available() != 0) { + throw new IOException("Extra parameter data"); + } + + // publickey + byte[] key = val.data.getBitString(); + DerInputStream in = new DerInputStream(key); + BigInteger y = in.getBigInteger(); + + if (val.data.available() != 0) { + throw new IOException("Excess key data"); + } + return new DHComponents(y, p, g, l, key); + } catch (NumberFormatException e) { + throw new IOException("Error parsing key encoding", e); + } + } + + // generates the ASN.1 encoding + private static byte[] encode(BigInteger p, BigInteger g, int l, + byte[] key) throws IOException { + DerOutputStream algid = new DerOutputStream(); + + // store oid in algid + algid.putOID(DH_OID); + + // encode parameters + DerOutputStream params = new DerOutputStream(); + params.putInteger(p); + params.putInteger(g); + if (l != 0) { + params.putInteger(l); + } + + // wrap parameters into SEQUENCE + DerValue paramSequence = new DerValue(DerValue.tag_Sequence, + params.toByteArray()); + // store parameter SEQUENCE in algid + algid.putDerValue(paramSequence); + + // wrap algid into SEQUENCE, and store it in key encoding + DerOutputStream tmpDerKey = new DerOutputStream(); + tmpDerKey.write(DerValue.tag_Sequence, algid); + + // store key data + tmpDerKey.putBitString(key); + + // wrap algid and key into SEQUENCE + DerOutputStream derKey = new DerOutputStream(); + derKey.write(DerValue.tag_Sequence, tmpDerKey); + return derKey.toByteArray(); + } + /** * Make a DH public key out of a public value y, a prime * modulus p, and a base generator g. @@ -106,7 +217,7 @@ final class DHPublicKey implements PublicKey, try { this.key = new DerValue(DerValue.tag_Integer, this.y.toByteArray()).toByteArray(); - this.encodedKey = getEncoded(); + this.encodedKey = encode(p, g, l, key); } catch (IOException e) { throw new ProviderException("Cannot produce ASN.1 encoding", e); } @@ -121,63 +232,19 @@ final class DHPublicKey implements PublicKey, * a Diffie-Hellman public key */ DHPublicKey(byte[] encodedKey) throws InvalidKeyException { - InputStream inStream = new ByteArrayInputStream(encodedKey); - try { - DerValue derKeyVal = new DerValue(inStream); - if (derKeyVal.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException ("Invalid key format"); - } - - /* - * Parse the algorithm identifier - */ - DerValue algid = derKeyVal.data.getDerValue(); - if (algid.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("AlgId is not a SEQUENCE"); - } - DerInputStream derInStream = algid.toDerInputStream(); - ObjectIdentifier oid = derInStream.getOID(); - if (oid == null) { - throw new InvalidKeyException("Null OID"); - } - if (derInStream.available() == 0) { - throw new InvalidKeyException("Parameters missing"); - } - - /* - * Parse the parameters - */ - DerValue params = derInStream.getDerValue(); - if (params.tag == DerValue.tag_Null) { - throw new InvalidKeyException("Null parameters"); - } - if (params.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("Parameters not a SEQUENCE"); - } - params.data.reset(); - this.p = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); - // Private-value length is OPTIONAL - if (params.data.available() != 0) { - this.l = params.data.getInteger(); - } - if (params.data.available() != 0) { - throw new InvalidKeyException("Extra parameter data"); - } - - /* - * Parse the key - */ - this.key = derKeyVal.data.getBitString(); - parseKeyBits(); - if (derKeyVal.data.available() != 0) { - throw new InvalidKeyException("Excess key data"); - } + this.encodedKey = encodedKey.clone(); - this.encodedKey = encodedKey.clone(); - } catch (IOException | NumberFormatException e) { - throw new InvalidKeyException("Error parsing key encoding", e); + DHComponents dc; + try { + dc = decode(this.encodedKey); + } catch (IOException e) { + throw new InvalidKeyException("Invalid encoding", e); } + this.y = dc.y; + this.p = dc.p; + this.g = dc.g; + this.l = dc.l; + this.key = dc.key; } /** @@ -198,41 +265,6 @@ public String getAlgorithm() { * Get the encoding of the key. */ public synchronized byte[] getEncoded() { - if (this.encodedKey == null) { - try { - DerOutputStream algid = new DerOutputStream(); - - // store oid in algid - algid.putOID(DH_OID); - - // encode parameters - DerOutputStream params = new DerOutputStream(); - params.putInteger(this.p); - params.putInteger(this.g); - if (this.l != 0) { - params.putInteger(this.l); - } - // wrap parameters into SEQUENCE - DerValue paramSequence = new DerValue(DerValue.tag_Sequence, - params.toByteArray()); - // store parameter SEQUENCE in algid - algid.putDerValue(paramSequence); - - // wrap algid into SEQUENCE, and store it in key encoding - DerOutputStream tmpDerKey = new DerOutputStream(); - tmpDerKey.write(DerValue.tag_Sequence, algid); - - // store key data - tmpDerKey.putBitString(this.key); - - // wrap algid and key into SEQUENCE - DerOutputStream derKey = new DerOutputStream(); - derKey.write(DerValue.tag_Sequence, tmpDerKey); - this.encodedKey = derKey.toByteArray(); - } catch (IOException e) { - return null; - } - } return this.encodedKey.clone(); } @@ -269,19 +301,10 @@ public String toString() { + Debug.toHexString(this.p) + LINE_SEP + "g:" + LINE_SEP + Debug.toHexString(this.g)); - if (this.l != 0) + if (this.l != 0) { sb.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l); - return sb.toString(); - } - - private void parseKeyBits() throws InvalidKeyException { - try { - DerInputStream in = new DerInputStream(this.key); - this.y = in.getBigInteger(); - } catch (IOException e) { - throw new InvalidKeyException( - "Error parsing key encoding: " + e.toString()); } + return sb.toString(); } /** @@ -320,7 +343,7 @@ private Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(), - getEncoded()); + encodedKey); } /** @@ -339,11 +362,30 @@ private void readObject(ObjectInputStream stream) if ((key == null) || (key.length == 0)) { throw new InvalidObjectException("key not deserializable"); } - this.key = key.clone(); if ((encodedKey == null) || (encodedKey.length == 0)) { throw new InvalidObjectException( "encoded key not deserializable"); } - this.encodedKey = encodedKey.clone(); + // check if the "encodedKey" value matches the deserialized fields + DHComponents c; + byte[] encodedKeyIntern = encodedKey.clone(); + try { + c = decode(encodedKeyIntern); + } catch (IOException e) { + InvalidObjectException ioe = new InvalidObjectException("Invalid encoding"); + ioe.initCause(e); + throw ioe; + } + if (!Arrays.equals(c.key, key) || !c.y.equals(y) || !c.p.equals(p) + || !c.g.equals(g) || c.l != l) { + throw new InvalidObjectException( + "encoded key not matching internal fields"); + } + // zero out external arrays + Arrays.fill(key, (byte)0x00); + Arrays.fill(encodedKey, (byte)0x00); + // use self-created internal copies + this.key = c.key; + this.encodedKey = encodedKeyIntern; } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.java index be685a5c19520..30a4a68074ce5 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/KeyWrapCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -123,6 +123,25 @@ public AES256_KWP_NoPadding() { } } + // validate the key algorithm/encoding and then returns the key bytes + // which callers should erase after use + private static byte[] checkKey(Key key, int fixedKeySize) + throws InvalidKeyException { + + byte[] keyBytes = key.getEncoded(); + if (keyBytes == null) { + throw new InvalidKeyException("Null key"); + } + int keyLen = keyBytes.length; + if (!key.getAlgorithm().equalsIgnoreCase("AES") || + !AESCrypt.isKeySizeValid(keyLen) || + (fixedKeySize != -1 && fixedKeySize != keyLen)) { + throw new InvalidKeyException("Invalid key length: " + + keyLen + " bytes"); + } + return keyBytes; + } + // store the specified bytes, e.g. in[inOfs...(inOfs+inLen-1)] into // 'dataBuf' starting at 'dataIdx'. // NOTE: if 'in' is null, this method will ensure that 'dataBuf' has enough @@ -294,10 +313,8 @@ protected byte[] engineGetIV() { // actual impl for various engineInit(...) methods private void implInit(int opmode, Key key, byte[] iv, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - byte[] keyBytes = key.getEncoded(); - if (keyBytes == null) { - throw new InvalidKeyException("Null key"); - } + byte[] keyBytes = checkKey(key, fixedKeySize); + this.opmode = opmode; boolean decrypting = (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE); @@ -658,21 +675,11 @@ protected AlgorithmParameters engineGetParameters() { * @exception InvalidKeyException if key is invalid. */ protected int engineGetKeySize(Key key) throws InvalidKeyException { - byte[] encoded = key.getEncoded(); - if (encoded == null) { - throw new InvalidKeyException("Cannot decide key length"); - } + byte[] keyBytes = checkKey(key, fixedKeySize); + // only need length; erase immediately + Arrays.fill(keyBytes, (byte) 0); + return Math.multiplyExact(keyBytes.length, 8); - // only need length - Arrays.fill(encoded, (byte) 0); - int keyLen = encoded.length; - if (!key.getAlgorithm().equalsIgnoreCase("AES") || - !AESCrypt.isKeySizeValid(keyLen) || - (fixedKeySize != -1 && fixedKeySize != keyLen)) { - throw new InvalidKeyException("Invalid key length: " + - keyLen + " bytes"); - } - return Math.multiplyExact(keyLen, 8); } /** diff --git a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java index 09ab8143bdd1e..3e9ccc352282d 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -196,22 +196,24 @@ public byte[] getEncoded() { return key.clone(); } - /** - * Restores the state of this object from the stream. - * - * @param stream the {@code ObjectInputStream} from which data is read - * @throws IOException if an I/O error occurs - * @throws ClassNotFoundException if a serialized class cannot be loaded - */ - @java.io.Serial - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - if ((key == null) || (key.length == 0)) { - throw new InvalidObjectException("TlsMasterSecretKey is null"); - } - key = key.clone(); - } - } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + if (key == null || key.length == 0) { + throw new InvalidObjectException("TlsMasterSecretKey is null"); + } + byte[] temp = key; + this.key = temp.clone(); + Arrays.fill(temp, (byte)0); + } + } } diff --git a/src/java.base/share/classes/java/lang/ProcessBuilder.java b/src/java.base/share/classes/java/lang/ProcessBuilder.java index 1bbbec2a8476b..396b87f4c545a 100644 --- a/src/java.base/share/classes/java/lang/ProcessBuilder.java +++ b/src/java.base/share/classes/java/lang/ProcessBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1296,6 +1296,10 @@ public static List startPipeline(List builders) throws redirects[1] = new RedirectPipeImpl(); // placeholder for new output } processes.add(builder.start(redirects)); + if (prevOutput instanceof RedirectPipeImpl redir) { + // Wrap the fd so it can be closed + new Process.PipeInputStream(redir.getFd()).close(); + } prevOutput = redirects[1]; } } catch (Exception ex) { diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index c77a626493ff9..d1c2b8c93cfa2 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -726,6 +726,9 @@ public static native void arraycopy(Object src, int srcPos, * Java Runtime Environment specification version, whose value is * the {@linkplain Runtime.Version#feature feature} element of the * {@linkplain Runtime#version() runtime version} + * {@systemProperty java.specification.maintenance.version} + * Java Runtime Environment specification maintenance version, + * may be interpreted as a positive integer (optional, see below) * {@systemProperty java.specification.vendor} * Java Runtime Environment specification vendor * {@systemProperty java.specification.name} @@ -765,6 +768,16 @@ public static native void arraycopy(Object src, int srcPos, * * *

    + * The {@code java.specification.maintenance.version} property is + * defined if the specification implemented by this runtime at the + * time of its construction had undergone a maintenance + * release. When defined, its value identifies that + * maintenance release. To indicate the first maintenance release + * this property will have the value {@code "1"}, to indicate the + * second maintenance release this property will have the value + * {@code "2"}, and so on. + *

    * Multiple paths in a system property value are separated by the path * separator character of the platform. *

    diff --git a/src/java.base/share/classes/java/lang/ThreadLocal.java b/src/java.base/share/classes/java/lang/ThreadLocal.java index ae696768395e8..848e3721dcfe3 100644 --- a/src/java.base/share/classes/java/lang/ThreadLocal.java +++ b/src/java.base/share/classes/java/lang/ThreadLocal.java @@ -90,7 +90,7 @@ public class ThreadLocal { * The next hash code to be given out. Updated atomically. Starts at * zero. */ - private static AtomicInteger nextHashCode = + private static final AtomicInteger nextHashCode = new AtomicInteger(); /** diff --git a/src/java.base/share/classes/java/lang/VersionProps.java.template b/src/java.base/share/classes/java/lang/VersionProps.java.template index 6037c61b3bea1..33066cea91263 100644 --- a/src/java.base/share/classes/java/lang/VersionProps.java.template +++ b/src/java.base/share/classes/java/lang/VersionProps.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -109,6 +109,7 @@ class VersionProps { props.put("java.class.version", CLASSFILE_MAJOR_MINOR); props.put("java.specification.version", VERSION_SPECIFICATION); + props.put("java.specification.maintenance.version", "1"); props.put("java.specification.name", "Java Platform API Specification"); props.put("java.specification.vendor", "Oracle Corporation"); diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandle.java b/src/java.base/share/classes/java/lang/invoke/MethodHandle.java index c2e34a62e0998..a5341e17fce90 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1764,8 +1764,7 @@ void updateForm(Function updater) { if (oldForm != newForm) { assert (newForm.customized == null || newForm.customized == this); newForm.prepare(); // as in MethodHandle. - UNSAFE.putReference(this, FORM_OFFSET, newForm); - UNSAFE.fullFence(); + UNSAFE.putReferenceRelease(this, FORM_OFFSET, newForm); // properly publish newForm } } finally { updateInProgress = false; diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandles.java b/src/java.base/share/classes/java/lang/invoke/VarHandles.java index fb86bfab0e8c7..97d53c7bbb033 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandles.java @@ -38,8 +38,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.stream.Stream; import static java.lang.invoke.MethodHandleStatics.UNSAFE; @@ -50,13 +48,6 @@ final class VarHandles { - static ClassValue> ADDRESS_FACTORIES = new ClassValue<>() { - @Override - protected ConcurrentMap computeValue(Class type) { - return new ConcurrentHashMap<>(); - } - }; - static VarHandle makeFieldHandle(MemberName f, Class refc, Class type, boolean isWriteAllowedOnFinalFields) { if (!f.isStatic()) { long foffset = MethodHandleNatives.objectFieldOffset(f); diff --git a/src/java.base/share/classes/java/lang/reflect/Constructor.java b/src/java.base/share/classes/java/lang/reflect/Constructor.java index eec4512b6320e..e06750c98d68a 100644 --- a/src/java.base/share/classes/java/lang/reflect/Constructor.java +++ b/src/java.base/share/classes/java/lang/reflect/Constructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import sun.reflect.annotation.TypeAnnotation; import sun.reflect.annotation.TypeAnnotationParser; import sun.reflect.generics.repository.ConstructorRepository; +import sun.reflect.generics.repository.GenericDeclRepository; import sun.reflect.generics.factory.CoreReflectionFactory; import sun.reflect.generics.factory.GenericsFactory; import sun.reflect.generics.scope.ConstructorScope; @@ -241,7 +242,7 @@ public TypeVariable>[] getTypeParameters() { if (getSignature() != null) { return (TypeVariable>[])getGenericInfo().getTypeParameters(); } else - return (TypeVariable>[])new TypeVariable[0]; + return (TypeVariable>[])GenericDeclRepository.EMPTY_TYPE_VARS; } diff --git a/src/java.base/share/classes/java/lang/reflect/Method.java b/src/java.base/share/classes/java/lang/reflect/Method.java index dedf2d5dd91fd..e471e6e5f07c1 100644 --- a/src/java.base/share/classes/java/lang/reflect/Method.java +++ b/src/java.base/share/classes/java/lang/reflect/Method.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import jdk.internal.vm.annotation.Stable; import sun.reflect.annotation.ExceptionProxy; import sun.reflect.annotation.TypeNotPresentExceptionProxy; +import sun.reflect.generics.repository.GenericDeclRepository; import sun.reflect.generics.repository.MethodRepository; import sun.reflect.generics.factory.CoreReflectionFactory; import sun.reflect.generics.factory.GenericsFactory; @@ -253,7 +254,7 @@ public TypeVariable[] getTypeParameters() { if (getGenericSignature() != null) return (TypeVariable[])getGenericInfo().getTypeParameters(); else - return (TypeVariable[])new TypeVariable[0]; + return (TypeVariable[])GenericDeclRepository.EMPTY_TYPE_VARS; } /** diff --git a/src/java.base/share/classes/java/net/doc-files/net-properties.html b/src/java.base/share/classes/java/net/doc-files/net-properties.html index b7ed2f9924fa3..2c10d18a46e53 100644 --- a/src/java.base/share/classes/java/net/doc-files/net-properties.html +++ b/src/java.base/share/classes/java/net/doc-files/net-properties.html @@ -240,6 +240,15 @@

    Misc HTTP URL stream protocol handler properties

    The channel binding tokens generated are of the type "tls-server-end-point" as defined in RFC 5929.

    + +
  • {@systemProperty jdk.http.maxHeaderSize} (default: 393216 or 384kB)
    + This is the maximum header field section size that a client is prepared to accept. + This is computed as the sum of the size of the uncompressed header name, plus + the size of the uncompressed header value, plus an overhead of 32 bytes for + each field section line. If a peer sends a field section that exceeds this + size a {@link java.net.ProtocolException ProtocolException} will be raised. + This applies to all versions of the HTTP protocol. A value of zero or a negative + value means no limit. If left unspecified, the default value is 393216 bytes.

    All these properties are checked only once at startup.

    diff --git a/src/java.base/share/classes/java/nio/charset/Charset.java b/src/java.base/share/classes/java/nio/charset/Charset.java index 0ac18a23ba84c..2b19421220976 100644 --- a/src/java.base/share/classes/java/nio/charset/Charset.java +++ b/src/java.base/share/classes/java/nio/charset/Charset.java @@ -41,7 +41,6 @@ import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; -import java.util.Objects; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.Set; @@ -636,7 +635,12 @@ public static Charset defaultCharset() { * If the canonical name or any of the aliases are illegal */ protected Charset(String canonicalName, String[] aliases) { - String[] as = Objects.requireNonNullElse(aliases, zeroAliases); + String[] as = + aliases == null ? + zeroAliases : + VM.isSystemDomainLoader(getClass().getClassLoader()) ? + aliases : + Arrays.copyOf(aliases, aliases.length); // Skip checks for the standard, built-in Charsets we always load // during initialization. diff --git a/src/java.base/share/classes/java/nio/file/Files.java b/src/java.base/share/classes/java/nio/file/Files.java index 6270f00be2641..dec80152cc653 100644 --- a/src/java.base/share/classes/java/nio/file/Files.java +++ b/src/java.base/share/classes/java/nio/file/Files.java @@ -806,7 +806,7 @@ private static void createAndCheckIsDirectory(Path dir, try { createDirectory(dir, attrs); } catch (FileAlreadyExistsException x) { - if (!isDirectory(dir, LinkOption.NOFOLLOW_LINKS)) + if (!isDirectory(dir)) throw x; } } diff --git a/src/java.base/share/classes/java/security/MessageDigestSpi.java b/src/java.base/share/classes/java/security/MessageDigestSpi.java index 17bd34e507c2c..c99abf79ecdbd 100644 --- a/src/java.base/share/classes/java/security/MessageDigestSpi.java +++ b/src/java.base/share/classes/java/security/MessageDigestSpi.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -204,7 +204,15 @@ protected int engineDigest(byte[] buf, int offset, int len) */ public Object clone() throws CloneNotSupportedException { if (this instanceof Cloneable) { - return super.clone(); + MessageDigestSpi o = (MessageDigestSpi)super.clone(); + if (o.tempArray != null) { + // New byte arrays are allocated when the ByteBuffer argument + // to engineUpdate is not backed by a byte array. + // Here, the newly allocated byte array must also be cloned + // to prevent threads from sharing the same memory. + o.tempArray = tempArray.clone(); + } + return o; } else { throw new CloneNotSupportedException(); } diff --git a/src/java.base/share/classes/java/security/Permissions.java b/src/java.base/share/classes/java/security/Permissions.java index 1c489b6b26676..06293723116d0 100644 --- a/src/java.base/share/classes/java/security/Permissions.java +++ b/src/java.base/share/classes/java/security/Permissions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -403,6 +403,11 @@ private void readObject(ObjectInputStream in) throws IOException, @SuppressWarnings("unchecked") Hashtable, PermissionCollection> perms = (Hashtable, PermissionCollection>)gfields.get("perms", null); + + if (perms == null) { + throw new InvalidObjectException("perms can't be null"); + } + permsMap = new ConcurrentHashMap<>(perms.size()*2); permsMap.putAll(perms); diff --git a/src/java.base/share/classes/java/security/Provider.java b/src/java.base/share/classes/java/security/Provider.java index af8ebeeda5784..72ddb41257896 100644 --- a/src/java.base/share/classes/java/security/Provider.java +++ b/src/java.base/share/classes/java/security/Provider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,9 @@ import jdk.internal.event.SecurityProviderServiceEvent; +import javax.security.auth.login.Configuration; import java.io.*; +import java.security.cert.CertStoreParameters; import java.util.*; import static java.util.Locale.ENGLISH; import java.lang.ref.*; @@ -193,6 +195,8 @@ protected Provider(String name, double version, String info) { this.versionStr = Double.toString(version); this.info = info; this.serviceMap = new ConcurrentHashMap<>(); + this.legacyMap = new ConcurrentHashMap<>(); + this.prngAlgos = new LinkedHashSet(6); putId(); initialized = true; } @@ -231,6 +235,8 @@ protected Provider(String name, String versionStr, String info) { this.version = parseVersionStr(versionStr); this.info = info; this.serviceMap = new ConcurrentHashMap<>(); + this.legacyMap = new ConcurrentHashMap<>(); + this.prngAlgos = new LinkedHashSet(6); putId(); initialized = true; } @@ -574,7 +580,6 @@ public synchronized boolean remove(Object key, Object value) { public synchronized boolean replace(Object key, Object oldValue, Object newValue) { check("putProviderProperty." + name); - if (debug != null) { debug.println("Replace " + name + " provider property " + key); } @@ -600,7 +605,6 @@ public synchronized boolean replace(Object key, Object oldValue, @Override public synchronized Object replace(Object key, Object value) { check("putProviderProperty." + name); - if (debug != null) { debug.println("Replace " + name + " provider property " + key); } @@ -629,7 +633,6 @@ public synchronized Object replace(Object key, Object value) { public synchronized void replaceAll(BiFunction function) { check("putProviderProperty." + name); - if (debug != null) { debug.println("ReplaceAll " + name + " provider property "); } @@ -659,7 +662,6 @@ public synchronized Object compute(Object key, BiFunction remappingFunction) { check("putProviderProperty." + name); check("removeProviderProperty." + name); - if (debug != null) { debug.println("Compute " + name + " provider property " + key); } @@ -686,11 +688,10 @@ public synchronized Object compute(Object key, BiFunction mappingFunction) { + public synchronized Object computeIfAbsent(Object key, + Function mappingFunction) { check("putProviderProperty." + name); check("removeProviderProperty." + name); - if (debug != null) { debug.println("ComputeIfAbsent " + name + " provider property " + key); @@ -716,11 +717,11 @@ public synchronized Object computeIfAbsent(Object key, Function remappingFunction) { + public synchronized Object computeIfPresent(Object key, + BiFunction + remappingFunction) { check("putProviderProperty." + name); check("removeProviderProperty." + name); - if (debug != null) { debug.println("ComputeIfPresent " + name + " provider property " + key); @@ -749,11 +750,11 @@ public synchronized Object computeIfPresent(Object key, BiFunction remappingFunction) { + public synchronized Object merge(Object key, Object value, + BiFunction + remappingFunction) { check("putProviderProperty." + name); check("removeProviderProperty." + name); - if (debug != null) { debug.println("Merge " + name + " provider property " + key); } @@ -779,7 +780,8 @@ public synchronized Object getOrDefault(Object key, Object defaultValue) { * @since 1.8 */ @Override - public synchronized void forEach(BiConsumer action) { + public synchronized void forEach(BiConsumer + action) { checkInitialized(); super.forEach(action); } @@ -819,14 +821,11 @@ private void check(String directive) { } } - // legacy properties changed since last call to any services method? - private transient boolean legacyChanged; + // legacyMap changed since last call to getServices() + private transient volatile boolean legacyChanged; // serviceMap changed since last call to getServices() private volatile transient boolean servicesChanged; - // Map used to keep track of legacy registration - private transient Map legacyStrings; - // Map // used for services added via putService(), initialized on demand private transient Map serviceMap; @@ -834,6 +833,9 @@ private void check(String directive) { // For backward compatibility, the registration ordering of // SecureRandom (RNG) algorithms needs to be preserved for // "new SecureRandom()" calls when this provider is used + // NOTE: may need extra mechanism for providers to indicate their + // preferred ordering of SecureRandom algorithms since registration + // ordering info is lost once serialized private transient Set prngAlgos; // Map @@ -842,7 +844,7 @@ private void check(String directive) { // Set // Unmodifiable set of all services. Initialized on demand. - private transient Set serviceSet; + private transient volatile Set serviceSet; // register the id attributes for this provider // this is to ensure that equals() and hashCode() do not incorrectly @@ -874,6 +876,7 @@ private void readObject(ObjectInputStream in) for (Map.Entry entry : super.entrySet()) { copy.put(entry.getKey(), entry.getValue()); } + defaults = null; in.defaultReadObject(); if (this.versionStr == null) { @@ -884,23 +887,22 @@ private void readObject(ObjectInputStream in) this.version = parseVersionStr(this.versionStr); } this.serviceMap = new ConcurrentHashMap<>(); + this.legacyMap = new ConcurrentHashMap<>(); + this.prngAlgos = new LinkedHashSet(6); implClear(); initialized = true; putAll(copy); } - // check whether to update 'legacyString' with the specified key - private boolean checkLegacy(Object key) { - String keyString = (String)key; - if (keyString.startsWith("Provider.")) { + // returns false if no update necessary, i.e. key isn't String or + // is String but it's provider-related (name/version/info/className) + private static boolean checkLegacy(Object key) { + if (key instanceof String && ((String)key).startsWith("Provider.")) { + // ignore provider related updates return false; + } else { + return true; } - - legacyChanged = true; - if (legacyStrings == null) { - legacyStrings = new LinkedHashMap<>(); - } - return true; } /** @@ -915,149 +917,161 @@ private void implPutAll(Map t) { } private Object implRemove(Object key) { - if (key instanceof String) { - if (!checkLegacy(key)) { - return null; - } - legacyStrings.remove((String)key); + if (!checkLegacy(key)) return null; + + Object o = super.remove(key); + if (o instanceof String so && key instanceof String sk) { + parseLegacy(sk, so, OPType.REMOVE); } - return super.remove(key); + return o; } private boolean implRemove(Object key, Object value) { - if (key instanceof String && value instanceof String) { - if (!checkLegacy(key)) { - return false; - } - legacyStrings.remove((String)key, (String)value); + if (!checkLegacy(key)) return false; + + boolean result = super.remove(key, value); + if (result && key instanceof String sk && value instanceof String sv) { + parseLegacy(sk, sv, OPType.REMOVE); } - return super.remove(key, value); + return result; } private boolean implReplace(Object key, Object oldValue, Object newValue) { - if ((key instanceof String) && (oldValue instanceof String) && - (newValue instanceof String)) { - if (!checkLegacy(key)) { - return false; + if (!checkLegacy(key)) return false; + + boolean result = super.replace(key, oldValue, newValue); + if (result && key instanceof String sk) { + if (newValue instanceof String sv) { + parseLegacy(sk, sv, OPType.ADD); + } else if (oldValue instanceof String sv) { + parseLegacy(sk, sv, OPType.REMOVE); } - legacyStrings.replace((String)key, (String)oldValue, - (String)newValue); } - return super.replace(key, oldValue, newValue); + return result; } private Object implReplace(Object key, Object value) { - if ((key instanceof String) && (value instanceof String)) { - if (!checkLegacy(key)) { - return null; + if (!checkLegacy(key)) return null; + + Object o = super.replace(key, value); + if (key instanceof String sk) { + if (o instanceof String so) { + if (value instanceof String sv) { + parseLegacy(sk, sv, OPType.ADD); + } else { + parseLegacy(sk, so, OPType.REMOVE); + } } - legacyStrings.replace((String)key, (String)value); } - return super.replace(key, value); + return o; } @SuppressWarnings("unchecked") // Function must actually operate over strings private void implReplaceAll(BiFunction function) { + + super.replaceAll(function); + // clear out all existing mappings and start fresh + legacyMap.clear(); legacyChanged = true; - if (legacyStrings == null) { - legacyStrings = new LinkedHashMap<>(); - } else { - legacyStrings.replaceAll((BiFunction) function); + for (Map.Entry entry : super.entrySet()) { + Object key = entry.getKey(); + Object value = entry.getValue(); + if ((key instanceof String sk) && (value instanceof String sv)) { + if (!checkLegacy(sk)) { + continue; + } + parseLegacy(sk, sv, OPType.ADD); + } } - super.replaceAll(function); } @SuppressWarnings("unchecked") // Function must actually operate over strings private Object implMerge(Object key, Object value, BiFunction remappingFunction) { - if ((key instanceof String) && (value instanceof String)) { - if (!checkLegacy(key)) { - return null; + if (!checkLegacy(key)) return null; + + Object o = super.merge(key, value, remappingFunction); + if (key instanceof String sk) { + if (o == null) { + parseLegacy(sk, null, OPType.REMOVE); + } else if (o instanceof String so) { + parseLegacy(sk, so, OPType.ADD); } - legacyStrings.merge((String)key, (String)value, - (BiFunction) remappingFunction); } - return super.merge(key, value, remappingFunction); + return o; } @SuppressWarnings("unchecked") // Function must actually operate over strings private Object implCompute(Object key, BiFunction remappingFunction) { - if (key instanceof String) { - if (!checkLegacy(key)) { - return null; + + if (!checkLegacy(key)) return null; + + Object o = super.compute(key, remappingFunction); + if (key instanceof String sk) { + if (o == null) { + parseLegacy(sk, null, OPType.REMOVE); + } else if (o instanceof String so) { + parseLegacy(sk, so, OPType.ADD); } - legacyStrings.compute((String) key, - (BiFunction) remappingFunction); } - return super.compute(key, remappingFunction); + return o; } @SuppressWarnings("unchecked") // Function must actually operate over strings private Object implComputeIfAbsent(Object key, Function mappingFunction) { - if (key instanceof String) { - if (!checkLegacy(key)) { - return null; - } - legacyStrings.computeIfAbsent((String) key, - (Function) - mappingFunction); + if (!checkLegacy(key)) return null; + + Object o = super.computeIfAbsent(key, mappingFunction); + if (o instanceof String so && key instanceof String sk) { + parseLegacy(sk, so, OPType.ADD); } - return super.computeIfAbsent(key, mappingFunction); + return o; } @SuppressWarnings("unchecked") // Function must actually operate over strings private Object implComputeIfPresent(Object key, BiFunction remappingFunction) { - if (key instanceof String) { - if (!checkLegacy(key)) { - return null; - } - legacyStrings.computeIfPresent((String) key, - (BiFunction) remappingFunction); + if (!checkLegacy(key)) return null; + + Object o = super.computeIfPresent(key, remappingFunction); + if (o instanceof String so && key instanceof String sk) { + parseLegacy(sk, so, OPType.ADD); } - return super.computeIfPresent(key, remappingFunction); + return o; } private Object implPut(Object key, Object value) { - if ((key instanceof String) && (value instanceof String)) { - if (!checkLegacy(key)) { - return null; - } - legacyStrings.put((String)key, (String)value); + if (!checkLegacy(key)) return null; + + Object o = super.put(key, value); + if (key instanceof String sk && value instanceof String sv) { + parseLegacy(sk, sv, OPType.ADD); } - return super.put(key, value); + return o; } private Object implPutIfAbsent(Object key, Object value) { - if ((key instanceof String) && (value instanceof String)) { - if (!checkLegacy(key)) { - return null; - } - legacyStrings.putIfAbsent((String)key, (String)value); + if (!checkLegacy(key)) return null; + + Object o = super.putIfAbsent(key, value); + if (o == null && key instanceof String sk && + value instanceof String sv) { + parseLegacy(sk, sv, OPType.ADD); } - return super.putIfAbsent(key, value); + return o; } private void implClear() { - if (legacyStrings != null) { - legacyStrings.clear(); - } - if (legacyMap != null) { - legacyMap.clear(); - } + legacyMap.clear(); serviceMap.clear(); legacyChanged = false; servicesChanged = false; serviceSet = null; - prngAlgos = null; + prngAlgos.clear(); super.clear(); putId(); } @@ -1087,40 +1101,8 @@ public boolean equals(Object obj) { boolean matches(String type, String algorithm) { return (this.type == type) && (this.originalAlgorithm == algorithm); } - } - - /** - * Ensure all the legacy String properties are fully parsed into - * service objects. - */ - private void ensureLegacyParsed() { - if (legacyChanged == false || (legacyStrings == null)) { - return; - } - serviceSet = null; - if (legacyMap == null) { - legacyMap = new ConcurrentHashMap<>(); - } else { - legacyMap.clear(); - } - for (Map.Entry entry : legacyStrings.entrySet()) { - parseLegacyPut(entry.getKey(), entry.getValue()); - } - removeInvalidServices(legacyMap); - legacyChanged = false; - } - - /** - * Remove all invalid services from the Map. Invalid services can only - * occur if the legacy properties are inconsistent or incomplete. - */ - private void removeInvalidServices(Map map) { - for (Iterator> t = - map.entrySet().iterator(); t.hasNext(); ) { - Service s = t.next().getValue(); - if (s.isValid() == false) { - t.remove(); - } + public String toString() { + return type + "." + algorithm; } } @@ -1138,71 +1120,136 @@ private static String[] getTypeAndAlgorithm(String key) { return new String[] {type, alg}; } + // utility method for getting a String with service type and algorithm + private static String getKey(Service s) { + return s.getType() + "." + s.getAlgorithm(); + } + private static final String ALIAS_PREFIX = "Alg.Alias."; private static final String ALIAS_PREFIX_LOWER = "alg.alias."; private static final int ALIAS_LENGTH = ALIAS_PREFIX.length(); - private void parseLegacyPut(String name, String value) { + private static enum OPType { + ADD, REMOVE; + } + + private void parseLegacy(String name, String value, OPType opType) { + // alias if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) { // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1"); // aliasKey ~ MessageDigest.SHA - String stdAlg = value; - String aliasKey = name.substring(ALIAS_LENGTH); - String[] typeAndAlg = getTypeAndAlgorithm(aliasKey); + String aliasKeyStr = name.substring(ALIAS_LENGTH); + String[] typeAndAlg = getTypeAndAlgorithm(aliasKeyStr); if (typeAndAlg == null) { return; } + legacyChanged = true; + Objects.requireNonNull(value, "alias value should map to an alg"); String type = getEngineName(typeAndAlg[0]); String aliasAlg = typeAndAlg[1].intern(); - ServiceKey key = new ServiceKey(type, stdAlg, true); - Service s = legacyMap.get(key); - if (s == null) { - s = new Service(this, type, stdAlg); - legacyMap.put(key, s); + ServiceKey stdKey = new ServiceKey(type, value, true); + Service stdService = legacyMap.get(stdKey); + ServiceKey aliasKey = new ServiceKey(type, aliasAlg, true); + switch (opType) { + case ADD: + // clean up old alias if present + Service prevAliasService = legacyMap.get(aliasKey); + if (prevAliasService != null) { + prevAliasService.removeAlias(aliasAlg); + } + if (stdService == null) { + // add standard mapping in order to add alias + stdService = new Service(this, type, value); + legacyMap.put(stdKey, stdService); + } + stdService.addAlias(aliasAlg); + legacyMap.put(aliasKey, stdService); + break; + case REMOVE: + if (stdService != null) { + stdService.removeAlias(aliasAlg); + } + legacyMap.remove(aliasKey); + break; + default: + throw new AssertionError(); } - legacyMap.put(new ServiceKey(type, aliasAlg, true), s); - s.addAlias(aliasAlg); } else { String[] typeAndAlg = getTypeAndAlgorithm(name); if (typeAndAlg == null) { return; } + legacyChanged = true; int i = typeAndAlg[1].indexOf(' '); + // regular registration if (i == -1) { // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA"); String type = getEngineName(typeAndAlg[0]); String stdAlg = typeAndAlg[1].intern(); - String className = value; - ServiceKey key = new ServiceKey(type, stdAlg, true); - Service s = legacyMap.get(key); - if (s == null) { - s = new Service(this, type, stdAlg); - legacyMap.put(key, s); - } - s.className = className; - - if (type.equals("SecureRandom")) { - updateSecureRandomEntries(true, s.algorithm); + ServiceKey stdKey = new ServiceKey(type, stdAlg, true); + Service stdService = legacyMap.get(stdKey); + switch (opType) { + case ADD: + Objects.requireNonNull(value, + "className can't be null"); + if (stdService == null) { + stdService = new Service(this, type, stdAlg); + legacyMap.put(stdKey, stdService); + } + stdService.className = value; + break; + case REMOVE: + // only remove if value also matches when non-null + if (stdService != null) { + if (value == null) { + legacyMap.remove(stdKey); + } else if (stdService.className.equals(value)) { + legacyMap.remove(stdKey, stdService); + } + // remove all corresponding alias mappings + for (String alias : stdService.getAliases()) { + legacyMap.remove(new ServiceKey(type, alias, + true), stdService); + } + } + break; + default: + throw new AssertionError(); } + checkAndUpdateSecureRandom(type, stdAlg, + (opType != OPType.REMOVE)); } else { // attribute // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software"); - String attributeValue = value; String type = getEngineName(typeAndAlg[0]); - String attributeString = typeAndAlg[1]; - String stdAlg = attributeString.substring(0, i).intern(); - String attributeName = attributeString.substring(i + 1); + String attrString = typeAndAlg[1]; + String stdAlg = attrString.substring(0, i).intern(); + String attrName = attrString.substring(i + 1); // kill additional spaces - while (attributeName.startsWith(" ")) { - attributeName = attributeName.substring(1); + while (attrName.startsWith(" ")) { + attrName = attrName.substring(1); } - attributeName = attributeName.intern(); - ServiceKey key = new ServiceKey(type, stdAlg, true); - Service s = legacyMap.get(key); - if (s == null) { - s = new Service(this, type, stdAlg); - legacyMap.put(key, s); + attrName = attrName.intern(); + ServiceKey stdKey = new ServiceKey(type, stdAlg, true); + Service stdService = legacyMap.get(stdKey); + switch (opType) { + case ADD: + Objects.requireNonNull(value, + "attribute value should not be null"); + + if (stdService == null) { + stdService = new Service(this, type, stdAlg); + legacyMap.put(stdKey, stdService); + } + stdService.addAttribute(attrName, value); + break; + case REMOVE: + if (stdService != null) { + stdService.removeAttribute(attrName, value); + } + break; + default: + throw new AssertionError(); } - s.addAttribute(attributeName, attributeValue); } } } @@ -1229,23 +1276,18 @@ private void parseLegacyPut(String name, String value) { */ public Service getService(String type, String algorithm) { checkInitialized(); - // avoid allocating a new ServiceKey object if possible ServiceKey key = previousKey; if (key.matches(type, algorithm) == false) { key = new ServiceKey(type, algorithm, false); previousKey = key; } - Service s = null; - if (!serviceMap.isEmpty()) { - s = serviceMap.get(key); - } + + Service s = serviceMap.get(key); if (s == null) { - synchronized (this) { - ensureLegacyParsed(); - if (legacyMap != null && !legacyMap.isEmpty()) { - s = legacyMap.get(key); - } + s = legacyMap.get(key); + if (s != null && !s.isValid()) { + legacyMap.remove(key, s); } } @@ -1278,22 +1320,25 @@ public Service getService(String type, String algorithm) { * * @since 1.5 */ - public synchronized Set getServices() { + public Set getServices() { checkInitialized(); - if (legacyChanged || servicesChanged) { - serviceSet = null; - } - if (serviceSet == null) { - ensureLegacyParsed(); + if (serviceSet == null || legacyChanged || servicesChanged) { Set set = new LinkedHashSet<>(); if (!serviceMap.isEmpty()) { set.addAll(serviceMap.values()); } - if (legacyMap != null && !legacyMap.isEmpty()) { - set.addAll(legacyMap.values()); + if (!legacyMap.isEmpty()) { + legacyMap.entrySet().forEach(entry -> { + if (!entry.getValue().isValid()) { + legacyMap.remove(entry.getKey(), entry.getValue()); + } else { + set.add(entry.getValue()); + } + }); } serviceSet = Collections.unmodifiableSet(set); servicesChanged = false; + legacyChanged = false; } return serviceSet; } @@ -1350,44 +1395,36 @@ protected void putService(Service s) { servicesChanged = true; synchronized (this) { putPropertyStrings(s); - if (type.equals("SecureRandom")) { - updateSecureRandomEntries(true, s.algorithm); - } + checkAndUpdateSecureRandom(type, algorithm, true); } } - // keep tracks of the registered secure random algos and store them in order - private void updateSecureRandomEntries(boolean doAdd, String s) { - Objects.requireNonNull(s); - if (doAdd) { - if (prngAlgos == null) { - prngAlgos = new LinkedHashSet(); + private void checkAndUpdateSecureRandom(String type, String algo, + boolean doAdd) { + if (type.equalsIgnoreCase("SecureRandom")) { + if (doAdd) { + prngAlgos.add(algo); + } else { + prngAlgos.remove(algo); + } + if (debug != null) { + debug.println((doAdd? "Add":"Remove") + + " SecureRandom algo " + algo); } - prngAlgos.add(s); - } else { - prngAlgos.remove(s); - } - - if (debug != null) { - debug.println((doAdd? "Add":"Remove") + " SecureRandom algo " + s); } } // used by new SecureRandom() to find out the default SecureRandom // service for this provider - synchronized Service getDefaultSecureRandomService() { + Service getDefaultSecureRandomService() { checkInitialized(); - if (legacyChanged) { - prngAlgos = null; - ensureLegacyParsed(); - } - - if (prngAlgos != null && !prngAlgos.isEmpty()) { + if (!prngAlgos.isEmpty()) { + String algo = prngAlgos.iterator().next(); // IMPORTANT: use the Service obj returned by getService(...) call // as providers may override putService(...)/getService(...) and // return their own Service objects - return getService("SecureRandom", prngAlgos.iterator().next()); + return getService("SecureRandom", algo); } return null; @@ -1484,12 +1521,9 @@ private void implRemoveService(Service s) { for (String alias : s.getAliases()) { serviceMap.remove(new ServiceKey(type, alias, false)); } - synchronized (this) { - removePropertyStrings(s); - if (type.equals("SecureRandom")) { - updateSecureRandomEntries(false, s.algorithm); - } - } + + removePropertyStrings(s); + checkAndUpdateSecureRandom(type, algorithm, false); } // Wrapped String that behaves in a case insensitive way for equals/hashCode @@ -1523,29 +1557,20 @@ public String toString() { private static class EngineDescription { final String name; final boolean supportsParameter; - final String constructorParameterClassName; - private volatile Class constructorParameterClass; + final Class constructorParameterClass; - EngineDescription(String name, boolean sp, String paramName) { + EngineDescription(String name, boolean sp, Class constructorParameterClass) { this.name = name; this.supportsParameter = sp; - this.constructorParameterClassName = paramName; - } - Class getConstructorParameterClass() throws ClassNotFoundException { - Class clazz = constructorParameterClass; - if (clazz == null) { - clazz = Class.forName(constructorParameterClassName); - constructorParameterClass = clazz; - } - return clazz; + this.constructorParameterClass = constructorParameterClass; } } // built in knowledge of the engine types shipped as part of the JDK private static final Map knownEngines; - private static void addEngine(String name, boolean sp, String paramName) { - EngineDescription ed = new EngineDescription(name, sp, paramName); + private static void addEngine(String name, boolean sp, Class constructorParameterClass) { + EngineDescription ed = new EngineDescription(name, sp, constructorParameterClass); // also index by canonical name to avoid toLowerCase() for some lookups knownEngines.put(name.toLowerCase(ENGLISH), ed); knownEngines.put(name, ed); @@ -1561,13 +1586,13 @@ private static void addEngine(String name, boolean sp, String paramName) { addEngine("KeyStore", false, null); addEngine("MessageDigest", false, null); addEngine("SecureRandom", false, - "java.security.SecureRandomParameters"); + SecureRandomParameters.class); addEngine("Signature", true, null); addEngine("CertificateFactory", false, null); addEngine("CertPathBuilder", false, null); addEngine("CertPathValidator", false, null); addEngine("CertStore", false, - "java.security.cert.CertStoreParameters"); + CertStoreParameters.class); // JCE addEngine("Cipher", true, null); addEngine("ExemptionMechanism", false, null); @@ -1575,6 +1600,7 @@ private static void addEngine(String name, boolean sp, String paramName) { addEngine("KeyAgreement", true, null); addEngine("KeyGenerator", false, null); addEngine("SecretKeyFactory", false, null); + addEngine("KEM", true, null); // JSSE addEngine("KeyManagerFactory", false, null); addEngine("SSLContext", false, null); @@ -1585,18 +1611,20 @@ private static void addEngine(String name, boolean sp, String paramName) { addEngine("SaslClientFactory", false, null); addEngine("SaslServerFactory", false, null); // POLICY + @SuppressWarnings("removal") + Class policyParams = Policy.Parameters.class; addEngine("Policy", false, - "java.security.Policy$Parameters"); + policyParams); // CONFIGURATION addEngine("Configuration", false, - "javax.security.auth.login.Configuration$Parameters"); + Configuration.Parameters.class); // XML DSig addEngine("XMLSignatureFactory", false, null); addEngine("KeyInfoFactory", false, null); addEngine("TransformService", false, null); // Smart Card I/O addEngine("TerminalFactory", false, - "java.lang.Object"); + Object.class); } // get the "standard" (mixed-case) engine name for arbitary case engine name @@ -1697,6 +1725,13 @@ private void addAlias(String alias) { aliases.add(alias); } + private void removeAlias(String alias) { + if (aliases.isEmpty()) { + return; + } + aliases.remove(alias); + } + void addAttribute(String type, String value) { if (attributes.isEmpty()) { attributes = new HashMap<>(8); @@ -1704,6 +1739,17 @@ void addAttribute(String type, String value) { attributes.put(new UString(type), value); } + void removeAttribute(String type, String value) { + if (attributes.isEmpty()) { + return; + } + if (value == null) { + attributes.remove(new UString(type)); + } else { + attributes.remove(new UString(type), value); + } + } + /** * Construct a new service. * @@ -1850,8 +1896,7 @@ public Object newInstance(Object constructorParameter) ctrParamClz = constructorParameter == null? null : constructorParameter.getClass(); } else { - ctrParamClz = cap.constructorParameterClassName == null? - null : Class.forName(cap.constructorParameterClassName); + ctrParamClz = cap.constructorParameterClass; if (constructorParameter != null) { if (ctrParamClz == null) { throw new InvalidParameterException @@ -1862,7 +1907,7 @@ public Object newInstance(Object constructorParameter) if (ctrParamClz.isAssignableFrom(argClass) == false) { throw new InvalidParameterException ("constructorParameter must be instanceof " - + cap.constructorParameterClassName.replace('$', '.') + + cap.constructorParameterClass.getName().replace('$', '.') + " for engine type " + type); } } diff --git a/src/java.base/share/classes/java/security/SignedObject.java b/src/java.base/share/classes/java/security/SignedObject.java index 0fe6c38370f86..7f97df2cbe9d4 100644 --- a/src/java.base/share/classes/java/security/SignedObject.java +++ b/src/java.base/share/classes/java/security/SignedObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -152,20 +152,20 @@ public final class SignedObject implements Serializable { */ public SignedObject(Serializable object, PrivateKey signingKey, Signature signingEngine) - throws IOException, InvalidKeyException, SignatureException { - // creating a stream pipe-line, from a to b - ByteArrayOutputStream b = new ByteArrayOutputStream(); - ObjectOutput a = new ObjectOutputStream(b); + throws IOException, InvalidKeyException, SignatureException { + // creating a stream pipe-line, from a to b + ByteArrayOutputStream b = new ByteArrayOutputStream(); + ObjectOutput a = new ObjectOutputStream(b); - // write and flush the object content to byte array - a.writeObject(object); - a.flush(); - a.close(); - this.content = b.toByteArray(); - b.close(); + // write and flush the object content to byte array + a.writeObject(object); + a.flush(); + a.close(); + this.content = b.toByteArray(); + b.close(); - // now sign the encapsulated object - this.sign(signingKey, signingEngine); + // now sign the encapsulated object + this.sign(signingKey, signingEngine); } /** @@ -245,12 +245,12 @@ public boolean verify(PublicKey verificationKey, * @throws SignatureException if signing fails. */ private void sign(PrivateKey signingKey, Signature signingEngine) - throws InvalidKeyException, SignatureException { - // initialize the signing engine - signingEngine.initSign(signingKey); - signingEngine.update(this.content.clone()); - this.signature = signingEngine.sign().clone(); - this.thealgorithm = signingEngine.getAlgorithm(); + throws InvalidKeyException, SignatureException { + // initialize the signing engine + signingEngine.initSign(signingKey); + signingEngine.update(this.content.clone()); + this.signature = signingEngine.sign(); + this.thealgorithm = signingEngine.getAlgorithm(); } /** @@ -263,10 +263,16 @@ private void sign(PrivateKey signingKey, Signature signingEngine) */ @Serial private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { - ObjectInputStream.GetField fields = s.readFields(); - content = ((byte[])fields.get("content", null)).clone(); - signature = ((byte[])fields.get("signature", null)).clone(); - thealgorithm = (String)fields.get("thealgorithm", null); + throws IOException, ClassNotFoundException { + ObjectInputStream.GetField fields = s.readFields(); + byte[] c = (byte[]) fields.get("content", null); + byte[] sig = (byte[]) fields.get("signature", null); + String a = (String) fields.get("thealgorithm", null); + if (c == null || sig == null || a == null) { + throw new InvalidObjectException("One or more null fields"); + } + content = c.clone(); + signature = sig.clone(); + thealgorithm = a; } } diff --git a/src/java.base/share/classes/java/security/Timestamp.java b/src/java.base/share/classes/java/security/Timestamp.java index e3994ca05652f..3469a240d04ce 100644 --- a/src/java.base/share/classes/java/security/Timestamp.java +++ b/src/java.base/share/classes/java/security/Timestamp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,7 +74,7 @@ public final class Timestamp implements Serializable { * @throws NullPointerException if timestamp or signerCertPath is null. */ public Timestamp(Date timestamp, CertPath signerCertPath) { - if (timestamp == null || signerCertPath == null) { + if (isNull(timestamp, signerCertPath)) { throw new NullPointerException(); } this.timestamp = new Date(timestamp.getTime()); // clone @@ -161,9 +161,16 @@ public String toString() { */ @java.io.Serial private void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { + throws IOException, ClassNotFoundException { ois.defaultReadObject(); + if (isNull(timestamp, signerCertPath)) { + throw new InvalidObjectException("Invalid null field(s)"); + } myhash = -1; timestamp = new Date(timestamp.getTime()); } + + private static boolean isNull(Date d, CertPath c) { + return (d == null || c == null); + } } diff --git a/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java b/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java index 61d2e7ce1ef87..cc3cb3b086e86 100644 --- a/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java +++ b/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; +import java.io.InvalidObjectException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -203,23 +204,32 @@ private void readObject(ObjectInputStream in) throws IOException, ObjectInputStream.GetField gfields = in.readFields(); // Get permissions - @SuppressWarnings("unchecked") // writeObject writes a Hashtable> // for the permissions key, so this cast is safe, unless the data is corrupt. - Hashtable> permissions = - (Hashtable>) - gfields.get("permissions", null); - perms = new ConcurrentHashMap<>(permissions.size()*2); + try { + @SuppressWarnings("unchecked") + Hashtable> permissions = + (Hashtable>) + gfields.get("permissions", null); + + if (permissions == null) { + throw new InvalidObjectException("Invalid null permissions"); + } - // Convert each entry (Vector) into a List - Set>> set = permissions.entrySet(); - for (Map.Entry> e : set) { - // Convert Vector into ArrayList - Vector vec = e.getValue(); - List list = new CopyOnWriteArrayList<>(vec); + perms = new ConcurrentHashMap<>(permissions.size()*2); - // Add to Hashtable being serialized - perms.put(e.getKey(), list); + // Convert each entry (Vector) into a List + Set>> set = permissions.entrySet(); + for (Map.Entry> e : set) { + // Convert Vector into ArrayList + Vector vec = e.getValue(); + List list = new CopyOnWriteArrayList<>(vec); + + // Add to Hashtable being serialized + perms.put(e.getKey(), list); + } + } catch (ClassCastException cce) { + throw new InvalidObjectException("Invalid type for permissions"); } } } diff --git a/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java b/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java index 607662f39dfd9..65e17ad4176e4 100644 --- a/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java +++ b/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.IOException; +import java.io.InvalidObjectException; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -70,6 +71,13 @@ public class CertificateRevokedException extends CertificateException { private transient Map extensions; + private static boolean isNull(Date revocationDate, + CRLReason reason, X500Principal authority, + Map extensions) { + return (revocationDate == null || reason == null || authority == null + || extensions == null); + } + /** * Constructs a {@code CertificateRevokedException} with * the specified revocation date, reason code, authority name, and map @@ -92,8 +100,7 @@ public class CertificateRevokedException extends CertificateException { */ public CertificateRevokedException(Date revocationDate, CRLReason reason, X500Principal authority, Map extensions) { - if (revocationDate == null || reason == null || authority == null || - extensions == null) { + if (isNull(revocationDate, reason, authority, extensions)) { throw new NullPointerException(); } this.revocationDate = new Date(revocationDate.getTime()); @@ -234,9 +241,6 @@ private void readObject(ObjectInputStream ois) // (revocationDate, reason, authority) ois.defaultReadObject(); - // Defensively copy the revocation date - revocationDate = new Date(revocationDate.getTime()); - // Read in the size (number of mappings) of the extensions map // and create the extensions map int size = ois.readInt(); @@ -247,6 +251,13 @@ private void readObject(ObjectInputStream ois) } else { extensions = new HashMap<>(size > 20 ? 20 : size); } + // make sure all fields are set before checking + if (isNull(revocationDate, reason, authority, extensions)) { + throw new InvalidObjectException("Invalid null field(s)"); + } + + // Defensively copy the revocation date + revocationDate = new Date(revocationDate.getTime()); // Read in the extensions and put the mappings in the extensions map for (int i = 0; i < size; i++) { diff --git a/src/java.base/share/classes/java/security/cert/X509CertSelector.java b/src/java.base/share/classes/java/security/cert/X509CertSelector.java index b838b3f51c2b7..f5448c479a92a 100644 --- a/src/java.base/share/classes/java/security/cert/X509CertSelector.java +++ b/src/java.base/share/classes/java/security/cert/X509CertSelector.java @@ -1970,10 +1970,10 @@ public boolean match(Certificate cert) { } if (debug != null) { - debug.println("X509CertSelector.match(SN: " - + (xcert.getSerialNumber()).toString(16) + "\n Issuer: " - + xcert.getIssuerX500Principal() + "\n Subject: " + xcert.getSubjectX500Principal() - + ")"); + debug.println("X509CertSelector.match(Serial number: " + + Debug.toString(xcert.getSerialNumber()) + + "\n Issuer: " + xcert.getIssuerX500Principal() + "\n Subject: " + + xcert.getSubjectX500Principal() + ")"); } /* match on X509Certificate */ diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java index e304d07241140..5ac88f0cfaa8a 100644 --- a/src/java.base/share/classes/java/text/MessageFormat.java +++ b/src/java.base/share/classes/java/text/MessageFormat.java @@ -41,6 +41,7 @@ import java.io.InvalidObjectException; import java.io.IOException; import java.io.ObjectInputStream; +import java.io.ObjectStreamException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; @@ -983,6 +984,8 @@ public Object[] parse(String source, ParsePosition pos) { maximumArgumentNumber = argumentNumbers[i]; } } + + // Constructors/applyPattern ensure that resultArray.length < MAX_ARGUMENT_INDEX Object[] resultArray = new Object[maximumArgumentNumber + 1]; int patternOffset = 0; @@ -1235,6 +1238,9 @@ protected Object readResolve() throws InvalidObjectException { * @serial */ private int[] argumentNumbers = new int[INITIAL_FORMATS]; + // Implementation limit for ArgumentIndex pattern element. Valid indices must + // be less than this value + private static final int MAX_ARGUMENT_INDEX = 10000; /** * One less than the number of entries in {@code offsets}. Can also be thought of @@ -1459,6 +1465,11 @@ private void makeFormat(int position, int offsetNumber, + argumentNumber); } + if (argumentNumber >= MAX_ARGUMENT_INDEX) { + throw new IllegalArgumentException( + argumentNumber + " exceeds the ArgumentIndex implementation limit"); + } + // resize format information arrays if necessary if (offsetNumber >= formats.length) { int newLength = formats.length * 2; @@ -1606,24 +1617,53 @@ private static final void copyAndFixQuotes(String source, int start, int end, */ @java.io.Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - boolean isValid = maxOffset >= -1 - && formats.length > maxOffset - && offsets.length > maxOffset - && argumentNumbers.length > maxOffset; + ObjectInputStream.GetField fields = in.readFields(); + if (fields.defaulted("argumentNumbers") || fields.defaulted("offsets") + || fields.defaulted("formats") || fields.defaulted("locale") + || fields.defaulted("pattern") || fields.defaulted("maxOffset")){ + throw new InvalidObjectException("Stream has missing data"); + } + + locale = (Locale) fields.get("locale", null); + String patt = (String) fields.get("pattern", null); + int maxOff = fields.get("maxOffset", -2); + int[] argNums = ((int[]) fields.get("argumentNumbers", null)).clone(); + int[] offs = ((int[]) fields.get("offsets", null)).clone(); + Format[] fmts = ((Format[]) fields.get("formats", null)).clone(); + + // Check arrays/maxOffset have correct value/length + boolean isValid = maxOff >= -1 && argNums.length > maxOff + && offs.length > maxOff && fmts.length > maxOff; + + // Check the correctness of arguments and offsets if (isValid) { - int lastOffset = pattern.length() + 1; - for (int i = maxOffset; i >= 0; --i) { - if ((offsets[i] < 0) || (offsets[i] > lastOffset)) { + int lastOffset = patt.length() + 1; + for (int i = maxOff; i >= 0; --i) { + if (argNums[i] < 0 || argNums[i] >= MAX_ARGUMENT_INDEX + || offs[i] < 0 || offs[i] > lastOffset) { isValid = false; break; } else { - lastOffset = offsets[i]; + lastOffset = offs[i]; } } } + if (!isValid) { - throw new InvalidObjectException("Could not reconstruct MessageFormat from corrupt stream."); + throw new InvalidObjectException("Stream has invalid data"); } + maxOffset = maxOff; + pattern = patt; + offsets = offs; + formats = fmts; + argumentNumbers = argNums; + } + + /** + * Serialization without data not supported for this class. + */ + @java.io.Serial + private void readObjectNoData() throws ObjectStreamException { + throw new InvalidObjectException("Deserialized MessageFormat objects need data"); } } diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java index c94b123b26c9d..c8e865db2b469 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java @@ -2240,29 +2240,23 @@ public Object parseObject(String text, ParsePosition pos) { DateTimeParseContext context; try { context = formatter.parseUnresolved0(text, pos); - } catch (IndexOutOfBoundsException ex) { - if (pos.getErrorIndex() < 0) { - pos.setErrorIndex(0); - } - return null; - } - if (context == null) { - if (pos.getErrorIndex() < 0) { - pos.setErrorIndex(0); + if (context == null) { + if (pos.getErrorIndex() < 0) { + pos.setErrorIndex(0); + } + return null; } - return null; - } - try { TemporalAccessor resolved = context.toResolved(formatter.resolverStyle, formatter.resolverFields); if (parseType == null) { return resolved; } return resolved.query(parseType); } catch (RuntimeException ex) { - pos.setErrorIndex(0); + if (pos.getErrorIndex() < 0) { + pos.setErrorIndex(0); + } return null; } } } - } diff --git a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java index 18bdc93205ac8..a722ec3a76037 100644 --- a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java +++ b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java @@ -2891,7 +2891,7 @@ static final class Canceller implements BiConsumer { final Future f; Canceller(Future f) { this.f = f; } public void accept(Object ignore, Throwable ex) { - if (ex == null && f != null && !f.isDone()) + if (f != null && !f.isDone()) f.cancel(false); } } diff --git a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java index 9a18002ebd518..83de94cd61219 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java @@ -1080,13 +1080,18 @@ public ConditionObject() { } private void doSignal(ConditionNode first, boolean all) { while (first != null) { ConditionNode next = first.nextWaiter; + if ((firstWaiter = next) == null) lastWaiter = null; + else + first.nextWaiter = null; // GC assistance + if ((first.getAndUnsetStatus(COND) & COND) != 0) { enqueue(first); if (!all) break; } + first = next; } } diff --git a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index 618ba7b05f38b..7a320b6f4328d 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -1448,13 +1448,18 @@ public ConditionObject() { } private void doSignal(ConditionNode first, boolean all) { while (first != null) { ConditionNode next = first.nextWaiter; + if ((firstWaiter = next) == null) lastWaiter = null; + else + first.nextWaiter = null; // GC assistance + if ((first.getAndUnsetStatus(COND) & COND) != 0) { enqueue(first); if (!all) break; } + first = next; } } diff --git a/src/java.base/share/classes/java/util/regex/Grapheme.java b/src/java.base/share/classes/java/util/regex/Grapheme.java index 550415e35598f..efc92158dfecb 100644 --- a/src/java.base/share/classes/java/util/regex/Grapheme.java +++ b/src/java.base/share/classes/java/util/regex/Grapheme.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -151,10 +151,10 @@ static int nextBoundary(CharSequence src, int off, int limit) { // #tr29: SpacingMark exceptions: The following (which have // General_Category = Spacing_Mark and would otherwise be included) // are specifically excluded - private static boolean isExcludedSpacingMark(int cp) { + static boolean isExcludedSpacingMark(int cp) { return cp == 0x102B || cp == 0x102C || cp == 0x1038 || cp >= 0x1062 && cp <= 0x1064 || - cp >= 0x1062 && cp <= 0x106D || + cp >= 0x1067 && cp <= 0x106D || cp == 0x1083 || cp >= 0x1087 && cp <= 0x108C || cp == 0x108F || @@ -164,7 +164,7 @@ private static boolean isExcludedSpacingMark(int cp) { } @SuppressWarnings("fallthrough") - private static int getType(int cp) { + static int getType(int cp) { if (cp < 0x007F) { // ASCII if (cp < 32) { // Control characters if (cp == 0x000D) diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index 02946e885d605..5b39137bf12bd 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1660,6 +1660,9 @@ private void initCEN(int knownTotal) throws IOException { zerror("invalid END header (bad central directory offset)"); } // read in the CEN and END + if (end.cenlen + ENDHDR >= Integer.MAX_VALUE) { + zerror("invalid END header (central directory size too large)"); + } cen = this.cen = new byte[(int)(end.cenlen + ENDHDR)]; if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) { zerror("read CEN tables failed"); diff --git a/src/java.base/share/classes/javax/crypto/CryptoPolicyParser.java b/src/java.base/share/classes/javax/crypto/CryptoPolicyParser.java index 9cc8bc48e80c8..1b033391fa329 100644 --- a/src/java.base/share/classes/javax/crypto/CryptoPolicyParser.java +++ b/src/java.base/share/classes/javax/crypto/CryptoPolicyParser.java @@ -70,6 +70,7 @@ final class CryptoPolicyParser { // Convenience variables for parsing private StreamTokenizer st; private int lookahead; + private boolean allPermEntryFound = false; /** * Creates a CryptoPolicyParser object. @@ -128,7 +129,7 @@ void read(Reader policy) * The crypto jurisdiction policy must be consistent. The * following hashtable is used for checking consistency. */ - Hashtable> processedPermissions = null; + Hashtable> processedPermissions = new Hashtable<>(); /* * The main parsing loop. The loop is executed once for each entry @@ -191,6 +192,16 @@ private CryptoPermissionEntry parsePermissionEntry( e.cryptoPermission = match("permission type"); if (e.cryptoPermission.equals("javax.crypto.CryptoAllPermission")) { + /* + * This catches while processing the "javax.crypto.CryptoAllPermission" + * entry, but the "processedPermissions" Hashtable already contains + * an entry for "javax.crypto.CryptoPermission". + */ + if (!processedPermissions.isEmpty()) { + throw new ParsingException(st.lineno(), "Inconsistent policy"); + } + allPermEntryFound = true; + // Done with the CryptoAllPermission entry. e.alg = CryptoAllPermission.ALG_NAME; e.maxKeySize = Integer.MAX_VALUE; @@ -498,18 +509,21 @@ private boolean isConsistent(String alg, String exemptionMechanism, String thisExemptionMechanism = exemptionMechanism == null ? "none" : exemptionMechanism; - if (processedPermissions == null) { - processedPermissions = new Hashtable>(); + /* + * This catches while processing a "javax.crypto.CryptoPermission" entry, but + * "javax.crypto.CryptoAllPermission" entry already exists. + */ + if (allPermEntryFound) { + return false; + } + + if (processedPermissions.isEmpty()) { Vector exemptionMechanisms = new Vector<>(1); exemptionMechanisms.addElement(thisExemptionMechanism); processedPermissions.put(alg, exemptionMechanisms); return true; } - if (processedPermissions.containsKey(CryptoAllPermission.ALG_NAME)) { - return false; - } - Vector exemptionMechanisms; if (processedPermissions.containsKey(alg)) { diff --git a/src/java.base/share/classes/javax/crypto/DecapsulateException.java b/src/java.base/share/classes/javax/crypto/DecapsulateException.java new file mode 100644 index 0000000000000..5d27caa5bde49 --- /dev/null +++ b/src/java.base/share/classes/javax/crypto/DecapsulateException.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.crypto; + +import java.security.GeneralSecurityException; + +/** + * An exception that is thrown by the + * {@link javax.crypto.KEM.Decapsulator#decapsulate} method to denote an + * error during decapsulation. + * + * @apiNote This class is defined in Java SE 17 Maintenance Release 1. + * @since 17 + */ +public class DecapsulateException extends GeneralSecurityException { + + @java.io.Serial + private static final long serialVersionUID = 21L; + + /** + * Creates a {@code DecapsulateException} with the specified + * detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + */ + public DecapsulateException(String message) { + super(message); + } + + /** + * Creates a {@code DecapsulateException} with the specified + * detail message and cause. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is permitted, + * and indicates that the cause is nonexistent or unknown.) + */ + public DecapsulateException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/java.base/share/classes/javax/crypto/KEM.java b/src/java.base/share/classes/javax/crypto/KEM.java new file mode 100644 index 0000000000000..708b4f9910766 --- /dev/null +++ b/src/java.base/share/classes/javax/crypto/KEM.java @@ -0,0 +1,751 @@ +/* + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.crypto; + +import sun.security.jca.GetInstance; + +import java.security.*; +import java.security.InvalidAlgorithmParameterException; +import java.security.spec.AlgorithmParameterSpec; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * This class provides the functionality of a Key Encapsulation Mechanism (KEM). + * A KEM can be used to secure symmetric keys using asymmetric or public key + * cryptography between two parties. The sender calls the encapsulate method + * to generate a secret key and a key encapsulation message, and the receiver + * calls the decapsulate method to recover the same secret key from + * the key encapsulation message. + *

    + * The {@code getInstance} method creates a new {@code KEM} object that + * implements the specified algorithm. + *

    + * A {@code KEM} object is immutable. It is safe to call multiple + * {@code newEncapsulator} and {@code newDecapsulator} methods on the + * same {@code KEM} object at the same time. + *

    + * If a provider is not specified in the {@code getInstance} method when + * instantiating a {@code KEM} object, the {@code newEncapsulator} and + * {@code newDecapsulator} methods may return encapsulators or decapsulators + * from different providers. The provider selected is based on the parameters + * passed to the {@code newEncapsulator} or {@code newDecapsulator} methods: + * the private or public key and the optional {@code AlgorithmParameterSpec}. + * The {@link Encapsulator#providerName} and {@link Decapsulator#providerName} + * methods return the name of the selected provider. + *

    + * {@code Encapsulator} and {@code Decapsulator} objects are also immutable. + * It is safe to invoke multiple {@code encapsulate} and {@code decapsulate} + * methods on the same {@code Encapsulator} or {@code Decapsulator} object + * at the same time. Each invocation of {@code encapsulate} will generate a + * new shared secret and key encapsulation message. + *

    + * + * Example: + *

    {@code
    + *    // Receiver side
    + *    var kpg = KeyPairGenerator.getInstance("X25519");
    + *    var kp = kpg.generateKeyPair();
    + *
    + *    // Sender side
    + *    var kem1 = KEM.getInstance("DHKEM");
    + *    var sender = kem1.newEncapsulator(kp.getPublic());
    + *    var encapsulated = sender.encapsulate();
    + *    var k1 = encapsulated.key();
    + *
    + *    // Receiver side
    + *    var kem2 = KEM.getInstance("DHKEM");
    + *    var receiver = kem2.newDecapsulator(kp.getPrivate());
    + *    var k2 = receiver.decapsulate(encapsulated.encapsulation());
    + *
    + *    assert Arrays.equals(k1.getEncoded(), k2.getEncoded());
    + * }
    + * + * @apiNote This class is defined in Java SE 17 Maintenance Release 1. + * @since 17 + */ +public final class KEM { + + /** + * This class specifies the return value of the encapsulate method of + * a Key Encapsulation Mechanism (KEM), which includes the shared secret + * (as a {@code SecretKey}), the key encapsulation message, + * and optional parameters. + *

    + * Note: the key encapsulation message can be also referred to as ciphertext. + * + * @see #newEncapsulator(PublicKey, AlgorithmParameterSpec, SecureRandom) + * @see Encapsulator#encapsulate(int, int, String) + * + * @apiNote This class is defined in Java SE 17 Maintenance Release 1. + * @since 17 + */ + public static final class Encapsulated { + private final SecretKey key; + private final byte[] encapsulation; + private final byte[] params; + + /** + * Constructs an {@code Encapsulated} object. + * + * @param key the shared secret as a key, must not be {@code null}. + * @param encapsulation the key encapsulation message, must not + * be {@code null}. The contents of the array are copied + * to protect against subsequent modification. + * @param params optional parameters, can be {@code null}. + * The contents of the array are copied to protect + * against subsequent modification. + * @throws NullPointerException if {@code key} or {@code encapsulation} + * is {@code null} + */ + public Encapsulated(SecretKey key, byte[] encapsulation, byte[] params) { + Objects.requireNonNull(key); + Objects.requireNonNull(encapsulation); + this.key = key; + this.encapsulation = encapsulation.clone(); + this.params = params == null ? null : params.clone(); + } + + /** + * Returns the {@code SecretKey}. + * + * @return the secret key + */ + public SecretKey key() { + return key; + } + + /** + * Returns the key encapsulation message. + * + * @return the key encapsulation message. A new copy of the byte array + * is returned. + */ + public byte[] encapsulation() { + return encapsulation.clone(); + } + + /** + * Returns the optional parameters in a byte array. + * + * @return the optional parameters in a byte array or {@code null} + * if not specified. A new copy of the byte array is returned. + */ + public byte[] params() { + return params == null ? null : params.clone(); + } + } + + /** + * An encapsulator, generated by {@link #newEncapsulator} on the KEM + * sender side. + *

    + * This class represents the key encapsulation function of a KEM. + * Each invocation of the {@code encapsulate} method generates a + * new secret key and key encapsulation message that is returned + * in an {@link Encapsulated} object. + * + * @apiNote This class is defined in Java SE 17 Maintenance Release 1. + * @since 17 + */ + public static final class Encapsulator { + + private final KEMSpi.EncapsulatorSpi e; + private final Provider p; + + private Encapsulator(KEMSpi.EncapsulatorSpi e, Provider p) { + assert e != null; + assert p != null; + this.e = e; + this.p = p; + } + + /** + * Returns the name of the provider. + * + * @return the name of the provider + */ + public String providerName() { + return p.getName(); + } + + /** + * The key encapsulation function. + *

    + * This method is equivalent to + * {@code encapsulate(0, secretSize(), "Generic")}. This combination + * of arguments must be supported by every implementation. + *

    + * The generated secret key is usually passed to a key derivation + * function (KDF) as the input keying material. + * + * @return a {@link Encapsulated} object containing the shared + * secret, key encapsulation message, and optional parameters. + * The shared secret is a {@code SecretKey} containing all of + * the bytes of the secret, and an algorithm name of "Generic". + */ + public Encapsulated encapsulate() { + return encapsulate(0, secretSize(), "Generic"); + } + + /** + * The key encapsulation function. + *

    + * Each invocation of this method generates a new secret key and key + * encapsulation message that is returned in an {@link Encapsulated} object. + *

    + * An implementation may choose to not support arbitrary combinations + * of {@code from}, {@code to}, and {@code algorithm}. + * + * @param from the initial index of the shared secret byte array + * to be returned, inclusive + * @param to the final index of the shared secret byte array + * to be returned, exclusive + * @param algorithm the algorithm name for the secret key that is returned + * @return a {@link Encapsulated} object containing a portion of + * the shared secret, key encapsulation message, and optional + * parameters. The portion of the shared secret is a + * {@code SecretKey} containing the bytes of the secret + * ranging from {@code from} to {@code to}, exclusive, + * and an algorithm name as specified. For example, + * {@code encapsulate(0, 16, "AES")} uses the first 16 bytes + * of the shared secret as a 128-bit AES key. + * @throws IndexOutOfBoundsException if {@code from < 0}, + * {@code from > to}, or {@code to > secretSize()} + * @throws NullPointerException if {@code algorithm} is {@code null} + * @throws UnsupportedOperationException if the combination of + * {@code from}, {@code to}, and {@code algorithm} + * is not supported by the encapsulator + */ + public Encapsulated encapsulate(int from, int to, String algorithm) { + return e.engineEncapsulate(from, to, algorithm); + } + + /** + * Returns the size of the shared secret. + *

    + * This method can be called to find out the length of the shared secret + * before {@code encapsulate} is called or if the obtained + * {@code SecretKey} is not extractable. + * + * @return the size of the shared secret + */ + public int secretSize() { + int result = e.engineSecretSize(); + assert result >= 0 && result != Integer.MAX_VALUE + : "invalid engineSecretSize result"; + return result; + } + + /** + * Returns the size of the key encapsulation message. + *

    + * This method can be called to find out the length of the encapsulation + * message before {@code encapsulate} is called. + * + * @return the size of the key encapsulation message + */ + public int encapsulationSize() { + int result = e.engineEncapsulationSize(); + assert result >= 0 && result != Integer.MAX_VALUE + : "invalid engineEncapsulationSize result"; + return result; + } + } + + /** + * A decapsulator, generated by {@link #newDecapsulator} on the KEM + * receiver side. + *

    + * This class represents the key decapsulation function of a KEM. + * An invocation of the {@code decapsulate} method recovers the + * secret key from the key encapsulation message. + * + * @apiNote This class is defined in Java SE 17 Maintenance Release 1. + * @since 17 + */ + public static final class Decapsulator { + private final KEMSpi.DecapsulatorSpi d; + private final Provider p; + + private Decapsulator(KEMSpi.DecapsulatorSpi d, Provider p) { + assert d != null; + assert p != null; + this.d = d; + this.p = p; + } + + /** + * Returns the name of the provider. + * + * @return the name of the provider + */ + public String providerName() { + return p.getName(); + } + + /** + * The key decapsulation function. + *

    + * This method is equivalent to + * {@code decapsulate(encapsulation, 0, secretSize(), "Generic")}. This + * combination of arguments must be supported by every implementation. + *

    + * The generated secret key is usually passed to a key derivation + * function (KDF) as the input keying material. + * + * @param encapsulation the key encapsulation message from the sender. + * The size must be equal to the value returned by + * {@link #encapsulationSize()}, or a {@code DecapsulateException} + * will be thrown. + * @return the shared secret as a {@code SecretKey} with + * an algorithm name of "Generic" + * @throws DecapsulateException if an error occurs during the + * decapsulation process + * @throws NullPointerException if {@code encapsulation} is {@code null} + */ + public SecretKey decapsulate(byte[] encapsulation) throws DecapsulateException { + return decapsulate(encapsulation, 0, secretSize(), "Generic"); + } + + /** + * The key decapsulation function. + *

    + * An invocation of this method recovers the secret key from the key + * encapsulation message. + *

    + * An implementation may choose to not support arbitrary combinations + * of {@code from}, {@code to}, and {@code algorithm}. + * + * @param encapsulation the key encapsulation message from the sender. + * The size must be equal to the value returned by + * {@link #encapsulationSize()}, or a {@code DecapsulateException} + * will be thrown. + * @param from the initial index of the shared secret byte array + * to be returned, inclusive + * @param to the final index of the shared secret byte array + * to be returned, exclusive + * @param algorithm the algorithm name for the secret key that is returned + * @return a portion of the shared secret as a {@code SecretKey} + * containing the bytes of the secret ranging from {@code from} + * to {@code to}, exclusive, and an algorithm name as specified. + * For example, {@code decapsulate(encapsulation, secretSize() + * - 16, secretSize(), "AES")} uses the last 16 bytes + * of the shared secret as a 128-bit AES key. + * @throws DecapsulateException if an error occurs during the + * decapsulation process + * @throws IndexOutOfBoundsException if {@code from < 0}, + * {@code from > to}, or {@code to > secretSize()} + * @throws NullPointerException if {@code encapsulation} or + * {@code algorithm} is {@code null} + * @throws UnsupportedOperationException if the combination of + * {@code from}, {@code to}, and {@code algorithm} + * is not supported by the decapsulator + */ + public SecretKey decapsulate(byte[] encapsulation, + int from, int to, String algorithm) + throws DecapsulateException { + return d.engineDecapsulate( + encapsulation, + from, to, + algorithm); + } + + /** + * Returns the size of the shared secret. + *

    + * This method can be called to find out the length of the shared secret + * before {@code decapsulate} is called or if the obtained + * {@code SecretKey} is not extractable. + * + * @return the size of the shared secret + */ + public int secretSize() { + int result = d.engineSecretSize(); + assert result >= 0 && result != Integer.MAX_VALUE + : "invalid engineSecretSize result"; + return result; + } + + /** + * Returns the size of the key encapsulation message. + *

    + * This method can be used to extract the encapsulation message + * from a longer byte array if no length information is provided + * by a higher level protocol. + * + * @return the size of the key encapsulation message + */ + public int encapsulationSize() { + int result = d.engineEncapsulationSize(); + assert result >= 0 && result != Integer.MAX_VALUE + : "invalid engineEncapsulationSize result"; + return result; + } + } + + private static final class DelayedKEM { + + private final Provider.Service[] list; // non empty array + + private DelayedKEM(Provider.Service[] list) { + this.list = list; + } + + private Encapsulator newEncapsulator(PublicKey publicKey, + AlgorithmParameterSpec spec, SecureRandom secureRandom) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (publicKey == null) { + throw new InvalidKeyException("input key is null"); + } + RuntimeException re = null; + InvalidAlgorithmParameterException iape = null; + InvalidKeyException ike = null; + NoSuchAlgorithmException nsae = null; + for (Provider.Service service : list) { + if (!service.supportsParameter(publicKey)) { + continue; + } + try { + KEMSpi spi = (KEMSpi) service.newInstance(null); + return new Encapsulator( + spi.engineNewEncapsulator(publicKey, spec, secureRandom), + service.getProvider()); + } catch (NoSuchAlgorithmException e) { + nsae = merge(nsae, e); + } catch (InvalidAlgorithmParameterException e) { + iape = merge(iape, e); + } catch (InvalidKeyException e) { + ike = merge(ike, e); + } catch (RuntimeException e) { + re = merge(re, e); + } + } + if (iape != null) throw iape; + if (ike != null) throw ike; + if (nsae != null) { + throw new InvalidKeyException("No installed provider found", nsae); + } + throw new InvalidKeyException("No installed provider supports this key: " + + publicKey.getClass().getName(), re); + } + + private static T merge(T e1, T e2) { + if (e1 == null) { + return e2; + } else { + e1.addSuppressed(e2); + return e1; + } + } + + private Decapsulator newDecapsulator(PrivateKey privateKey, AlgorithmParameterSpec spec) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (privateKey == null) { + throw new InvalidKeyException("input key is null"); + } + RuntimeException re = null; + InvalidAlgorithmParameterException iape = null; + InvalidKeyException ike = null; + NoSuchAlgorithmException nsae = null; + for (Provider.Service service : list) { + if (!service.supportsParameter(privateKey)) { + continue; + } + try { + KEMSpi spi = (KEMSpi) service.newInstance(null); + return new Decapsulator( + spi.engineNewDecapsulator(privateKey, spec), + service.getProvider()); + } catch (NoSuchAlgorithmException e) { + nsae = merge(nsae, e); + } catch (InvalidAlgorithmParameterException e) { + iape = merge(iape, e); + } catch (InvalidKeyException e) { + ike = merge(ike, e); + } catch (RuntimeException e) { + re = merge(re, e); + } + } + if (iape != null) throw iape; + if (ike != null) throw ike; + if (nsae != null) { + throw new InvalidKeyException("No installed provider found", nsae); + } + throw new InvalidKeyException("No installed provider supports this key: " + + privateKey.getClass().getName(), re); + } + } + + // If delayed provider selection is needed + private final DelayedKEM delayed; + + // otherwise + private final KEMSpi spi; + private final Provider provider; + + private final String algorithm; + + private KEM(String algorithm, KEMSpi spi, Provider provider) { + assert spi != null; + assert provider != null; + this.delayed = null; + this.spi = spi; + this.provider = provider; + this.algorithm = algorithm; + } + + private KEM(String algorithm, DelayedKEM delayed) { + assert delayed != null; + this.delayed = delayed; + this.spi = null; + this.provider = null; + this.algorithm = algorithm; + } + + /** + * Returns a {@code KEM} object that implements the specified algorithm. + * + * @param algorithm the name of the KEM algorithm. + * See the {@code KEM} section in the + * Java Security Standard Algorithm Names Specification + * for information about standard KEM algorithm names. + * @return the new {@code KEM} object + * @throws NoSuchAlgorithmException if no {@code Provider} supports a + * {@code KEM} implementation for the specified algorithm + * @throws NullPointerException if {@code algorithm} is {@code null} + */ + public static KEM getInstance(String algorithm) + throws NoSuchAlgorithmException { + List list = GetInstance.getServices( + "KEM", + Objects.requireNonNull(algorithm, "null algorithm name")); + List allowed = new ArrayList<>(); + for (Provider.Service s : list) { + if (!JceSecurity.canUseProvider(s.getProvider())) { + continue; + } + allowed.add(s); + } + if (allowed.isEmpty()) { + throw new NoSuchAlgorithmException + (algorithm + " KEM not available"); + } + + return new KEM(algorithm, new DelayedKEM(allowed.toArray(new Provider.Service[0]))); + } + + /** + * Returns a {@code KEM} object that implements the specified algorithm + * from the specified security provider. + * + * @param algorithm the name of the KEM algorithm. + * See the {@code KEM} section in the + * Java Security Standard Algorithm Names Specification + * for information about standard KEM algorithm names. + * @param provider the provider. If {@code null}, this method is equivalent + * to {@link #getInstance(String)}. + * @return the new {@code KEM} object + * @throws NoSuchAlgorithmException if a {@code provider} is specified and + * it does not support the specified KEM algorithm, + * or if {@code provider} is {@code null} and there is no provider + * that supports a KEM implementation of the specified algorithm + * @throws NullPointerException if {@code algorithm} is {@code null} + */ + public static KEM getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException { + if (provider == null) { + return getInstance(algorithm); + } + GetInstance.Instance instance = JceSecurity.getInstance( + "KEM", + KEMSpi.class, + Objects.requireNonNull(algorithm, "null algorithm name"), + provider); + return new KEM(algorithm, (KEMSpi) instance.impl, instance.provider); + } + + /** + * Returns a {@code KEM} object that implements the specified algorithm + * from the specified security provider. + * + * @param algorithm the name of the KEM algorithm. + * See the {@code KEM} section in the + * Java Security Standard Algorithm Names Specification + * for information about standard KEM algorithm names. + * @param provider the provider. If {@code null}, this method is equivalent + * to {@link #getInstance(String)}. + * @return the new {@code KEM} object + * @throws NoSuchAlgorithmException if a {@code provider} is specified and + * it does not support the specified KEM algorithm, + * or if {@code provider} is {@code null} and there is no provider + * that supports a KEM implementation of the specified algorithm + * @throws NoSuchProviderException if the specified provider is not + * registered in the security provider list + * @throws NullPointerException if {@code algorithm} is {@code null} + */ + public static KEM getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) { + return getInstance(algorithm); + } + GetInstance.Instance instance = JceSecurity.getInstance( + "KEM", + KEMSpi.class, + Objects.requireNonNull(algorithm, "null algorithm name"), + provider); + return new KEM(algorithm, (KEMSpi) instance.impl, instance.provider); + } + + /** + * Creates a KEM encapsulator on the KEM sender side. + *

    + * This method is equivalent to {@code newEncapsulator(publicKey, null, null)}. + * + * @param publicKey the receiver's public key, must not be {@code null} + * @return the encapsulator for this key + * @throws InvalidKeyException if {@code publicKey} is {@code null} or invalid + * @throws UnsupportedOperationException if this method is not supported + * because an {@code AlgorithmParameterSpec} must be provided + */ + public Encapsulator newEncapsulator(PublicKey publicKey) + throws InvalidKeyException { + try { + return newEncapsulator(publicKey, null, null); + } catch (InvalidAlgorithmParameterException e) { + throw new UnsupportedOperationException( + "AlgorithmParameterSpec must be provided", e); + } + } + + /** + * Creates a KEM encapsulator on the KEM sender side. + *

    + * This method is equivalent to {@code newEncapsulator(publicKey, null, secureRandom)}. + * + * @param publicKey the receiver's public key, must not be {@code null} + * @param secureRandom the source of randomness for encapsulation. + * If {@code} null, a default one from the + * implementation will be used. + * @return the encapsulator for this key + * @throws InvalidKeyException if {@code publicKey} is {@code null} or invalid + * @throws UnsupportedOperationException if this method is not supported + * because an {@code AlgorithmParameterSpec} must be provided + */ + public Encapsulator newEncapsulator(PublicKey publicKey, SecureRandom secureRandom) + throws InvalidKeyException { + try { + return newEncapsulator(publicKey, null, secureRandom); + } catch (InvalidAlgorithmParameterException e) { + throw new UnsupportedOperationException( + "AlgorithmParameterSpec must be provided", e); + } + } + + /** + * Creates a KEM encapsulator on the KEM sender side. + *

    + * An algorithm can define an {@code AlgorithmParameterSpec} child class to + * provide extra information in this method. This is especially useful if + * the same key can be used to derive shared secrets in different ways. + * If any extra information inside this object needs to be transmitted along + * with the key encapsulation message so that the receiver is able to create + * a matching decapsulator, it will be included as a byte array in the + * {@link Encapsulated#params} field inside the encapsulation output. + * In this case, the security provider should provide an + * {@code AlgorithmParameters} implementation using the same algorithm name + * as the KEM. The receiver can initiate such an {@code AlgorithmParameters} + * instance with the {@code params} byte array received and recover + * an {@code AlgorithmParameterSpec} object to be used in its + * {@link #newDecapsulator(PrivateKey, AlgorithmParameterSpec)} call. + * + * @param publicKey the receiver's public key, must not be {@code null} + * @param spec the optional parameter, can be {@code null} + * @param secureRandom the source of randomness for encapsulation. + * If {@code} null, a default one from the + * implementation will be used. + * @return the encapsulator for this key + * @throws InvalidAlgorithmParameterException if {@code spec} is invalid + * or one is required but {@code spec} is {@code null} + * @throws InvalidKeyException if {@code publicKey} is {@code null} or invalid + */ + public Encapsulator newEncapsulator(PublicKey publicKey, + AlgorithmParameterSpec spec, SecureRandom secureRandom) + throws InvalidAlgorithmParameterException, InvalidKeyException { + return delayed != null + ? delayed.newEncapsulator(publicKey, spec, secureRandom) + : new Encapsulator(spi.engineNewEncapsulator(publicKey, spec, secureRandom), provider); + } + + /** + * Creates a KEM decapsulator on the KEM receiver side. + *

    + * This method is equivalent to {@code newDecapsulator(privateKey, null)}. + * + * @param privateKey the receiver's private key, must not be {@code null} + * @return the decapsulator for this key + * @throws InvalidKeyException if {@code privateKey} is {@code null} or invalid + * @throws UnsupportedOperationException if this method is not supported + * because an {@code AlgorithmParameterSpec} must be provided + */ + public Decapsulator newDecapsulator(PrivateKey privateKey) + throws InvalidKeyException { + try { + return newDecapsulator(privateKey, null); + } catch (InvalidAlgorithmParameterException e) { + throw new UnsupportedOperationException(e); + } + } + + /** + * Creates a KEM decapsulator on the KEM receiver side. + * + * @param privateKey the receiver's private key, must not be {@code null} + * @param spec the parameter, can be {@code null} + * @return the decapsulator for this key + * @throws InvalidAlgorithmParameterException if {@code spec} is invalid + * or one is required but {@code spec} is {@code null} + * @throws InvalidKeyException if {@code privateKey} is {@code null} or invalid + */ + public Decapsulator newDecapsulator(PrivateKey privateKey, AlgorithmParameterSpec spec) + throws InvalidAlgorithmParameterException, InvalidKeyException { + return delayed != null + ? delayed.newDecapsulator(privateKey, spec) + : new Decapsulator(spi.engineNewDecapsulator(privateKey, spec), provider); + } + + /** + * Returns the name of the algorithm for this {@code KEM} object. + * + * @return the name of the algorithm for this {@code KEM} object. + */ + public String getAlgorithm() { + return this.algorithm; + } +} diff --git a/src/java.base/share/classes/javax/crypto/KEMSpi.java b/src/java.base/share/classes/javax/crypto/KEMSpi.java new file mode 100644 index 0000000000000..83318145866af --- /dev/null +++ b/src/java.base/share/classes/javax/crypto/KEMSpi.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.crypto; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +/** + * This class defines the Service Provider Interface (SPI) for the {@link KEM} + * class. A security provider implements this interface to provide an + * implementation of a Key Encapsulation Mechanism (KEM) algorithm. + *

    + * A KEM algorithm may support a family of configurations. Each configuration + * may accept different types of keys, cryptographic primitives, and sizes of + * shared secrets and key encapsulation messages. A configuration is defined + * by the KEM algorithm name, the key it uses, and an optional + * {@code AlgorithmParameterSpec} argument that is specified when creating + * an encapsulator or decapsulator. The result of calling + * {@link #engineNewEncapsulator} or {@link #engineNewDecapsulator} must return + * an encapsulator or decapsulator that maps to a single configuration, + * where its {@code engineSecretSize()} and {@code engineEncapsulationSize()} + * methods return constant values. + *

    + * A {@code KEMSpi} implementation must be immutable. It must be safe to + * call multiple {@code engineNewEncapsulator} and {@code engineNewDecapsulator} + * methods at the same time. + *

    + * {@code EncapsulatorSpi} and {@code DecapsulatorSpi} implementations must also + * be immutable. It must be safe to invoke multiple {@code encapsulate} and + * {@code decapsulate} methods at the same time. Each invocation of + * {@code encapsulate} should generate a new shared secret and key + * encapsulation message. + *

    + * For example, + *

    {@code
    + * public static class MyKEMImpl implements KEMSpi {
    + *
    + *     @Override
    + *     public KEMSpi.EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey,
    + *             AlgorithmParameterSpec spec, SecureRandom secureRandom)
    + *             throws InvalidAlgorithmParameterException, InvalidKeyException {
    + *         if (!checkPublicKey(publicKey)) {
    + *             throw new InvalidKeyException("unsupported key");
    + *         }
    + *         if (!checkParameters(spec)) {
    + *             throw new InvalidAlgorithmParameterException("unsupported params");
    + *         }
    + *         return new MyEncapsulator(publicKey, spec, secureRandom);
    + *     }
    + *
    + *     class MyEncapsulator implements KEMSpi.EncapsulatorSpi {
    + *         MyEncapsulator(PublicKey publicKey, AlgorithmParameterSpec spec,
    + *                 SecureRandom secureRandom){
    + *             this.spec = spec != null ? spec : getDefaultParameters();
    + *             this.secureRandom = secureRandom != null
    + *                     ? secureRandom
    + *                     : getDefaultSecureRandom();
    + *             this.publicKey = publicKey;
    + *         }
    + *
    + *         @Override
    + *         public KEM.Encapsulated encapsulate(int from, int to, String algorithm) {
    + *             byte[] encapsulation;
    + *             byte[] secret;
    + *             // calculating...
    + *             return new KEM.Encapsulated(
    + *                     new SecretKeySpec(secret, from, to - from, algorithm),
    + *                     encapsulation, null);
    + *         }
    + *
    + *         // ...
    + *     }
    + *
    + *     // ...
    + * }
    + * }
    + * + * @see KEM + * @apiNote This interface is defined in Java SE 17 Maintenance Release 1. + * @since 17 + */ +public interface KEMSpi { + + /** + * The KEM encapsulator implementation, generated by + * {@link #engineNewEncapsulator} on the KEM sender side. + * + * @see KEM.Encapsulator + * + * @apiNote This interface is defined in Java SE 17 Maintenance Release 1. + * @since 17 + */ + interface EncapsulatorSpi { + /** + * The key encapsulation function. + *

    + * Each invocation of this method must generate a new secret key and key + * encapsulation message that is returned in an {@link KEM.Encapsulated} object. + *

    + * An implementation must support the case where {@code from} is 0, + * {@code to} is the same as the return value of {@code secretSize()}, + * and {@code algorithm} is "Generic". + * + * @param from the initial index of the shared secret byte array + * to be returned, inclusive + * @param to the final index of the shared secret byte array + * to be returned, exclusive + * @param algorithm the algorithm name for the secret key that is returned + * @return an {@link KEM.Encapsulated} object containing a portion of + * the shared secret as a key with the specified algorithm, + * key encapsulation message, and optional parameters. + * @throws IndexOutOfBoundsException if {@code from < 0}, + * {@code from > to}, or {@code to > secretSize()} + * @throws NullPointerException if {@code algorithm} is {@code null} + * @throws UnsupportedOperationException if the combination of + * {@code from}, {@code to}, and {@code algorithm} + * is not supported by the encapsulator + * @see KEM.Encapsulated + * @see KEM.Encapsulator#encapsulate(int, int, String) + */ + KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm); + + /** + * Returns the size of the shared secret. + * + * @return the size of the shared secret as a finite non-negative integer + * @see KEM.Encapsulator#secretSize() + */ + int engineSecretSize(); + + /** + * Returns the size of the key encapsulation message. + * + * @return the size of the key encapsulation message as a finite non-negative integer + * @see KEM.Encapsulator#encapsulationSize() + */ + int engineEncapsulationSize(); + } + + /** + * The KEM decapsulator implementation, generated by + * {@link #engineNewDecapsulator} on the KEM receiver side. + * + * @see KEM.Decapsulator + * + * @apiNote This interface is defined in Java SE 17 Maintenance Release 1. + * @since 17 + */ + interface DecapsulatorSpi { + /** + * The key decapsulation function. + *

    + * An invocation of this method recovers the secret key from the key + * encapsulation message. + *

    + * An implementation must support the case where {@code from} is 0, + * {@code to} is the same as the return value of {@code secretSize()}, + * and {@code algorithm} is "Generic". + * + * @param encapsulation the key encapsulation message from the sender. + * The size must be equal to the value returned by + * {@link #engineEncapsulationSize()} ()}, or a + * {@code DecapsulateException} must be thrown. + * @param from the initial index of the shared secret byte array + * to be returned, inclusive + * @param to the final index of the shared secret byte array + * to be returned, exclusive + * @param algorithm the algorithm name for the secret key that is returned + * @return a portion of the shared secret as a {@code SecretKey} with + * the specified algorithm + * @throws DecapsulateException if an error occurs during the + * decapsulation process + * @throws IndexOutOfBoundsException if {@code from < 0}, + * {@code from > to}, or {@code to > secretSize()} + * @throws NullPointerException if {@code encapsulation} or + * {@code algorithm} is {@code null} + * @throws UnsupportedOperationException if the combination of + * {@code from}, {@code to}, and {@code algorithm} + * is not supported by the decapsulator + * @see KEM.Decapsulator#decapsulate(byte[], int, int, String) + */ + SecretKey engineDecapsulate(byte[] encapsulation, int from, int to, String algorithm) + throws DecapsulateException; + + /** + * Returns the size of the shared secret. + * + * @return the size of the shared secret as a finite non-negative integer + * @see KEM.Decapsulator#secretSize() + */ + int engineSecretSize(); + + /** + * Returns the size of the key encapsulation message. + * + * @return the size of the key encapsulation message as a finite non-negative integer + * @see KEM.Decapsulator#encapsulationSize() + */ + int engineEncapsulationSize(); + } + + /** + * Creates a KEM encapsulator on the KEM sender side. + * + * @param publicKey the receiver's public key, must not be {@code null} + * @param spec the optional parameter, can be {@code null} + * @param secureRandom the source of randomness for encapsulation. + * If {@code null}, the implementation must provide + * a default one. + * @return the encapsulator for this key + * @throws InvalidAlgorithmParameterException if {@code spec} is invalid + * or one is required but {@code spec} is {@code null} + * @throws InvalidKeyException if {@code publicKey} is {@code null} or invalid + * @see KEM#newEncapsulator(PublicKey, AlgorithmParameterSpec, SecureRandom) + */ + EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey, + AlgorithmParameterSpec spec, SecureRandom secureRandom) + throws InvalidAlgorithmParameterException, InvalidKeyException; + + /** + * Creates a KEM decapsulator on the KEM receiver side. + * + * @param privateKey the receiver's private key, must not be {@code null} + * @param spec the optional parameter, can be {@code null} + * @return the decapsulator for this key + * @throws InvalidAlgorithmParameterException if {@code spec} is invalid + * or one is required but {@code spec} is {@code null} + * @throws InvalidKeyException if {@code privateKey} is {@code null} or invalid + * @see KEM#newDecapsulator(PrivateKey, AlgorithmParameterSpec) + */ + DecapsulatorSpi engineNewDecapsulator(PrivateKey privateKey, AlgorithmParameterSpec spec) + throws InvalidAlgorithmParameterException, InvalidKeyException; +} diff --git a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java index e691fa9fb6671..d33bf8358146c 100644 --- a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -106,11 +106,9 @@ public void clearSecretKeySpec(SecretKeySpec keySpec) { * is null or key is null or empty. */ public SecretKeySpec(byte[] key, String algorithm) { - if (key == null || algorithm == null) { - throw new IllegalArgumentException("Missing argument"); - } - if (key.length == 0) { - throw new IllegalArgumentException("Empty key"); + String errMsg = doSanityCheck(key, algorithm); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } this.key = key.clone(); this.algorithm = algorithm; @@ -274,14 +272,22 @@ void clear() { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - - if (key == null || algorithm == null) { - throw new InvalidObjectException("Missing argument"); + String errMsg = doSanityCheck(key, algorithm); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); } + byte[] temp = key; + this.key = temp.clone(); + Arrays.fill(temp, (byte) 0); + } - this.key = key.clone(); - if (key.length == 0) { - throw new InvalidObjectException("Invalid key length"); + private static String doSanityCheck(byte[] key, String algorithm) { + String errMsg = null; + if (key == null || algorithm == null) { + errMsg = "Missing argument"; + } else if (key.length == 0) { + errMsg = "Empty key"; } + return errMsg; } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java b/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java index 973d11b1c7e88..6b2aefba89e06 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -103,20 +103,18 @@ public class ChoiceCallback implements Callback, java.io.Serializable { public ChoiceCallback(String prompt, String[] choices, int defaultChoice, boolean multipleSelectionsAllowed) { - if (prompt == null || prompt.isEmpty() || - choices == null || choices.length == 0 || - defaultChoice < 0 || defaultChoice >= choices.length) - throw new IllegalArgumentException(); - + choices = (choices == null || choices.length == 0 ? choices : + choices.clone()); + String errMsg = doSanityCheck(prompt, choices, defaultChoice, + multipleSelectionsAllowed); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = prompt; this.defaultChoice = defaultChoice; this.multipleSelectionsAllowed = multipleSelectionsAllowed; - this.choices = choices.clone(); - for (int i = 0; i < choices.length; i++) { - if (choices[i] == null || choices[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.choices = choices; } /** @@ -184,9 +182,11 @@ public void setSelectedIndex(int selection) { * @see #getSelectedIndexes */ public void setSelectedIndexes(int[] selections) { - if (!multipleSelectionsAllowed) + if (!multipleSelectionsAllowed) { throw new UnsupportedOperationException(); - this.selections = selections == null ? null : selections.clone(); + } + this.selections = ((selections == null || selections.length == 0) ? + selections : selections.clone()); } /** @@ -212,26 +212,35 @@ public int[] getSelectedIndexes() { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); + choices = (choices == null || choices.length == 0 ? + choices : choices.clone()); + String errMsg = doSanityCheck(prompt, choices, defaultChoice, + multipleSelectionsAllowed); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); + } + selections = (selections == null || selections.length == 0 ? + selections : selections.clone()); + if (selections != null && selections.length > 1 && + !multipleSelectionsAllowed) { + throw new InvalidObjectException("Multiple selections not allowed"); + } + } + + private static String doSanityCheck(String prompt, String[] choices, + int defaultChoice, boolean allowMultiple) { if ((prompt == null) || prompt.isEmpty() || (choices == null) || (choices.length == 0) || (defaultChoice < 0) || (defaultChoice >= choices.length)) { - throw new InvalidObjectException( - "Missing/invalid prompt/choices"); + return "Missing/invalid prompt/choices"; } - choices = choices.clone(); for (int i = 0; i < choices.length; i++) { - if ((choices[i] == null) || choices[i].isEmpty()) - throw new InvalidObjectException("Null/empty choices"); - } - - if (selections != null) { - selections = selections.clone(); - if (!multipleSelectionsAllowed && (selections.length != 1)) { - throw new InvalidObjectException( - "Multiple selections not allowed"); + if ((choices[i] == null) || choices[i].isEmpty()) { + return "Null/empty choices value"; } } + return null; } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java b/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java index 437ce7041a7d7..a00fc7013ecc1 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package javax.security.auth.callback; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; /** @@ -189,25 +190,10 @@ public class ConfirmationCallback implements Callback, java.io.Serializable { */ public ConfirmationCallback(int messageType, int optionType, int defaultOption) { - - if (messageType < INFORMATION || messageType > ERROR || - optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) - throw new IllegalArgumentException(); - - switch (optionType) { - case YES_NO_OPTION: - if (defaultOption != YES && defaultOption != NO) - throw new IllegalArgumentException(); - break; - case YES_NO_CANCEL_OPTION: - if (defaultOption != YES && defaultOption != NO && - defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; - case OK_CANCEL_OPTION: - if (defaultOption != OK && defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; + String errMsg = doSanityCheck(messageType, optionType, false, null, + defaultOption, null, false); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } this.prompt = null; @@ -250,21 +236,20 @@ public ConfirmationCallback(int messageType, public ConfirmationCallback(int messageType, String[] options, int defaultOption) { - if (messageType < INFORMATION || messageType > ERROR || - options == null || options.length == 0 || - defaultOption < 0 || defaultOption >= options.length) - throw new IllegalArgumentException(); + if (options != null) { + options = options.clone(); + } + String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true, + options, defaultOption, null, false); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = null; this.messageType = messageType; this.optionType = UNSPECIFIED_OPTION; this.defaultOption = defaultOption; - - this.options = options.clone(); - for (int i = 0; i < options.length; i++) { - if (options[i] == null || options[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.options = options; } /** @@ -304,27 +289,11 @@ public ConfirmationCallback(int messageType, public ConfirmationCallback(String prompt, int messageType, int optionType, int defaultOption) { - if (prompt == null || prompt.isEmpty() || - messageType < INFORMATION || messageType > ERROR || - optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) - throw new IllegalArgumentException(); - - switch (optionType) { - case YES_NO_OPTION: - if (defaultOption != YES && defaultOption != NO) - throw new IllegalArgumentException(); - break; - case YES_NO_CANCEL_OPTION: - if (defaultOption != YES && defaultOption != NO && - defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; - case OK_CANCEL_OPTION: - if (defaultOption != OK && defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; + String errMsg = doSanityCheck(messageType, optionType, false, null, + defaultOption, prompt, true); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } - this.prompt = prompt; this.messageType = messageType; this.optionType = optionType; @@ -369,22 +338,20 @@ public ConfirmationCallback(String prompt, int messageType, public ConfirmationCallback(String prompt, int messageType, String[] options, int defaultOption) { - if (prompt == null || prompt.isEmpty() || - messageType < INFORMATION || messageType > ERROR || - options == null || options.length == 0 || - defaultOption < 0 || defaultOption >= options.length) - throw new IllegalArgumentException(); + if (options != null) { + options = options.clone(); + } + String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true, + options, defaultOption, prompt, true); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = prompt; this.messageType = messageType; this.optionType = UNSPECIFIED_OPTION; this.defaultOption = defaultOption; - - this.options = options.clone(); - for (int i = 0; i < options.length; i++) { - if (options[i] == null || options[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.options = options; } /** @@ -491,6 +458,49 @@ public int getSelectedIndex() { return selection; } + private static String doSanityCheck(int msgType, int optionType, + boolean isUnspecifiedOption, String[] options, int defOption, + String prompt, boolean checkPrompt) { + // validate msgType + if (msgType < INFORMATION || msgType > ERROR) { + return "Invalid msgType"; + } + // validate prompt if checkPrompt == true + if (checkPrompt && (prompt == null || prompt.isEmpty())) { + return "Invalid prompt"; + } + // validate optionType + if (isUnspecifiedOption) { + if (optionType != UNSPECIFIED_OPTION) { + return "Invalid optionType"; + } + // check options + if (options == null || options.length == 0 || + defOption < 0 || defOption >= options.length) { + return "Invalid options and/or default option"; + } + for (String ov : options) { + if (ov == null || ov.isEmpty()) { + return "Invalid option value"; + } + } + } else { + if (optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) { + return "Invalid optionType"; + } + // validate defOption based on optionType + if ((optionType == YES_NO_OPTION && (defOption != YES && + defOption != NO)) || + (optionType == YES_NO_CANCEL_OPTION && (defOption != YES && + defOption != NO && defOption != CANCEL)) || + (optionType == OK_CANCEL_OPTION && (defOption != OK && + defOption != CANCEL))) { + return "Invalid default option"; + } + } + return null; + } + /** * Restores the state of this object from the stream. * @@ -502,8 +512,15 @@ public int getSelectedIndex() { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); + if (options != null) { options = options.clone(); } + String errMsg = doSanityCheck(messageType, optionType, + (optionType == UNSPECIFIED_OPTION), options, defaultOption, + prompt, false); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); + } } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java b/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java index 5e0f6bb5fde32..85e586ec3936d 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -181,7 +181,9 @@ private void readObject(ObjectInputStream stream) } if (inputPassword != null) { - inputPassword = inputPassword.clone(); + char[] temp = inputPassword; + inputPassword = temp.clone(); + Arrays.fill(temp, '0'); cleanable = CleanerFactory.cleaner().register( this, cleanerFor(inputPassword)); } diff --git a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java index 02b9614971cea..e8a648c46441f 100644 --- a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java +++ b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java @@ -212,7 +212,9 @@ public URLClassPath(URL[] urls, @SuppressWarnings("removal") AccessControlContex this.unopenedUrls = unopenedUrls; this.path = path; - this.jarHandler = null; + // the application class loader uses the built-in protocol handler to avoid protocol + // handler lookup when opening JAR files on the class path. + this.jarHandler = new sun.net.www.protocol.jar.Handler(); this.acc = null; } diff --git a/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java b/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java index 578519f7dcf77..b4565daa466d3 100644 --- a/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java +++ b/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java @@ -214,7 +214,7 @@ static ThreadFactory factory() { public Thread newThread(Runnable r) { return InnocuousThread.newThread("Cleaner-" + cleanerThreadNumber.getAndIncrement(), - r, Thread.MIN_PRIORITY - 2); + r, Thread.MAX_PRIORITY - 2); } } diff --git a/src/java.base/share/classes/sun/launcher/LauncherHelper.java b/src/java.base/share/classes/sun/launcher/LauncherHelper.java index 43a98a8686947..74c0cef881e96 100644 --- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java +++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java @@ -167,7 +167,7 @@ static void showSettings(boolean printToStderr, String optionFlag, printProperties(); break; case "locale": - printLocale(); + printLocale(false); break; case "security": var opt = opts.length > 2 ? opts[2].trim() : "all"; @@ -181,7 +181,7 @@ static void showSettings(boolean printToStderr, String optionFlag, default: printVmSettings(initialHeapSize, maxHeapSize, stackSize); printProperties(); - printLocale(); + printLocale(true); SecuritySettings.printSecuritySummarySettings(ostream); if (System.getProperty("os.name").contains("Linux")) { printSystemMetrics(); @@ -277,9 +277,15 @@ private static void printPropertyValue(String key, String value) { /* * prints the locale subopt/section */ - private static void printLocale() { + private static void printLocale(boolean summaryMode) { Locale locale = Locale.getDefault(); - ostream.println(LOCALE_SETTINGS); + if (!summaryMode) { + ostream.println(LOCALE_SETTINGS); + } else { + ostream.println("Locale settings summary:"); + ostream.println(INDENT + "Use \"-XshowSettings:locale\" " + + "option for verbose locale settings options"); + } ostream.println(INDENT + "default locale = " + locale.getDisplayName()); ostream.println(INDENT + "default display locale = " + @@ -288,7 +294,9 @@ private static void printLocale() { Locale.getDefault(Category.FORMAT).getDisplayName()); ostream.println(INDENT + "tzdata version = " + ZoneInfoFile.getVersion()); - printLocales(); + if (!summaryMode) { + printLocales(); + } ostream.println(); } diff --git a/src/java.base/share/classes/sun/net/www/MessageHeader.java b/src/java.base/share/classes/sun/net/www/MessageHeader.java index 22b16407dd240..4a60ab482c7ce 100644 --- a/src/java.base/share/classes/sun/net/www/MessageHeader.java +++ b/src/java.base/share/classes/sun/net/www/MessageHeader.java @@ -30,6 +30,8 @@ package sun.net.www; import java.io.*; +import java.lang.reflect.Array; +import java.net.ProtocolException; import java.util.Collections; import java.util.*; @@ -46,11 +48,32 @@ class MessageHeader { private String values[]; private int nkeys; + // max number of bytes for headers, <=0 means unlimited; + // this corresponds to the length of the names, plus the length + // of the values, plus an overhead of 32 bytes per name: value + // pair. + // Note: we use the same definition as HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE + // see RFC 9113, section 6.5.2. + // https://www.rfc-editor.org/rfc/rfc9113.html#SETTINGS_MAX_HEADER_LIST_SIZE + private final int maxHeaderSize; + + // Aggregate size of the field lines (name + value + 32) x N + // that have been parsed and accepted so far. + // This is defined as a long to force promotion to long + // and avoid overflows; see checkNewSize; + private long size; + public MessageHeader () { + this(0); + } + + public MessageHeader (int maxHeaderSize) { + this.maxHeaderSize = maxHeaderSize; grow(); } public MessageHeader (InputStream is) throws java.io.IOException { + maxHeaderSize = 0; parseHeader(is); } @@ -477,10 +500,28 @@ public static String canonicalID(String id) { public void parseHeader(InputStream is) throws java.io.IOException { synchronized (this) { nkeys = 0; + size = 0; } mergeHeader(is); } + private void checkMaxHeaderSize(int sz) throws ProtocolException { + if (maxHeaderSize > 0) checkNewSize(size, sz, 0); + } + + private long checkNewSize(long size, int name, int value) throws ProtocolException { + // See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2. + long newSize = size + name + value + 32; + if (maxHeaderSize > 0 && newSize > maxHeaderSize) { + Arrays.fill(keys, 0, nkeys, null); + Arrays.fill(values,0, nkeys, null); + nkeys = 0; + throw new ProtocolException(String.format("Header size too big: %s > %s", + newSize, maxHeaderSize)); + } + return newSize; + } + /** Parse and merge a MIME header from an input stream. */ @SuppressWarnings("fallthrough") public void mergeHeader(InputStream is) throws java.io.IOException { @@ -494,7 +535,15 @@ public void mergeHeader(InputStream is) throws java.io.IOException { int c; boolean inKey = firstc > ' '; s[len++] = (char) firstc; + checkMaxHeaderSize(len); parseloop:{ + // We start parsing for a new name value pair here. + // The max header size includes an overhead of 32 bytes per + // name value pair. + // See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2. + long maxRemaining = maxHeaderSize > 0 + ? maxHeaderSize - size - 32 + : Long.MAX_VALUE; while ((c = is.read()) >= 0) { switch (c) { case ':': @@ -528,6 +577,9 @@ public void mergeHeader(InputStream is) throws java.io.IOException { s = ns; } s[len++] = (char) c; + if (maxHeaderSize > 0 && len > maxRemaining) { + checkMaxHeaderSize(len); + } } firstc = -1; } @@ -549,6 +601,9 @@ public void mergeHeader(InputStream is) throws java.io.IOException { v = new String(); else v = String.copyValueOf(s, keyend, len - keyend); + int klen = k == null ? 0 : k.length(); + + size = checkNewSize(size, klen, v.length()); add(k, v); } } diff --git a/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java b/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java index 5bdd6f2c009f5..2abb5667b97f2 100644 --- a/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java +++ b/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -407,9 +407,9 @@ public KeepAliveKey(URL url, Object obj) { */ @Override public boolean equals(Object obj) { - if ((obj instanceof KeepAliveKey) == false) + if (!(obj instanceof KeepAliveKey kae)) return false; - KeepAliveKey kae = (KeepAliveKey)obj; + return host.equals(kae.host) && (port == kae.port) && protocol.equals(kae.protocol) @@ -423,7 +423,7 @@ public boolean equals(Object obj) { @Override public int hashCode() { String str = protocol+host+port; - return this.obj == null? str.hashCode() : + return this.obj == null ? str.hashCode() : str.hashCode() + this.obj.hashCode(); } } diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 7dc9f99eb1869..e3e72723428f4 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -171,6 +171,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection { */ private static int bufSize4ES = 0; + private static final int maxHeaderSize; + /* * Restrict setting of request headers through the public api * consistent with JavaScript XMLHttpRequest2 with a few @@ -285,10 +287,24 @@ private static Set schemesListToSet(String list) { } else { restrictedHeaderSet = null; } + + int defMaxHeaderSize = 384 * 1024; + String maxHeaderSizeStr = getNetProperty("jdk.http.maxHeaderSize"); + int maxHeaderSizeVal = defMaxHeaderSize; + if (maxHeaderSizeStr != null) { + try { + maxHeaderSizeVal = Integer.parseInt(maxHeaderSizeStr); + } catch (NumberFormatException n) { + maxHeaderSizeVal = defMaxHeaderSize; + } + } + if (maxHeaderSizeVal < 0) maxHeaderSizeVal = 0; + maxHeaderSize = maxHeaderSizeVal; } static final String httpVersion = "HTTP/1.1"; - static final String acceptString = "*/*"; + static final String acceptString = + "text/html, image/gif, image/jpeg, */*; q=0.2"; // the following http request headers should NOT have their values // returned for security reasons. @@ -759,7 +775,7 @@ private void writeRequests() throws IOException { } ps = (PrintStream) http.getOutputStream(); connected=true; - responses = new MessageHeader(); + responses = new MessageHeader(maxHeaderSize); setRequests=false; writeRequests(); } @@ -917,7 +933,7 @@ protected HttpURLConnection(URL u, Proxy p, Handler handler) throws IOException { super(checkURL(u)); requests = new MessageHeader(); - responses = new MessageHeader(); + responses = new MessageHeader(maxHeaderSize); userHeaders = new MessageHeader(); this.handler = handler; instProxy = p; @@ -2872,7 +2888,7 @@ private boolean followRedirect0(String loc, int stat, URL locUrl) } // clear out old response headers!!!! - responses = new MessageHeader(); + responses = new MessageHeader(maxHeaderSize); if (stat == HTTP_USE_PROXY) { /* This means we must re-request the resource through the * proxy denoted in the "Location:" field of the response. @@ -3062,7 +3078,7 @@ private void reset() throws IOException { } catch (IOException e) { } } responseCode = -1; - responses = new MessageHeader(); + responses = new MessageHeader(maxHeaderSize); connected = false; } diff --git a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java index 23c968ea6c2fe..b6b21f310bc0b 100644 --- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -547,7 +547,7 @@ public SocketAddress receive(ByteBuffer dst) throws IOException { n = receive(dst, connected); } } - if (n >= 0) { + if (n > 0 || (n == 0 && isOpen())) { // sender address is in socket address buffer sender = sourceSocketAddress(); } @@ -668,7 +668,7 @@ private SocketAddress trustedBlockingReceive(ByteBuffer dst) park(Net.POLLIN); n = receive(dst, connected); } - if (n >= 0) { + if (n > 0 || (n == 0 && isOpen())) { // sender address is in socket address buffer sender = sourceSocketAddress(); } @@ -705,7 +705,7 @@ private SocketAddress trustedBlockingReceive(ByteBuffer dst, long nanos) park(Net.POLLIN, remainingNanos); n = receive(dst, connected); } - if (n >= 0) { + if (n > 0 || (n == 0 && isOpen())) { // sender address is in socket address buffer sender = sourceSocketAddress(); } diff --git a/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java b/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java index 15cdaedf4b9a5..c8a6a476e66f3 100644 --- a/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java +++ b/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java @@ -186,7 +186,13 @@ public int read(char[] cbuf, int offset, int length) throws IOException { return n + 1; } - return n + implRead(cbuf, off, off + len); + // Read remaining characters + int nr = implRead(cbuf, off, off + len); + + // At this point, n is either 1 if a leftover character was read, + // or 0 if no leftover character was read. If n is 1 and nr is -1, + // indicating EOF, then we don't return their sum as this loses data. + return (nr < 0) ? (n == 1 ? 1 : nr) : (n + nr); } } diff --git a/src/java.base/share/classes/sun/reflect/generics/repository/GenericDeclRepository.java b/src/java.base/share/classes/sun/reflect/generics/repository/GenericDeclRepository.java index 5a34ab1749ad9..841526320474e 100644 --- a/src/java.base/share/classes/sun/reflect/generics/repository/GenericDeclRepository.java +++ b/src/java.base/share/classes/sun/reflect/generics/repository/GenericDeclRepository.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,8 @@ public abstract class GenericDeclRepository extends AbstractRepository { + public static final TypeVariable[] EMPTY_TYPE_VARS = new TypeVariable[0]; + /** The formal type parameters. Lazily initialized. */ private volatile TypeVariable[] typeParameters; @@ -77,6 +79,9 @@ private TypeVariable[] computeTypeParameters() { FormalTypeParameter[] ftps = getTree().getFormalTypeParameters(); // create array to store reified subtree(s) int length = ftps.length; + if (length == 0) { + return EMPTY_TYPE_VARS; + } TypeVariable[] typeParameters = new TypeVariable[length]; // reify all subtrees for (int i = 0; i < length; i++) { diff --git a/src/java.base/share/classes/sun/security/action/GetPropertyAction.java b/src/java.base/share/classes/sun/security/action/GetPropertyAction.java index f4c07f29a2867..b46578a833a0e 100644 --- a/src/java.base/share/classes/sun/security/action/GetPropertyAction.java +++ b/src/java.base/share/classes/sun/security/action/GetPropertyAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -162,6 +162,68 @@ public Properties run() { } } + /** + * Convenience method for fetching System property values that are timeouts. + * Accepted timeout values may be purely numeric, a numeric value + * followed by "s" (both interpreted as seconds), or a numeric value + * followed by "ms" (interpreted as milliseconds). + * + * @param prop the name of the System property + * @param def a default value (in milliseconds) + * @param dbg a Debug object, if null no debug messages will be sent + * + * @return an integer value corresponding to the timeout value in the System + * property in milliseconds. If the property value is empty, negative, + * or contains non-numeric characters (besides a trailing "s" or "ms") + * then the default value will be returned. If a negative value for + * the "def" parameter is supplied, zero will be returned if the + * property's value does not conform to the allowed syntax. + */ + public static int privilegedGetTimeoutProp(String prop, int def, Debug dbg) { + if (def < 0) { + def = 0; + } + + String rawPropVal = privilegedGetProperty(prop, "").trim(); + if (rawPropVal.length() == 0) { + return def; + } + + // Determine if "ms" or just "s" is on the end of the string. + // We may do a little surgery on the value so we'll retain + // the original value in rawPropVal for debug messages. + boolean isMillis = false; + String propVal = rawPropVal; + if (rawPropVal.toLowerCase().endsWith("ms")) { + propVal = rawPropVal.substring(0, rawPropVal.length() - 2); + isMillis = true; + } else if (rawPropVal.toLowerCase().endsWith("s")) { + propVal = rawPropVal.substring(0, rawPropVal.length() - 1); + } + + // Next check to make sure the string is built only from digits + if (propVal.matches("^\\d+$")) { + try { + int timeout = Integer.parseInt(propVal); + return isMillis ? timeout : timeout * 1000; + } catch (NumberFormatException nfe) { + if (dbg != null) { + dbg.println("Warning: Unexpected " + nfe + + " for timeout value " + rawPropVal + + ". Using default value of " + def + " msec."); + } + return def; + } + } else { + if (dbg != null) { + dbg.println("Warning: Incorrect syntax for timeout value " + + rawPropVal + ". Using default value of " + def + + " msec."); + } + return def; + } + } + /** * Convenience method for fetching System property values that are booleans. * diff --git a/src/java.base/share/classes/sun/security/jca/JCAUtil.java b/src/java.base/share/classes/sun/security/jca/JCAUtil.java index 862a33530ed36..0e2d0e29cb40a 100644 --- a/src/java.base/share/classes/sun/security/jca/JCAUtil.java +++ b/src/java.base/share/classes/sun/security/jca/JCAUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import jdk.internal.event.EventHelper; import jdk.internal.event.X509CertificateEvent; import sun.security.util.KeyUtil; +import sun.security.util.Debug; /** * Collection of static utility methods used by the security framework. @@ -105,7 +106,7 @@ public static void tryCommitCertEvent(Certificate cert) { (cert instanceof X509Certificate x509)) { PublicKey pKey = x509.getPublicKey(); String algId = x509.getSigAlgName(); - String serNum = x509.getSerialNumber().toString(16); + String serNum = Debug.toString(x509.getSerialNumber()); String subject = x509.getSubjectX500Principal().toString(); String issuer = x509.getIssuerX500Principal().toString(); String keyType = pKey.getAlgorithm(); diff --git a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java index af466e6615683..52b916b5bd639 100644 --- a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java +++ b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java @@ -698,14 +698,15 @@ private void verifyTimestamp(TimestampToken token) md.digest(encryptedDigest))) { throw new SignatureException("Signature timestamp (#" + - token.getSerialNumber() + ") generated on " + token.getDate() + - " is inapplicable"); + Debug.toString(token.getSerialNumber()) + + ") generated on " + token.getDate() + " is inapplicable"); } if (debug != null) { debug.println(); debug.println("Detected signature timestamp (#" + - token.getSerialNumber() + ") generated on " + token.getDate()); + Debug.toString(token.getSerialNumber()) + + ") generated on " + token.getDate()); debug.println(); } } diff --git a/src/java.base/share/classes/sun/security/provider/DRBG.java b/src/java.base/share/classes/sun/security/provider/DRBG.java index 5b4332139cef6..e5ca3f7b6e0c8 100644 --- a/src/java.base/share/classes/sun/security/provider/DRBG.java +++ b/src/java.base/share/classes/sun/security/provider/DRBG.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package sun.security.provider; import java.io.IOException; +import java.io.InvalidObjectException; import java.security.AccessController; import java.security.DrbgParameters; import java.security.PrivilegedAction; @@ -275,11 +276,18 @@ private static void checkTwice(boolean flag, String name) { } } + /** + * Restores the state of this object from the stream. + * + * @param s the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ @java.io.Serial private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); - if (mdp.mech == null) { + if (mdp == null || mdp.mech == null) { throw new IllegalArgumentException("Input data is corrupted"); } createImpl(); diff --git a/src/java.base/share/classes/sun/security/provider/PolicyFile.java b/src/java.base/share/classes/sun/security/provider/PolicyFile.java index 3dfc12ac377ad..85e27a401cc7e 100644 --- a/src/java.base/share/classes/sun/security/provider/PolicyFile.java +++ b/src/java.base/share/classes/sun/security/provider/PolicyFile.java @@ -614,6 +614,9 @@ public Void run() { pe.add(new PropertyPermission ("java.specification.version", SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission + ("java.specification.maintenance.version", + SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission ("java.specification.vendor", SecurityConstants.PROPERTY_READ_ACTION)); diff --git a/src/java.base/share/classes/sun/security/provider/certpath/BasicChecker.java b/src/java.base/share/classes/sun/security/provider/certpath/BasicChecker.java index 7fc691bd2dbd6..a09be783e6bdd 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/BasicChecker.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/BasicChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -244,7 +244,7 @@ private void updateState(X509Certificate currCert) debug.println("BasicChecker.updateState issuer: " + currCert.getIssuerX500Principal().toString() + "; subject: " + currCert.getSubjectX500Principal() + "; serial#: " + - currCert.getSerialNumber().toString()); + Debug.toString(currCert.getSerialNumber())); } if (PKIX.isDSAPublicKeyWithoutParams(cKey)) { // cKey needs to inherit DSA parameters from prev key diff --git a/src/java.base/share/classes/sun/security/provider/certpath/Builder.java b/src/java.base/share/classes/sun/security/provider/certpath/Builder.java index c920ad0282689..f48bf83ce73db 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/Builder.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/Builder.java @@ -435,8 +435,7 @@ boolean addMatchingCerts(X509CertSelector selector, if (debug != null) { debug.println("Builder.addMatchingCerts: " + "adding target cert" + - "\n SN: " + Debug.toHexString( - targetCert.getSerialNumber()) + + "\n SN: " + Debug.toString(targetCert.getSerialNumber()) + "\n Subject: " + targetCert.getSubjectX500Principal() + "\n Issuer: " + targetCert.getIssuerX500Principal()); } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/CertId.java b/src/java.base/share/classes/sun/security/provider/certpath/CertId.java index 34c06a464df9e..54a37aaeafe8a 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/CertId.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/CertId.java @@ -108,7 +108,7 @@ public CertId(X500Principal issuerName, PublicKey issuerKey, encoder.encodeBuffer(issuerNameHash)); System.out.println("issuerKeyHash is " + encoder.encodeBuffer(issuerKeyHash)); - System.out.println("SerialNumber is " + serialNumber.getNumber()); + System.out.println("SerialNumber is " + Debug.toString(serialNumber.getNumber())); } } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java b/src/java.base/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java index 16d9b7f606fd9..8cde6d66be857 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -349,7 +349,7 @@ static boolean verifyCRL(X509CertImpl certImpl, DistributionPoint point, if (debug != null) { debug.println("DistributionPointFetcher.verifyCRL: " + "checking revocation status for" + - "\n SN: " + Debug.toHexString(certImpl.getSerialNumber()) + + "\n SN: " + Debug.toString(certImpl.getSerialNumber()) + "\n Subject: " + certImpl.getSubjectX500Principal() + "\n Issuer: " + certImpl.getIssuerX500Principal()); } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java b/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java index 50f2daa60812c..77c8f3825d968 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java @@ -280,7 +280,7 @@ private void getMatchingCACerts(ForwardState currentState, debug.println("ForwardBuilder.getMatchingCACerts: " + "found matching trust anchor." + "\n SN: " + - Debug.toHexString(trustedCert.getSerialNumber()) + + Debug.toString(trustedCert.getSerialNumber()) + "\n Subject: " + trustedCert.getSubjectX500Principal() + "\n Issuer: " + @@ -703,7 +703,7 @@ void verifyCert(X509Certificate cert, State currentState, { if (debug != null) { debug.println("ForwardBuilder.verifyCert(SN: " - + Debug.toHexString(cert.getSerialNumber()) + + Debug.toString(cert.getSerialNumber()) + "\n Issuer: " + cert.getIssuerX500Principal() + ")" + "\n Subject: " + cert.getSubjectX500Principal() + ")"); } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java b/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java index 03731b25cfba2..1cab369b874f7 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java @@ -26,10 +26,7 @@ import java.io.IOException; import java.io.OutputStream; -import java.net.URI; -import java.net.URL; -import java.net.HttpURLConnection; -import java.net.URLEncoder; +import java.net.*; import java.security.cert.CertificateException; import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException.BasicReason; @@ -74,11 +71,20 @@ public final class OCSP { private static final int DEFAULT_CONNECT_TIMEOUT = 15000; /** - * Integer value indicating the timeout length, in seconds, to be - * used for the OCSP check. A timeout of zero is interpreted as - * an infinite timeout. + * Integer value indicating the timeout length, in milliseconds, to be + * used for establishing a connection to an OCSP responder. A timeout of + * zero is interpreted as an infinite timeout. */ - private static final int CONNECT_TIMEOUT = initializeTimeout(); + private static final int CONNECT_TIMEOUT = initializeTimeout( + "com.sun.security.ocsp.timeout", DEFAULT_CONNECT_TIMEOUT); + + /** + * Integer value indicating the timeout length, in milliseconds, to be + * used for reading an OCSP response from the responder. A timeout of + * zero is interpreted as an infinite timeout. + */ + private static final int READ_TIMEOUT = initializeTimeout( + "com.sun.security.ocsp.readtimeout", CONNECT_TIMEOUT); /** * Boolean value indicating whether OCSP client can use GET for OCSP @@ -107,16 +113,13 @@ public final class OCSP { * system property. If the property has not been set, or if its * value is negative, set the timeout length to the default. */ - private static int initializeTimeout() { - @SuppressWarnings("removal") - Integer tmp = java.security.AccessController.doPrivileged( - new GetIntegerAction("com.sun.security.ocsp.timeout")); - if (tmp == null || tmp < 0) { - return DEFAULT_CONNECT_TIMEOUT; + private static int initializeTimeout(String prop, int def) { + int timeoutVal = + GetPropertyAction.privilegedGetTimeoutProp(prop, def, debug); + if (debug != null) { + debug.println(prop + " set to " + timeoutVal + " milliseconds"); } - // Convert to milliseconds, as the system property will be - // specified in seconds - return tmp * 1000; + return timeoutVal; } private static boolean initializeBoolean(String prop, boolean def) { @@ -277,8 +280,10 @@ public static byte[] getOCSPBytes(List certIds, URI responderURI, Base64.getEncoder().encodeToString(bytes), "UTF-8")); if (USE_GET && encodedGetReq.length() <= 255) { - url = new URL(encodedGetReq.toString()); + url = new URI(encodedGetReq.toString()).toURL(); con = (HttpURLConnection)url.openConnection(); + con.setConnectTimeout(CONNECT_TIMEOUT); + con.setReadTimeout(READ_TIMEOUT); con.setDoOutput(true); con.setDoInput(true); con.setRequestMethod("GET"); @@ -286,7 +291,7 @@ public static byte[] getOCSPBytes(List certIds, URI responderURI, url = responderURI.toURL(); con = (HttpURLConnection)url.openConnection(); con.setConnectTimeout(CONNECT_TIMEOUT); - con.setReadTimeout(CONNECT_TIMEOUT); + con.setReadTimeout(READ_TIMEOUT); con.setDoOutput(true); con.setDoInput(true); con.setRequestMethod("POST"); @@ -316,6 +321,8 @@ public static byte[] getOCSPBytes(List certIds, URI responderURI, return (contentLength == -1) ? con.getInputStream().readAllBytes() : IOUtils.readExactlyNBytes(con.getInputStream(), contentLength); + } catch (URISyntaxException urise) { + throw new IOException(urise); } finally { if (con != null) { con.disconnect(); diff --git a/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java b/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java index d31f206bf083c..dcaa0f4a0e45c 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -404,7 +404,8 @@ void verify(List certIds, IssuerInfo issuerInfo, } if (debug != null) { debug.println("Status of certificate (with serial number " + - certId.getSerialNumber() + ") is: " + sr.getCertStatus()); + Debug.toString(certId.getSerialNumber()) + + ") is: " + sr.getCertStatus()); } } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java b/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java index 9da1405a62805..bdb8dffac2ea5 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -351,7 +351,7 @@ private void check(X509Certificate xcert, { if (debug != null) { debug.println("RevocationChecker.check: checking cert" + - "\n SN: " + Debug.toHexString(xcert.getSerialNumber()) + + "\n SN: " + Debug.toString(xcert.getSerialNumber()) + "\n Subject: " + xcert.getSubjectX500Principal() + "\n Issuer: " + xcert.getIssuerX500Principal()); } @@ -647,7 +647,7 @@ private void checkApprovedCRLs(X509Certificate cert, debug.println("RevocationChecker.checkApprovedCRLs() " + "starting the final sweep..."); debug.println("RevocationChecker.checkApprovedCRLs()" + - " cert SN: " + sn.toString()); + " cert SN: " + Debug.toString(sn)); } CRLReason reasonCode = CRLReason.UNSPECIFIED; diff --git a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java index a3b9d176d1e9a..71abdbe87f43a 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,8 @@ import java.util.Collections; import java.util.List; import java.util.Locale; -import sun.security.action.GetIntegerAction; + +import sun.security.action.GetPropertyAction; import sun.security.x509.AccessDescription; import sun.security.x509.GeneralNameInterface; import sun.security.x509.URIName; @@ -127,8 +128,12 @@ class URICertStore extends CertStoreSpi { // allowed when downloading CRLs private static final int DEFAULT_CRL_READ_TIMEOUT = 15000; + // Default connect and read timeouts for CA certificate fetching (15 sec) + private static final int DEFAULT_CACERT_CONNECT_TIMEOUT = 15000; + private static final int DEFAULT_CACERT_READ_TIMEOUT = 15000; + /** - * Integer value indicating the connect timeout, in seconds, to be + * Integer value indicating the connect timeout, in milliseconds, to be * used for the CRL download. A timeout of zero is interpreted as * an infinite timeout. */ @@ -137,7 +142,7 @@ class URICertStore extends CertStoreSpi { DEFAULT_CRL_CONNECT_TIMEOUT); /** - * Integer value indicating the read timeout, in seconds, to be + * Integer value indicating the read timeout, in milliseconds, to be * used for the CRL download. A timeout of zero is interpreted as * an infinite timeout. */ @@ -145,22 +150,36 @@ class URICertStore extends CertStoreSpi { initializeTimeout("com.sun.security.crl.readtimeout", DEFAULT_CRL_READ_TIMEOUT); + /** + * Integer value indicating the connect timeout, in milliseconds, to be + * used for the CA certificate download. A timeout of zero is interpreted + * as an infinite timeout. + */ + private static final int CACERT_CONNECT_TIMEOUT = + initializeTimeout("com.sun.security.cert.timeout", + DEFAULT_CACERT_CONNECT_TIMEOUT); + + /** + * Integer value indicating the read timeout, in milliseconds, to be + * used for the CA certificate download. A timeout of zero is interpreted + * as an infinite timeout. + */ + private static final int CACERT_READ_TIMEOUT = + initializeTimeout("com.sun.security.cert.readtimeout", + DEFAULT_CACERT_READ_TIMEOUT); + /** * Initialize the timeout length by getting the specified CRL timeout * system property. If the property has not been set, or if its * value is negative, set the timeout length to the specified default. */ private static int initializeTimeout(String prop, int def) { - Integer tmp = GetIntegerAction.privilegedGetProperty(prop); - if (tmp == null || tmp < 0) { - return def; - } + int timeoutVal = + GetPropertyAction.privilegedGetTimeoutProp(prop, def, debug); if (debug != null) { - debug.println(prop + " set to " + tmp + " seconds"); + debug.println(prop + " set to " + timeoutVal + " milliseconds"); } - // Convert to milliseconds, as the system property will be - // specified in seconds - return tmp * 1000; + return timeoutVal; } /** @@ -276,6 +295,8 @@ static CertStore getInstance(AccessDescription ad) { connection.setIfModifiedSince(lastModified); } long oldLastModified = lastModified; + connection.setConnectTimeout(CACERT_CONNECT_TIMEOUT); + connection.setReadTimeout(CACERT_READ_TIMEOUT); try (InputStream in = connection.getInputStream()) { lastModified = connection.getLastModified(); if (oldLastModified != 0) { diff --git a/src/java.base/share/classes/sun/security/provider/certpath/Vertex.java b/src/java.base/share/classes/sun/security/provider/certpath/Vertex.java index 06bfb2f111d84..be48402f0d75f 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/Vertex.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/Vertex.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -150,7 +150,7 @@ public String certToString() { sb.append("Subject: ").append (x509Cert.getSubjectX500Principal()).append("\n"); sb.append("SerialNum: ").append - (x509Cert.getSerialNumber().toString(16)).append("\n"); + (Debug.toString(x509Cert.getSerialNumber())).append("\n"); sb.append("Expires: ").append (x509Cert.getNotAfter().toString()).append("\n"); boolean[] iUID = x509Cert.getIssuerUniqueID(); diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java index 5921b7cfca556..840c0055b9acb 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java @@ -388,7 +388,7 @@ private void onCertificate(ServerHandshakeContext shc, ClientAuthType.CLIENT_AUTH_REQUESTED) { // unexpected or require client authentication throw shc.conContext.fatal(Alert.BAD_CERTIFICATE, - "Empty server certificate chain"); + "Empty client certificate chain"); } else { return; } @@ -405,7 +405,7 @@ private void onCertificate(ServerHandshakeContext shc, } } catch (CertificateException ce) { throw shc.conContext.fatal(Alert.BAD_CERTIFICATE, - "Failed to parse server certificates", ce); + "Failed to parse client certificates", ce); } checkClientCerts(shc, x509Certs); @@ -1247,7 +1247,7 @@ private static X509Certificate[] checkClientCerts( } } catch (CertificateException ce) { throw shc.conContext.fatal(Alert.BAD_CERTIFICATE, - "Failed to parse server certificates", ce); + "Failed to parse client certificates", ce); } // find out the types of client authentication used diff --git a/src/java.base/share/classes/sun/security/ssl/ClientHello.java b/src/java.base/share/classes/sun/security/ssl/ClientHello.java index be06d5b902473..3a2641b6ec6c8 100644 --- a/src/java.base/share/classes/sun/security/ssl/ClientHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ClientHello.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -217,8 +217,6 @@ byte[] getHelloCookieBytes() { // ignore cookie hos.putBytes16(getEncodedCipherSuites()); hos.putBytes8(compressionMethod); - extensions.send(hos); // In TLS 1.3, use of certain - // extensions is mandatory. } catch (IOException ioe) { // unlikely } @@ -904,8 +902,8 @@ private ProtocolVersion negotiateProtocol( throw context.conContext.fatal(Alert.PROTOCOL_VERSION, "The client supported protocol versions " + Arrays.toString( ProtocolVersion.toStringArray(clientSupportedVersions)) + - " are not accepted by server preferences " + - context.activeProtocols); + " are not accepted by server preferences " + Arrays.toString( + ProtocolVersion.toStringArray(context.activeProtocols))); } } @@ -1427,6 +1425,9 @@ public void consume(ConnectionContext context, shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id, SSLHandshake.SERVER_HELLO); + // Reset the ClientHello non-zero offset fragment allowance + shc.acceptCliHelloFragments = false; + // // produce // diff --git a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java index 931f45e68ff8c..123b1667dfd12 100644 --- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,13 +28,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import javax.crypto.BadPaddingException; import javax.net.ssl.SSLException; import javax.net.ssl.SSLProtocolException; @@ -46,12 +40,23 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord { private DTLSReassembler reassembler = null; private int readEpoch; + private SSLContextImpl sslContext; DTLSInputRecord(HandshakeHash handshakeHash) { super(handshakeHash, SSLReadCipher.nullDTlsReadCipher()); this.readEpoch = 0; } + // Method to set TransportContext + public void setTransportContext(TransportContext tc) { + this.tc = tc; + } + + // Method to set SSLContext + public void setSSLContext(SSLContextImpl sslContext) { + this.sslContext = sslContext; + } + @Override void changeReadCiphers(SSLReadCipher readCipher) { this.readCipher = readCipher; @@ -544,6 +549,27 @@ public int compareTo(RecordFragment o) { } } + /** + * Turn a sufficiently-large initial ClientHello fragment into one that + * stops immediately after the compression methods. This is only used + * for the initial CH message fragment at offset 0. + * + * @param srcFrag the fragment actually received by the DTLSReassembler + * @param limit the size of the new, cloned/truncated handshake fragment + * + * @return a truncated handshake fragment that is sized to look like a + * complete message, but actually contains only up to the compression + * methods (no extensions) + */ + private static HandshakeFragment truncateChFragment(HandshakeFragment srcFrag, + int limit) { + return new HandshakeFragment(Arrays.copyOf(srcFrag.fragment, limit), + srcFrag.contentType, srcFrag.majorVersion, + srcFrag.minorVersion, srcFrag.recordEnS, srcFrag.recordEpoch, + srcFrag.recordSeq, srcFrag.handshakeType, limit, + srcFrag.messageSeq, srcFrag.fragmentOffset, limit); + } + private static final class HoleDescriptor { int offset; // fragment_offset int limit; // fragment_offset + fragment_length @@ -647,10 +673,17 @@ void expectingFinishFlight() { // Queue up a handshake message. void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { if (!isDesirable(hsf)) { - // Not a dedired record, discard it. + // Not a desired record, discard it. return; } + if (hsf.handshakeType == SSLHandshake.CLIENT_HELLO.id) { + // validate the first or subsequent ClientHello message + if ((hsf = valHello(hsf, hsf.messageSeq == 0)) == null) { + return; + } + } + // Clean up the retransmission messages if necessary. cleanUpRetransmit(hsf); @@ -783,6 +816,100 @@ void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { } } + private HandshakeFragment valHello(HandshakeFragment hsf, + boolean firstHello) { + ServerHandshakeContext shc = + (ServerHandshakeContext) tc.handshakeContext; + // Drop any fragment that is not a zero offset until we've received + // a second (or possibly later) CH message that passes the cookie + // check. + if (shc == null || !shc.acceptCliHelloFragments) { + if (hsf.fragmentOffset != 0) { + return null; + } + } else { + // Let this fragment through to the DTLSReassembler as-is + return hsf; + } + + try { + ByteBuffer fragmentData = ByteBuffer.wrap(hsf.fragment); + + ProtocolVersion pv = ProtocolVersion.valueOf( + Record.getInt16(fragmentData)); + if (!pv.isDTLS) { + return null; + } + // Read the random (32 bytes) + if (fragmentData.remaining() < 32) { + if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.fine("Rejected client hello fragment (bad random len) " + + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); + } + return null; + } + fragmentData.position(fragmentData.position() + 32); + + // SessionID + byte[] sessId = Record.getBytes8(fragmentData); + if (sessId.length > 0 && + !SSLConfiguration.enableDtlsResumeCookie) { + // If we are in a resumption it is possible that the cookie + // exchange will be skipped. This is a server-side setting + // and it is NOT the default. If enableDtlsResumeCookie is + // false though, then we will buffer fragments since there + // is no cookie exchange to execute prior to performing + // reassembly. + return hsf; + } + + // Cookie + byte[] cookie = Record.getBytes8(fragmentData); + if (firstHello && cookie.length != 0) { + if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.fine("Rejected initial client hello fragment (bad cookie len) " + + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); + } + return null; + } + // CipherSuites + Record.getBytes16(fragmentData); + // Compression methods + Record.getBytes8(fragmentData); + + // If it's the first fragment, we'll truncate it and push it + // through the reassembler. + if (firstHello) { + return truncateChFragment(hsf, fragmentData.position()); + } else { + HelloCookieManager hcMgr = sslContext. + getHelloCookieManager(ProtocolVersion.DTLS10); + ByteBuffer msgFragBuf = ByteBuffer.wrap(hsf.fragment, 0, + fragmentData.position()); + ClientHello.ClientHelloMessage chMsg = + new ClientHello.ClientHelloMessage(shc, msgFragBuf, null); + if (!hcMgr.isCookieValid(shc, chMsg, cookie)) { + // Bad cookie check, truncate it and let the ClientHello + // consumer recheck, fail and take the appropriate action. + return truncateChFragment(hsf, fragmentData.position()); + } else { + // It's a good cookie, return the original handshake + // fragment and let it go into the DTLSReassembler like + // any other fragment so we can wait for the rest of + // the CH message. + shc.acceptCliHelloFragments = true; + return hsf; + } + } + } catch (IOException ioe) { + if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.fine("Rejected client hello fragment " + + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); + } + return null; + } + } + // Queue up a ChangeCipherSpec message void queueUpChangeCipherSpec(RecordFragment rf) throws SSLProtocolException { diff --git a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java index cd74949cbb53c..0f336dbeac307 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,9 +71,6 @@ public abstract class SSLContextImpl extends SSLContextSpi { private volatile StatusResponseManager statusResponseManager; private final ReentrantLock contextLock = new ReentrantLock(); - final HashMap keyHashMap = new HashMap<>(); - SSLContextImpl() { ephemeralKeyManager = new EphemeralKeyManager(); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java index 9f2cbaf4837bb..a97eb68dfa6ac 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,6 +46,7 @@ import sun.security.action.GetPropertyAction; import sun.security.util.HexDumpEncoder; +import sun.security.util.Debug; import sun.security.x509.*; import static java.nio.charset.StandardCharsets.UTF_8; @@ -471,8 +472,7 @@ private static String formatCertificate(Certificate certificate) { if (certExts == null) { Object[] certFields = { x509.getVersion(), - Utilities.toHexString( - x509.getSerialNumber().toByteArray()), + Debug.toString(x509.getSerialNumber()), x509.getSigAlgName(), x509.getIssuerX500Principal().toString(), dateTimeFormat.format(x509.getNotBefore().toInstant()), @@ -496,8 +496,7 @@ private static String formatCertificate(Certificate certificate) { } Object[] certFields = { x509.getVersion(), - Utilities.toHexString( - x509.getSerialNumber().toByteArray()), + Debug.toString(x509.getSerialNumber()), x509.getSigAlgName(), x509.getIssuerX500Principal().toString(), dateTimeFormat.format(x509.getNotBefore().toInstant()), diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java index 9584a35c5de1e..2dd629b1f1ecb 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; +import java.util.Iterator; import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSessionContext; @@ -69,6 +73,11 @@ final class SSLSessionContextImpl implements SSLSessionContext { private int cacheLimit; // the max cache size private int timeout; // timeout in seconds + // The current session ticket encryption key ID (only used in server context) + private int currentKeyID; + // Session ticket encryption keys and IDs map (only used in server context) + private final Map keyHashMap; + // Default setting for stateless session resumption support (RFC 5077) private boolean statelessSession = true; @@ -80,6 +89,14 @@ final class SSLSessionContextImpl implements SSLSessionContext { // use soft reference sessionCache = Cache.newSoftMemoryCache(cacheLimit, timeout); sessionHostPortCache = Cache.newSoftMemoryCache(cacheLimit, timeout); + if (server) { + keyHashMap = new ConcurrentHashMap<>(); + // Should be "randomly generated" according to RFC 5077, + // but doesn't necessarily has to be a true random number. + currentKeyID = new Random(System.nanoTime()).nextInt(); + } else { + keyHashMap = Map.of(); + } } // Stateless sessions when available, but there is a cache @@ -170,6 +187,51 @@ public int getSessionCacheSize() { return cacheLimit; } + private void cleanupStatelessKeys() { + Iterator> it = + keyHashMap.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + SessionTicketExtension.StatelessKey k = entry.getValue(); + if (k.isInvalid(this)) { + it.remove(); + try { + k.key.destroy(); + } catch (Exception e) { + // Suppress + } + } + } + } + + // Package-private, used only from SessionTicketExtension.KeyState::getCurrentKey. + SessionTicketExtension.StatelessKey getKey(HandshakeContext hc) { + SessionTicketExtension.StatelessKey ssk = keyHashMap.get(currentKeyID); + if (ssk != null && !ssk.isExpired()) { + return ssk; + } + synchronized (this) { + // If the current key is no longer expired, it was already + // updated by a concurrent request, and we can return. + ssk = keyHashMap.get(currentKeyID); + if (ssk != null && !ssk.isExpired()) { + return ssk; + } + int newID = currentKeyID + 1; + ssk = new SessionTicketExtension.StatelessKey(hc, newID); + keyHashMap.put(Integer.valueOf(newID), ssk); + currentKeyID = newID; + } + // Check for and delete invalid keys every time we create a new stateless key. + cleanupStatelessKeys(); + return ssk; + } + + // Package-private, used only from SessionTicketExtension.KeyState::getKey. + SessionTicketExtension.StatelessKey getKey(int id) { + return keyHashMap.get(id); + } + // package-private method, used ONLY by ServerHandshaker SSLSessionImpl get(byte[] id) { return (SSLSessionImpl)getSession(id); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java index f288e210aa30c..387c4ca69e841 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -1791,21 +1791,24 @@ private void closeSocket(boolean selfInitiated) throws IOException { if (conContext.inputRecord instanceof SSLSocketInputRecord inputRecord && isConnected) { if (appInput.readLock.tryLock()) { - int soTimeout = getSoTimeout(); try { - // deplete could hang on the skip operation - // in case of infinite socket read timeout. - // Change read timeout to avoid deadlock. - // This workaround could be replaced later - // with the right synchronization - if (soTimeout == 0) - setSoTimeout(DEFAULT_SKIP_TIMEOUT); - inputRecord.deplete(false); - } catch (java.net.SocketTimeoutException stEx) { - // skip timeout exception during deplete + int soTimeout = getSoTimeout(); + try { + // deplete could hang on the skip operation + // in case of infinite socket read timeout. + // Change read timeout to avoid deadlock. + // This workaround could be replaced later + // with the right synchronization + if (soTimeout == 0) + setSoTimeout(DEFAULT_SKIP_TIMEOUT); + inputRecord.deplete(false); + } catch (java.net.SocketTimeoutException stEx) { + // skip timeout exception during deplete + } finally { + if (soTimeout == 0) + setSoTimeout(soTimeout); + } } finally { - if (soTimeout == 0) - setSoTimeout(soTimeout); appInput.readLock.unlock(); } } diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java index 829fa2af96c7c..11b625e57911c 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,6 +55,7 @@ class ServerHandshakeContext extends HandshakeContext { CertificateMessage.CertificateEntry currentCertEntry; private static final long DEFAULT_STATUS_RESP_DELAY = 5000L; final long statusRespTimeout; + boolean acceptCliHelloFragments = false; ServerHandshakeContext(SSLContextImpl sslContext, diff --git a/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java b/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java index 74f38bf5b56c4..18d05ca358502 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,7 @@ import static sun.security.ssl.SSLExtension.CH_SERVER_NAME; import static sun.security.ssl.SSLExtension.EE_SERVER_NAME; import sun.security.ssl.SSLExtension.ExtensionConsumer; +import static sun.security.ssl.SSLExtension.SH_PRE_SHARED_KEY; import static sun.security.ssl.SSLExtension.SH_SERVER_NAME; import sun.security.ssl.SSLExtension.SSLExtensionSpec; import sun.security.ssl.SSLHandshake.HandshakeMessage; @@ -344,6 +345,10 @@ public void consume(ConnectionContext context, sni, shc.resumingSession.serverNameIndication)) { shc.isResumption = false; shc.resumingSession = null; + // this server is disallowing this session resumption, + // so don't include the pre-shared key in the + // ServerHello handshake message + shc.handshakeExtensions.remove(SH_PRE_SHARED_KEY); if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + diff --git a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java index af9cd69575f3f..1132ec120da50 100644 --- a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import javax.net.ssl.SSLProtocolException; +import javax.net.ssl.SSLSessionContext; import static sun.security.ssl.SSLExtension.CH_SESSION_TICKET; import static sun.security.ssl.SSLExtension.SH_SESSION_TICKET; @@ -76,7 +77,6 @@ final class SessionTicketExtension { // Time in milliseconds until key is changed for encrypting session state private static final int TIMEOUT_DEFAULT = 3600 * 1000; private static final int keyTimeout; - private static int currentKeyID = new SecureRandom().nextInt(); private static final int KEYLEN = 256; static { @@ -117,7 +117,8 @@ static final class StatelessKey { final SecretKey key; final int num; - StatelessKey(HandshakeContext hc, int newNum) { + // package-private, used only by SSLContextImpl + StatelessKey(HandshakeContext hc, int num) { SecretKey k = null; try { KeyGenerator kg = KeyGenerator.getInstance("AES"); @@ -128,8 +129,7 @@ static final class StatelessKey { } key = k; timeout = System.currentTimeMillis() + keyTimeout; - num = newNum; - hc.sslContext.keyHashMap.put(Integer.valueOf(num), this); + this.num = num; } // Check if key needs to be changed @@ -138,7 +138,8 @@ boolean isExpired() { } // Check if this key is ready for deletion. - boolean isInvalid(long sessionTimeout) { + boolean isInvalid(SSLSessionContext sslSessionContext) { + int sessionTimeout = sslSessionContext.getSessionTimeout() * 1000; return ((System.currentTimeMillis()) > (timeout + sessionTimeout)); } } @@ -147,9 +148,11 @@ private static final class KeyState { // Get a key with a specific key number static StatelessKey getKey(HandshakeContext hc, int num) { - StatelessKey ssk = hc.sslContext.keyHashMap.get(num); + SSLSessionContextImpl serverCache = + (SSLSessionContextImpl)hc.sslContext.engineGetServerSessionContext(); + StatelessKey ssk = serverCache.getKey(num); - if (ssk == null || ssk.isInvalid(getSessionTimeout(hc))) { + if (ssk == null || ssk.isInvalid(serverCache)) { return null; } return ssk; @@ -157,69 +160,9 @@ static StatelessKey getKey(HandshakeContext hc, int num) { // Get the current valid key, this will generate a new key if needed static StatelessKey getCurrentKey(HandshakeContext hc) { - StatelessKey ssk = hc.sslContext.keyHashMap.get(currentKeyID); - - if (ssk != null && !ssk.isExpired()) { - return ssk; - } - return nextKey(hc); - } - - // This method locks when the first getCurrentKey() finds it to be too - // old and create a new key to replace the current key. After the new - // key established, the lock can be released so following - // operations will start using the new key. - // The first operation will take a longer code path by generating the - // next key and cleaning up old keys. - private static StatelessKey nextKey(HandshakeContext hc) { - StatelessKey ssk; - - synchronized (hc.sslContext.keyHashMap) { - // If the current key is no longer expired, it was already - // updated by a previous operation and we can return. - ssk = hc.sslContext.keyHashMap.get(currentKeyID); - if (ssk != null && !ssk.isExpired()) { - return ssk; - } - int newNum; - if (currentKeyID == Integer.MAX_VALUE) { - newNum = 0; - } else { - newNum = currentKeyID + 1; - } - // Get new key - ssk = new StatelessKey(hc, newNum); - currentKeyID = newNum; - // Release lock since the new key is ready to be used. - } - - // Clean up any old keys, then return the current key - cleanup(hc); - return ssk; - } - - // Deletes any invalid SessionStateKeys. - static void cleanup(HandshakeContext hc) { - int sessionTimeout = getSessionTimeout(hc); - - StatelessKey ks; - for (Object o : hc.sslContext.keyHashMap.keySet().toArray()) { - Integer i = (Integer)o; - ks = hc.sslContext.keyHashMap.get(i); - if (ks.isInvalid(sessionTimeout)) { - try { - ks.key.destroy(); - } catch (Exception e) { - // Suppress - } - hc.sslContext.keyHashMap.remove(i); - } - } - } - - static int getSessionTimeout(HandshakeContext hc) { - return hc.sslContext.engineGetServerSessionContext(). - getSessionTimeout() * 1000; + SSLSessionContextImpl serverCache = + (SSLSessionContextImpl)hc.sslContext.engineGetServerSessionContext(); + return serverCache.getKey(hc); } } diff --git a/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java b/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java index c659cdeb5bab4..65f373dc5c119 100644 --- a/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java +++ b/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,6 +53,7 @@ import sun.security.provider.certpath.OCSPResponse; import sun.security.provider.certpath.ResponderId; import sun.security.util.Cache; +import sun.security.util.Debug; import sun.security.x509.PKIXExtensions; import sun.security.x509.SerialNumber; import sun.security.ssl.X509Authentication.X509Possession; @@ -419,8 +420,8 @@ private ResponseCacheEntry getFromCache(CertId cid, if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { SSLLogger.fine( - "Check cache for SN" + cid.getSerialNumber() + ": " + - (respEntry != null ? "HIT" : "MISS")); + "Check cache for SN" + Debug.toString(cid.getSerialNumber()) + + ": " + (respEntry != null ? "HIT" : "MISS")); } return respEntry; } @@ -516,7 +517,7 @@ public String toString() { StringBuilder sb = new StringBuilder("StatusInfo:"); sb.append("\n\tCert: ").append( this.cert.getSubjectX500Principal()); - sb.append("\n\tSerial: ").append(this.cert.getSerialNumber()); + sb.append("\n\tSerial: ").append(Debug.toString(this.cert.getSerialNumber())); sb.append("\n\tResponder: ").append(this.responder); sb.append("\n\tResponse data: ").append( this.responseData != null ? @@ -563,7 +564,7 @@ static class ResponseCacheEntry { } else { throw new IOException( "Unable to find SingleResponse for SN " + - cid.getSerialNumber()); + Debug.toString(cid.getSerialNumber())); } } else { nextUpdate = null; @@ -614,7 +615,7 @@ public StatusInfo call() { if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Starting fetch for SN " + - statInfo.cid.getSerialNumber()); + Debug.toString(statInfo.cid.getSerialNumber())); } try { ResponseCacheEntry cacheEntry; @@ -706,7 +707,7 @@ private void addToCache(CertId certId, ResponseCacheEntry entry) { if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Added response for SN " + - certId.getSerialNumber() + + Debug.toString(certId.getSerialNumber()) + " to cache"); } } diff --git a/src/java.base/share/classes/sun/security/ssl/TransportContext.java b/src/java.base/share/classes/sun/security/ssl/TransportContext.java index b0572d8a56767..b9e62092840e1 100644 --- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java +++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -156,6 +156,11 @@ private TransportContext(SSLContextImpl sslContext, SSLTransport transport, this.acc = AccessController.getContext(); this.consumers = new HashMap<>(); + + if (inputRecord instanceof DTLSInputRecord dtlsInputRecord) { + dtlsInputRecord.setTransportContext(this); + dtlsInputRecord.setSSLContext(this.sslContext); + } } // Dispatch plaintext to a specific consumer. diff --git a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java index 681bb25904b35..adb763f79243a 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -429,8 +429,17 @@ static void checkIdentity(SSLSession session, } if (!identifiable) { - checkIdentity(peerHost, - trustedChain[0], algorithm, chainsToPublicCA); + try { + checkIdentity(peerHost, + trustedChain[0], algorithm, chainsToPublicCA); + } catch(CertificateException ce) { + if (checkClientTrusted && "HTTPS".equalsIgnoreCase(algorithm)) { + throw new CertificateException("Endpoint Identification Algorithm " + + "HTTPS is not supported on the server side"); + } else { + throw ce; + } + } } } diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Main.java b/src/java.base/share/classes/sun/security/tools/keytool/Main.java index 575367a405b81..54b3a09109086 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java @@ -1125,7 +1125,6 @@ && isKeyStoreRelated(command) } } - KeyStore cakstore = buildTrustedCerts(); // -trustcacerts can be specified on -importcert, -printcert or -printcrl. // Reset it so that warnings on CA cert will remain for other command. if (command != IMPORTCERT && command != PRINTCERT @@ -1134,6 +1133,7 @@ && isKeyStoreRelated(command) } if (trustcacerts) { + KeyStore cakstore = buildTrustedCerts(); if (cakstore != null) { caks = cakstore; } else { @@ -1287,6 +1287,7 @@ && isKeyStoreRelated(command) kssave = true; } else if (command == LIST) { if (storePass == null + && !protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype) && !isPasswordlessKeyStore) { printNoIntegrityWarning(); @@ -1686,6 +1687,7 @@ private void doExportCert(String alias, PrintStream out) throws Exception { if (storePass == null + && !protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype) && !isPasswordlessKeyStore) { printNoIntegrityWarning(); diff --git a/src/java.base/share/classes/sun/security/util/Debug.java b/src/java.base/share/classes/sun/security/util/Debug.java index aca672bdb3152..5a197cd242fb7 100644 --- a/src/java.base/share/classes/sun/security/util/Debug.java +++ b/src/java.base/share/classes/sun/security/util/Debug.java @@ -27,6 +27,9 @@ import java.io.PrintStream; import java.math.BigInteger; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.HexFormat; import java.util.regex.Pattern; import java.util.regex.Matcher; @@ -41,8 +44,14 @@ public class Debug { private String prefix; + private boolean printDateTime; + private boolean printThreadDetails; private static String args; + private static boolean threadInfoAll; + private static boolean timeStampInfoAll; + private static final String TIMESTAMP_OPTION = "+timestamp"; + private static final String THREAD_OPTION = "+thread"; static { args = GetPropertyAction.privilegedGetProperty("java.security.debug"); @@ -61,12 +70,21 @@ public class Debug { args = marshal(args); if (args.equals("help")) { Help(); + } else if (args.contains("all")) { + // "all" option has special handling for decorator options + // If the thread or timestamp decorator option is detected + // with the "all" option, then it impacts decorator options + // for other categories + int beginIndex = args.lastIndexOf("all") + "all".length(); + int commaIndex = args.indexOf(',', beginIndex); + if (commaIndex == -1) commaIndex = args.length(); + threadInfoAll = args.substring(beginIndex, commaIndex).contains(THREAD_OPTION); + timeStampInfoAll = args.substring(beginIndex, commaIndex).contains(TIMESTAMP_OPTION); } } } - public static void Help() - { + public static void Help() { System.err.println(); System.err.println("all turn on all debugging"); System.err.println("access print all checkPermission results"); @@ -92,6 +110,11 @@ public static void Help() System.err.println("securerandom SecureRandom"); System.err.println("ts timestamping"); System.err.println(); + System.err.println("+timestamp can be appended to any of above options to print"); + System.err.println(" a timestamp for that debug option"); + System.err.println("+thread can be appended to any of above options to print"); + System.err.println(" thread and caller information for that debug option"); + System.err.println(); System.err.println("The following can be used with access:"); System.err.println(); System.err.println("stack include stack trace"); @@ -132,8 +155,7 @@ public static void Help() * option is set. Set the prefix to be the same as option. */ - public static Debug getInstance(String option) - { + public static Debug getInstance(String option) { return getInstance(option, option); } @@ -141,23 +163,57 @@ public static Debug getInstance(String option) * Get a Debug object corresponding to whether or not the given * option is set. Set the prefix to be prefix. */ - public static Debug getInstance(String option, String prefix) - { + public static Debug getInstance(String option, String prefix) { if (isOn(option)) { Debug d = new Debug(); d.prefix = prefix; + d.configureExtras(option); return d; } else { return null; } } + private static String formatCaller() { + return StackWalker.getInstance().walk(s -> + s.dropWhile(f -> + f.getClassName().startsWith("sun.security.util.Debug")) + .map(f -> f.getFileName() + ":" + f.getLineNumber()) + .findFirst().orElse("unknown caller")); + } + + // parse an option string to determine if extra details, + // like thread and timestamp, should be printed + private void configureExtras(String option) { + // treat "all" as special case, only used for java.security.debug property + this.printDateTime = timeStampInfoAll; + this.printThreadDetails = threadInfoAll; + + if (printDateTime && printThreadDetails) { + // nothing left to configure + return; + } + + // args is converted to lower case for the most part via marshal method + int optionIndex = args.lastIndexOf(option); + if (optionIndex == -1) { + // option not in args list. Only here since "all" was present + // in debug property argument. "all" option already parsed + return; + } + int beginIndex = optionIndex + option.length(); + int commaIndex = args.indexOf(',', beginIndex); + if (commaIndex == -1) commaIndex = args.length(); + String subOpt = args.substring(beginIndex, commaIndex); + printDateTime = printDateTime || subOpt.contains(TIMESTAMP_OPTION); + printThreadDetails = printThreadDetails || subOpt.contains(THREAD_OPTION); + } + /** * True if the system property "security.debug" contains the * string "option". */ - public static boolean isOn(String option) - { + public static boolean isOn(String option) { if (args == null) return false; else { @@ -180,18 +236,16 @@ public static boolean isVerbose() { * created from the call to getInstance. */ - public void println(String message) - { - System.err.println(prefix + ": "+message); + public void println(String message) { + System.err.println(prefix + extraInfo() + ": " + message); } /** * print a message to stderr that is prefixed with the prefix * created from the call to getInstance and obj. */ - public void println(Object obj, String message) - { - System.err.println(prefix + " [" + obj.getClass().getSimpleName() + + public void println(Object obj, String message) { + System.err.println(prefix + extraInfo() + " [" + obj.getClass().getSimpleName() + "@" + System.identityHashCode(obj) + "]: "+message); } @@ -199,18 +253,36 @@ public void println(Object obj, String message) * print a blank line to stderr that is prefixed with the prefix. */ - public void println() - { - System.err.println(prefix + ":"); + public void println() { + System.err.println(prefix + extraInfo() + ":"); } /** * print a message to stderr that is prefixed with the prefix. */ - public static void println(String prefix, String message) - { - System.err.println(prefix + ": "+message); + public void println(String prefix, String message) { + System.err.println(prefix + extraInfo() + ": " + message); + } + + /** + * If thread debug option enabled, include information containing + * hex value of threadId and the current thread name + * If timestamp debug option enabled, include timestamp string + * @return extra info if debug option enabled. + */ + private String extraInfo() { + String retString = ""; + if (printThreadDetails) { + retString = "0x" + Long.toHexString( + Thread.currentThread().getId()).toUpperCase(Locale.ROOT) + + "|" + Thread.currentThread().getName() + "|" + formatCaller(); + } + if (printDateTime) { + retString += (retString.isEmpty() ? "" : "|") + + FormatHolder.DATE_TIME_FORMATTER.format(Instant.now()); + } + return retString.isEmpty() ? "" : "[" + retString + "]"; } /** @@ -326,4 +398,15 @@ public static String toString(byte[] b) { return HexFormat.ofDelimiter(":").formatHex(b); } + public static String toString(BigInteger b) { + return toString(b.toByteArray()); + } + + // Holder class to break cyclic dependency seen during build + private static class FormatHolder { + private static final String PATTERN = "yyyy-MM-dd kk:mm:ss.SSS"; + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter + .ofPattern(PATTERN, Locale.ENGLISH) + .withZone(ZoneId.systemDefault()); + } } diff --git a/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java b/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java index 9025fe0788f12..aab02e46d78fd 100644 --- a/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java +++ b/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -127,13 +127,24 @@ public final class ObjectIdentifier implements Serializable { // Is the components field calculated? private transient boolean componentsCalculated = false; + /** + * Restores the state of this object from the stream. + * + * @param is the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ @java.io.Serial private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { is.defaultReadObject(); if (encoding == null) { // from an old version - int[] comp = (int[])components; + if (components == null) { + throw new InvalidObjectException("OID components is null"); + } + + int[] comp = ((int[]) components).clone(); if (componentLen > comp.length) { componentLen = comp.length; } @@ -142,7 +153,9 @@ private void readObject(ObjectInputStream is) // will be performed again in init(). checkOidSize(componentLen); init(comp, componentLen); + components = comp; } else { + encoding = encoding.clone(); // defensive copying checkOidSize(encoding.length); check(encoding); } @@ -261,6 +274,7 @@ public ObjectIdentifier(DerInputStream in) throws IOException { encoding = in.getDerValue().getOID().encoding; } + // set 'encoding' field based on the specified 'components' and 'length' private void init(int[] components, int length) throws IOException { int pos = 0; byte[] tmp = new byte[length * 5 + 1]; // +1 for empty input diff --git a/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java b/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java index 8e201e550ed03..7e0706e19bb54 100644 --- a/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java +++ b/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,6 +53,38 @@ void checkDistrust(String variant, X509Certificate[] chain) } SymantecTLSPolicy.checkDistrust(chain); } + }, + + /** + * Distrust TLS Server certificates anchored by an Entrust root CA and + * issued after November 11, 2024. If enabled, this policy is currently + * enforced by the PKIX and SunX509 TrustManager implementations + * of the SunJSSE provider implementation. + */ + ENTRUST_TLS { + void checkDistrust(String variant, X509Certificate[] chain) + throws ValidatorException { + if (!variant.equals(Validator.VAR_TLS_SERVER)) { + return; + } + EntrustTLSPolicy.checkDistrust(chain); + } + }, + + /** + * Distrust TLS Server certificates anchored by a CAMERFIRMA root CA and + * issued after April 15, 2025. If enabled, this policy is currently + * enforced by the PKIX and SunX509 TrustManager implementations + * of the SunJSSE provider implementation. + */ + CAMERFIRMA_TLS { + void checkDistrust(String variant, X509Certificate[] chain) + throws ValidatorException { + if (!variant.equals(Validator.VAR_TLS_SERVER)) { + return; + } + CamerfirmaTLSPolicy.checkDistrust(chain); + } }; /** diff --git a/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java new file mode 100644 index 0000000000000..591a7a26c6893 --- /dev/null +++ b/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.validator; + +import java.security.cert.X509Certificate; +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import sun.security.util.Debug; +import sun.security.x509.X509CertImpl; + +/** + * This class checks if Camerfirma issued TLS Server certificates should be + * restricted. + */ +final class CamerfirmaTLSPolicy { + + private static final Debug debug = Debug.getInstance("certpath"); + + // SHA-256 certificate fingerprints of distrusted roots + private static final Set FINGERPRINTS = Set.of( + // cacerts alias: camerfirmachamberscommerceca + // DN: CN=Chambers of Commerce Root, + // OU=http://www.chambersign.org, + // O=AC Camerfirma SA CIF A82743287, C=EU + "0C258A12A5674AEF25F28BA7DCFAECEEA348E541E6F5CC4EE63B71B361606AC3", + // cacerts alias: camerfirmachambersca + // DN: CN=Chambers of Commerce Root - 2008, + // O=AC Camerfirma S.A., SERIALNUMBER=A82743287, + // L=Madrid (see current address at www.camerfirma.com/address), + // C=EU + "063E4AFAC491DFD332F3089B8542E94617D893D7FE944E10A7937EE29D9693C0", + // cacerts alias: camerfirmachambersignca + // DN: CN=Global Chambersign Root - 2008, + // O=AC Camerfirma S.A., SERIALNUMBER=A82743287, + // L=Madrid (see current address at www.camerfirma.com/address), + // C=EU + "136335439334A7698016A0D324DE72284E079D7B5220BB8FBD747816EEBEBACA" + ); + + // Any TLS Server certificate that is anchored by one of the Camerfirma + // roots above and is issued after this date will be distrusted. + private static final LocalDate APRIL_15_2025 = + LocalDate.of(2025, Month.APRIL, 15); + + /** + * This method assumes the eeCert is a TLS Server Cert and chains back to + * the anchor. + * + * @param chain the end-entity's certificate chain. The end entity cert + * is at index 0, the trust anchor at index n-1. + * @throws ValidatorException if the certificate is distrusted + */ + static void checkDistrust(X509Certificate[] chain) + throws ValidatorException { + X509Certificate anchor = chain[chain.length-1]; + String fp = fingerprint(anchor); + if (fp == null) { + throw new ValidatorException("Cannot generate fingerprint for " + + "trust anchor of TLS server certificate"); + } + if (FINGERPRINTS.contains(fp)) { + Date notBefore = chain[0].getNotBefore(); + LocalDate ldNotBefore = LocalDate.ofInstant(notBefore.toInstant(), + ZoneOffset.UTC); + // reject if certificate is issued after April 15, 2025 + checkNotBefore(ldNotBefore, APRIL_15_2025, anchor); + } + } + + private static String fingerprint(X509Certificate cert) { + return X509CertImpl.getFingerprint("SHA-256", cert, debug); + } + + private static void checkNotBefore(LocalDate notBeforeDate, + LocalDate distrustDate, X509Certificate anchor) + throws ValidatorException { + if (notBeforeDate.isAfter(distrustDate)) { + throw new ValidatorException + ("TLS Server certificate issued after " + distrustDate + + " and anchored by a distrusted legacy Camerfirma root CA: " + + anchor.getSubjectX500Principal(), + ValidatorException.T_UNTRUSTED_CERT, anchor); + } + } + + private CamerfirmaTLSPolicy() {} +} diff --git a/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java new file mode 100644 index 0000000000000..4c4906d8eb398 --- /dev/null +++ b/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.validator; + +import java.security.cert.X509Certificate; +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import sun.security.util.Debug; +import sun.security.x509.X509CertImpl; + +/** + * This class checks if Entrust issued TLS Server certificates should be + * restricted. + */ +final class EntrustTLSPolicy { + + private static final Debug debug = Debug.getInstance("certpath"); + + // SHA-256 certificate fingerprints of distrusted roots + private static final Set FINGERPRINTS = Set.of( + // cacerts alias: entrustevca + // DN: CN=Entrust Root Certification Authority, + // OU=(c) 2006 Entrust, Inc., + // OU=www.entrust.net/CPS is incorporated by reference, + // O=Entrust, Inc., C=US + "73C176434F1BC6D5ADF45B0E76E727287C8DE57616C1E6E6141A2B2CBC7D8E4C", + // cacerts alias: entrustrootcaec1 + // DN: CN=Entrust Root Certification Authority - EC1, + // OU=(c) 2012 Entrust, Inc. - for authorized use only, + // OU=See www.entrust.net/legal-terms, O=Entrust, Inc., C=US + "02ED0EB28C14DA45165C566791700D6451D7FB56F0B2AB1D3B8EB070E56EDFF5", + // cacerts alias: entrustrootcag2 + // DN: CN=Entrust Root Certification Authority - G2, + // OU=(c) 2009 Entrust, Inc. - for authorized use only, + // OU=See www.entrust.net/legal-terms, O=Entrust, Inc., C=US + "43DF5774B03E7FEF5FE40D931A7BEDF1BB2E6B42738C4E6D3841103D3AA7F339", + // cacerts alias: entrustrootcag4 + // DN: CN=Entrust Root Certification Authority - G4 + // OU=(c) 2015 Entrust, Inc. - for authorized use only, + // OU=See www.entrust.net/legal-terms, O=Entrust, Inc., C=US, + "DB3517D1F6732A2D5AB97C533EC70779EE3270A62FB4AC4238372460E6F01E88", + // cacerts alias: entrust2048ca + // DN: CN=Entrust.net Certification Authority (2048), + // OU=(c) 1999 Entrust.net Limited, + // OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), + // O=Entrust.net + "6DC47172E01CBCB0BF62580D895FE2B8AC9AD4F873801E0C10B9C837D21EB177", + // cacerts alias: affirmtrustcommercialca + // DN: CN=AffirmTrust Commercial, O=AffirmTrust, C=US + "0376AB1D54C5F9803CE4B2E201A0EE7EEF7B57B636E8A93C9B8D4860C96F5FA7", + // cacerts alias: affirmtrustnetworkingca + // DN: CN=AffirmTrust Networking, O=AffirmTrust, C=US + "0A81EC5A929777F145904AF38D5D509F66B5E2C58FCDB531058B0E17F3F0B41B", + // cacerts alias: affirmtrustpremiumca + // DN: CN=AffirmTrust Premium, O=AffirmTrust, C=US + "70A73F7F376B60074248904534B11482D5BF0E698ECC498DF52577EBF2E93B9A", + // cacerts alias: affirmtrustpremiumeccca + // DN: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US + "BD71FDF6DA97E4CF62D1647ADD2581B07D79ADF8397EB4ECBA9C5E8488821423" + ); + + // Any TLS Server certificate that is anchored by one of the Entrust + // roots above and is issued after this date will be distrusted. + private static final LocalDate NOVEMBER_11_2024 = + LocalDate.of(2024, Month.NOVEMBER, 11); + + /** + * This method assumes the eeCert is a TLS Server Cert and chains back to + * the anchor. + * + * @param chain the end-entity's certificate chain. The end entity cert + * is at index 0, the trust anchor at index n-1. + * @throws ValidatorException if the certificate is distrusted + */ + static void checkDistrust(X509Certificate[] chain) + throws ValidatorException { + X509Certificate anchor = chain[chain.length-1]; + String fp = fingerprint(anchor); + if (fp == null) { + throw new ValidatorException("Cannot generate fingerprint for " + + "trust anchor of TLS server certificate"); + } + if (FINGERPRINTS.contains(fp)) { + Date notBefore = chain[0].getNotBefore(); + LocalDate ldNotBefore = LocalDate.ofInstant(notBefore.toInstant(), + ZoneOffset.UTC); + // reject if certificate is issued after November 11, 2024 + checkNotBefore(ldNotBefore, NOVEMBER_11_2024, anchor); + } + } + + private static String fingerprint(X509Certificate cert) { + return X509CertImpl.getFingerprint("SHA-256", cert, debug); + } + + private static void checkNotBefore(LocalDate notBeforeDate, + LocalDate distrustDate, X509Certificate anchor) + throws ValidatorException { + if (notBeforeDate.isAfter(distrustDate)) { + throw new ValidatorException + ("TLS Server certificate issued after " + distrustDate + + " and anchored by a distrusted legacy Entrust root CA: " + + anchor.getSubjectX500Principal(), + ValidatorException.T_UNTRUSTED_CERT, anchor); + } + } + + private EntrustTLSPolicy() {} +} diff --git a/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java b/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java index d0c7a83a7610d..e68419c150e99 100644 --- a/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java +++ b/src/java.base/share/classes/sun/security/x509/AlgIdDSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,13 @@ package sun.security.x509; +import java.io.ObjectInputStream; import java.io.IOException; +import java.io.InvalidObjectException; import java.math.BigInteger; import java.security.*; import java.security.interfaces.DSAParams; - +import java.util.Arrays; import sun.security.util.*; @@ -72,33 +74,42 @@ * * @author David Brownell */ -public final -class AlgIdDSA extends AlgorithmId implements DSAParams -{ +public final class AlgIdDSA extends AlgorithmId implements DSAParams { @java.io.Serial private static final long serialVersionUID = 3437177836797504046L; + private static class DSAComponents { + private final BigInteger p; + private final BigInteger q; + private final BigInteger g; + DSAComponents(BigInteger p, BigInteger q, BigInteger g) { + this.p = p; + this.q = q; + this.g = g; + } + } + /* * The three unsigned integer parameters. */ - private BigInteger p , q, g; + private BigInteger p, q, g; /** Returns the DSS/DSA parameter "P" */ - public BigInteger getP () { return p; } + public BigInteger getP() { return p; } /** Returns the DSS/DSA parameter "Q" */ - public BigInteger getQ () { return q; } + public BigInteger getQ() { return q; } /** Returns the DSS/DSA parameter "G" */ - public BigInteger getG () { return g; } + public BigInteger getG() { return g; } /** * Default constructor. The OID and parameters must be * deserialized before this algorithm ID is used. */ @Deprecated - public AlgIdDSA () {} + public AlgIdDSA() {} /** * Constructs a DSS/DSA Algorithm ID from numeric parameters. @@ -109,7 +120,7 @@ public AlgIdDSA () {} * @param q the DSS/DSA parameter "Q" * @param g the DSS/DSA parameter "G" */ - public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) { + public AlgIdDSA(BigInteger p, BigInteger q, BigInteger g) { super (DSA_oid); if (p != null || q != null || g != null) { @@ -120,8 +131,10 @@ public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) { this.p = p; this.q = q; this.g = g; - initializeParams (); - + // For algorithm IDs which haven't been created from a DER + // encoded value, need to create DER encoding and store it + // into "encodedParams" + encodedParams = encode(p, q, g); } catch (IOException e) { /* this should not happen */ throw new ProviderException ("Construct DSS/DSA Algorithm ID"); @@ -133,50 +146,10 @@ public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) { * Returns "DSA", indicating the Digital Signature Algorithm (DSA) as * defined by the Digital Signature Standard (DSS), FIPS 186. */ - public String getName () - { return "DSA"; } - - - /* - * For algorithm IDs which haven't been created from a DER encoded - * value, "params" must be created. - */ - private void initializeParams () throws IOException { - DerOutputStream out = new DerOutputStream(); - out.putInteger(p); - out.putInteger(q); - out.putInteger(g); - DerOutputStream result = new DerOutputStream(); - result.write(DerValue.tag_Sequence, out); - encodedParams = result.toByteArray(); - } - - /** - * Parses algorithm parameters P, Q, and G. They're found - * in the "params" member, which never needs to be changed. - */ - protected void decodeParams () throws IOException { - if (encodedParams == null) { - throw new IOException("DSA alg params are null"); - } - - DerValue params = new DerValue(encodedParams); - if (params.tag != DerValue.tag_Sequence) { - throw new IOException("DSA alg parsing error"); - } - - params.data.reset (); - - this.p = params.data.getBigInteger(); - this.q = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); - - if (params.data.available () != 0) - throw new IOException ("AlgIdDSA params, extra="+ - params.data.available ()); + public String getName() { + return "DSA"; } - /* * Returns a formatted string describing the parameters. */ @@ -197,4 +170,44 @@ protected String paramsToString () { "\n"; } } + + /** + * Restores the state of this object from the stream. Override to check + * on the 'p', 'q', 'g', and 'encodedParams'. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) throws IOException { + try { + stream.defaultReadObject(); + // if any of the 'p', 'q', 'g', 'encodedParams' is non-null, + // then they must be all non-null w/ matching encoding + if ((p != null || q != null || g != null || encodedParams != null) + && !Arrays.equals(encodedParams, encode(p, q, g))) { + throw new InvalidObjectException("Invalid DSA alg params"); + } + } catch (ClassNotFoundException e) { + throw new IOException(e); + } + } + + /* + * Create the DER encoding w/ the specified 'p', 'q', 'g' + */ + private static byte[] encode(BigInteger p, BigInteger q, + BigInteger g) throws IOException { + if (p == null || q == null || g == null) { + throw new InvalidObjectException("invalid null value"); + } + DerOutputStream out = new DerOutputStream(); + out.putInteger(p); + out.putInteger(q); + out.putInteger(g); + DerOutputStream result = new DerOutputStream(); + result.write(DerValue.tag_Sequence, out); + return result.toByteArray(); + } } diff --git a/src/java.base/share/classes/sun/security/x509/DNSName.java b/src/java.base/share/classes/sun/security/x509/DNSName.java index ee9e8a9c0a0ba..07259f4c7b93c 100644 --- a/src/java.base/share/classes/sun/security/x509/DNSName.java +++ b/src/java.base/share/classes/sun/security/x509/DNSName.java @@ -202,18 +202,12 @@ public int hashCode() { * . These results are used in checking NameConstraints during * certification path verification. *

    - * RFC5280: DNS name restrictions are expressed as host.example.com. + * RFC5280: For DNS names, restrictions MUST use the DNSName syntax in Section 4.2.1.6. * Any DNS name that can be constructed by simply adding zero or more * labels to the left-hand side of the name satisfies the name constraint. * For example, www.host.example.com would satisfy the constraint but * host1.example.com would not. *

    - * RFC 5280: DNSName restrictions are expressed as foo.bar.com. - * Any DNSName that - * can be constructed by simply adding to the left hand side of the name - * satisfies the name constraint. For example, www.foo.bar.com would - * satisfy the constraint but foo1.bar.com would not. - *

    * RFC1034: By convention, domain names can be stored with arbitrary case, but * domain name comparisons for all present domain functions are done in a * case-insensitive manner, assuming an ASCII character set, and a high @@ -238,13 +232,13 @@ else if (inputName.getType() != NAME_DNS) constraintType = NAME_MATCH; else if (thisName.endsWith(inName)) { int inNdx = thisName.lastIndexOf(inName); - if (thisName.charAt(inNdx-1) == '.' ) + if (thisName.charAt(inNdx-1) == '.' ^ inName.charAt(0) == '.') constraintType = NAME_WIDENS; else constraintType = NAME_SAME_TYPE; } else if (inName.endsWith(thisName)) { int ndx = inName.lastIndexOf(thisName); - if (inName.charAt(ndx-1) == '.' ) + if (inName.charAt(ndx-1) == '.' ^ thisName.charAt(0) == '.') constraintType = NAME_NARROWS; else constraintType = NAME_SAME_TYPE; diff --git a/src/java.base/share/classes/sun/security/x509/SerialNumber.java b/src/java.base/share/classes/sun/security/x509/SerialNumber.java index 2264edfd4ab97..2fea710a44b38 100644 --- a/src/java.base/share/classes/sun/security/x509/SerialNumber.java +++ b/src/java.base/share/classes/sun/security/x509/SerialNumber.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; +import java.util.HexFormat; import sun.security.util.*; @@ -101,7 +102,7 @@ public SerialNumber(InputStream in) throws IOException { * Return the SerialNumber as user readable string. */ public String toString() { - return "SerialNumber: [" + Debug.toHexString(serialNum) + ']'; + return "SerialNumber: " + Debug.toString(serialNum); } /** diff --git a/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties index c231cadc4164a..3ed4f873a3c63 100644 --- a/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties +++ b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -284,6 +284,7 @@ ZAR=ZAR ZMK=ZMK ZMW=ZMW ZWD=ZWD +ZWG=ZWG ZWL=ZWL ZWN=ZWN ZWR=ZWR @@ -509,5 +510,6 @@ yum=Yugoslavian New Dinar (1994-2002) zar=South African Rand zmk=Zambian Kwacha zwd=Zimbabwean Dollar (1980-2008) +zwg=Zimbabwe Gold zwl=Zimbabwean Dollar (2009) zwr=Zimbabwean Dollar (2008) diff --git a/src/java.base/share/conf/net.properties b/src/java.base/share/conf/net.properties index 67f294355a10e..2aa9a9630bed2 100644 --- a/src/java.base/share/conf/net.properties +++ b/src/java.base/share/conf/net.properties @@ -130,3 +130,20 @@ jdk.http.auth.tunneling.disabledSchemes=Basic #jdk.http.ntlm.transparentAuth=trustedHosts # jdk.http.ntlm.transparentAuth=disabled + +# +# Maximum HTTP field section size that a client is prepared to accept +# +# jdk.http.maxHeaderSize=393216 +# +# This is the maximum header field section size that a client is prepared to accept. +# This is computed as the sum of the size of the uncompressed header name, plus +# the size of the uncompressed header value, plus an overhead of 32 bytes for +# each field section line. If a peer sends a field section that exceeds this +# size a {@link java.net.ProtocolException ProtocolException} will be raised. +# This applies to all versions of the HTTP protocol. A value of zero or a negative +# value means no limit. If left unspecified, the default value is 393216 bytes +# or 384kB. +# +# Note: This property is currently used by the JDK Reference implementation. It +# is not guaranteed to be examined and used by other implementations. diff --git a/src/java.base/share/conf/security/java.policy b/src/java.base/share/conf/security/java.policy index 1554541d126f9..bac4a949d4c35 100644 --- a/src/java.base/share/conf/security/java.policy +++ b/src/java.base/share/conf/security/java.policy @@ -30,6 +30,8 @@ grant { permission java.util.PropertyPermission "line.separator", "read"; permission java.util.PropertyPermission "java.specification.version", "read"; + permission java.util.PropertyPermission + "java.specification.maintenance.version", "read"; permission java.util.PropertyPermission "java.specification.vendor", "read"; permission java.util.PropertyPermission "java.specification.name", "read"; permission java.util.PropertyPermission diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 2726391bceaee..08effe23fce47 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -796,7 +796,8 @@ jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \ # jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048, \ # rsa_pkcs1_sha1, secp224r1 jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ - MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL + MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \ + ECDH # # Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) @@ -1339,6 +1340,12 @@ jdk.sasl.disabledMechanisms= # A4FE7C7F15155F3F0AEF7AAA83CF6E06DEB97CA3F909DF920AC1490882D488ED # Distrust after December 31, 2019. # +# ENTRUST_TLS : Distrust TLS Server certificates anchored by +# an Entrust root CA and issued after November 11, 2024. +# +# CAMERFIRMA_TLS : Distrust TLS Server certificates anchored by +# a Camerfirma root CA and issued after April 15, 2025. +# # Leading and trailing whitespace surrounding each value are ignored. # Unknown values are ignored. If the property is commented out or set to the # empty String, no policies are enforced. @@ -1350,7 +1357,7 @@ jdk.sasl.disabledMechanisms= # jdk.certpath.disabledAlgorithms; those restrictions are still enforced even # if this property is not enabled. # -jdk.security.caDistrustPolicies=SYMANTEC_TLS +jdk.security.caDistrustPolicies=SYMANTEC_TLS,ENTRUST_TLS,CAMERFIRMA_TLS # # FilePermission path canonicalization diff --git a/src/java.base/share/legal/public_suffix.md b/src/java.base/share/legal/public_suffix.md index 2c8c97127876b..6bd89c8d7cf02 100644 --- a/src/java.base/share/legal/public_suffix.md +++ b/src/java.base/share/legal/public_suffix.md @@ -11,7 +11,7 @@ If you do not wish to use the Public Suffix List, you may remove the The Source Code of this file is available under the Mozilla Public License, v. 2.0 and is located at -https://raw.githubusercontent.com/publicsuffix/list/b5bf572c52988dbe9d865b8f090ea819024a9936/public_suffix_list.dat. +https://raw.githubusercontent.com/publicsuffix/list/1cbd6e71a9b83620b1d0b11e49d3d9ff48c27e22/public_suffix_list.dat. If a copy of the MPL was not distributed with this file, you can obtain one at https://mozilla.org/MPL/2.0/. diff --git a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c index 2993d2551f6e3..18c515d867894 100644 --- a/src/java.base/share/native/libjli/java.c +++ b/src/java.base/share/native/libjli/java.c @@ -908,10 +908,11 @@ SetClassPath(const char *s) if (sizeof(format) - 2 + JLI_StrLen(s) < JLI_StrLen(s)) // s is became corrupted after expanding wildcards return; - def = JLI_MemAlloc(sizeof(format) + size_t defSize = sizeof(format) - 2 /* strlen("%s") */ - + JLI_StrLen(s)); - sprintf(def, format, s); + + JLI_StrLen(s); + def = JLI_MemAlloc(defSize); + snprintf(def, defSize, format, s); AddOption(def, NULL); if (s != orig) JLI_MemFree((char *) s); @@ -1368,8 +1369,9 @@ ParseArguments(int *pargc, char ***pargv, JLI_StrCCmp(arg, "-oss") == 0 || JLI_StrCCmp(arg, "-ms") == 0 || JLI_StrCCmp(arg, "-mx") == 0) { - char *tmp = JLI_MemAlloc(JLI_StrLen(arg) + 6); - sprintf(tmp, "-X%s", arg + 1); /* skip '-' */ + size_t tmpSize = JLI_StrLen(arg) + 6; + char *tmp = JLI_MemAlloc(tmpSize); + snprintf(tmp, tmpSize, "-X%s", arg + 1); /* skip '-' */ AddOption(tmp, NULL); } else if (JLI_StrCmp(arg, "-checksource") == 0 || JLI_StrCmp(arg, "-cs") == 0 || @@ -1701,8 +1703,9 @@ AddApplicationOptions(int cpathc, const char **cpathv) s = (char *) JLI_WildcardExpandClasspath(s); /* 40 for -Denv.class.path= */ if (JLI_StrLen(s) + 40 > JLI_StrLen(s)) { // Safeguard from overflow - envcp = (char *)JLI_MemAlloc(JLI_StrLen(s) + 40); - sprintf(envcp, "-Denv.class.path=%s", s); + size_t envcpSize = JLI_StrLen(s) + 40; + envcp = (char *)JLI_MemAlloc(envcpSize); + snprintf(envcp, envcpSize, "-Denv.class.path=%s", s); AddOption(envcp, NULL); } } @@ -1714,8 +1717,9 @@ AddApplicationOptions(int cpathc, const char **cpathv) } /* 40 for '-Dapplication.home=' */ - apphome = (char *)JLI_MemAlloc(JLI_StrLen(home) + 40); - sprintf(apphome, "-Dapplication.home=%s", home); + size_t apphomeSize = JLI_StrLen(home) + 40; + apphome = (char *)JLI_MemAlloc(apphomeSize); + snprintf(apphome, apphomeSize, "-Dapplication.home=%s", home); AddOption(apphome, NULL); /* How big is the application's classpath? */ diff --git a/src/java.base/share/native/libzip/zip_util.c b/src/java.base/share/native/libzip/zip_util.c index fbbd9d850fcc7..68fd756ada34c 100644 --- a/src/java.base/share/native/libzip/zip_util.c +++ b/src/java.base/share/native/libzip/zip_util.c @@ -442,7 +442,7 @@ hash(const char *s) static unsigned int hashN(const char *s, int length) { - int h = 0; + unsigned int h = 0; while (length-- > 0) h = 31*h + *s++; return h; diff --git a/src/java.base/unix/classes/sun/security/provider/NativePRNG.java b/src/java.base/unix/classes/sun/security/provider/NativePRNG.java index 5bdd85c4f898c..a3ebfdf271d85 100644 --- a/src/java.base/unix/classes/sun/security/provider/NativePRNG.java +++ b/src/java.base/unix/classes/sun/security/provider/NativePRNG.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -202,11 +202,13 @@ static boolean isAvailable() { } // constructor, called by the JCA framework - public NativePRNG() { - super(); + public NativePRNG(SecureRandomParameters params) { if (INSTANCE == null) { throw new AssertionError("NativePRNG not available"); } + if (params != null) { + throw new IllegalArgumentException("Unsupported params: " + params.getClass()); + } } // set the seed @@ -250,11 +252,13 @@ static boolean isAvailable() { } // constructor, called by the JCA framework - public Blocking() { - super(); + public Blocking(SecureRandomParameters params) { if (INSTANCE == null) { throw new AssertionError("NativePRNG$Blocking not available"); } + if (params != null) { + throw new IllegalArgumentException("Unsupported params: " + params.getClass()); + } } // set the seed @@ -299,12 +303,14 @@ static boolean isAvailable() { } // constructor, called by the JCA framework - public NonBlocking() { - super(); + public NonBlocking(SecureRandomParameters params) { if (INSTANCE == null) { throw new AssertionError( "NativePRNG$NonBlocking not available"); } + if (params != null) { + throw new IllegalArgumentException("Unsupported params: " + params.getClass()); + } } // set the seed diff --git a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c index 7f86826f077d2..5f2f04da52c93 100644 --- a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c +++ b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,10 @@ extern int errno; #define ERR_PIPE 2 #define ERR_ARGS 3 +#ifndef VERSION_STRING +#error VERSION_STRING must be defined +#endif + void error (int fd, int err) { if (write (fd, &err, sizeof(err)) != sizeof(err)) { /* Not sure what to do here. I have no one to speak to. */ @@ -58,10 +63,12 @@ void error (int fd, int err) { } void shutItDown() { + fprintf(stdout, "jspawnhelper version %s\n", VERSION_STRING); fprintf(stdout, "This command is not for general use and should "); fprintf(stdout, "only be run as the result of a call to\n"); fprintf(stdout, "ProcessBuilder.start() or Runtime.exec() in a java "); fprintf(stdout, "application\n"); + fflush(stdout); _exit(1); } @@ -145,12 +152,26 @@ int main(int argc, char *argv[]) { #ifdef DEBUG jtregSimulateCrash(0, 4); #endif - r = sscanf (argv[1], "%d:%d:%d", &fdinr, &fdinw, &fdout); + + if (argc != 3) { + fprintf(stdout, "Incorrect number of arguments: %d\n", argc); + shutItDown(); + } + + if (strcmp(argv[1], VERSION_STRING) != 0) { + fprintf(stdout, "Incorrect Java version: %s\n", argv[1]); + shutItDown(); + } + + r = sscanf (argv[2], "%d:%d:%d", &fdinr, &fdinw, &fdout); if (r == 3 && fcntl(fdinr, F_GETFD) != -1 && fcntl(fdinw, F_GETFD) != -1) { fstat(fdinr, &buf); - if (!S_ISFIFO(buf.st_mode)) + if (!S_ISFIFO(buf.st_mode)) { + fprintf(stdout, "Incorrect input pipe\n"); shutItDown(); + } } else { + fprintf(stdout, "Incorrect FD array data: %s\n", argv[2]); shutItDown(); } // Close the writing end of the pipe we use for reading from the parent. diff --git a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c index d53e88764c589..8b900dcf6f64f 100644 --- a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c +++ b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c @@ -537,7 +537,7 @@ jint unix_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, * position integer as a filename. */ if ((dir = opendir("/proc")) == NULL) { - JNU_ThrowByNameWithLastError(env, + JNU_ThrowByNameWithMessageAndLastError(env, "java/lang/RuntimeException", "Unable to open /proc"); return -1; } diff --git a/src/java.base/unix/native/libjava/ProcessImpl_md.c b/src/java.base/unix/native/libjava/ProcessImpl_md.c index bb340a8f120a6..d9a16dad4223a 100644 --- a/src/java.base/unix/native/libjava/ProcessImpl_md.c +++ b/src/java.base/unix/native/libjava/ProcessImpl_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -300,6 +300,10 @@ Java_java_lang_ProcessImpl_init(JNIEnv *env, jclass clazz) #define WTERMSIG(status) ((status)&0x7F) #endif +#ifndef VERSION_STRING +#error VERSION_STRING must be defined +#endif + static const char * getBytes(JNIEnv *env, jbyteArray arr) { @@ -491,7 +495,7 @@ spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) jboolean isCopy; int i, offset, rval, bufsize, magic; char *buf, buf1[(3 * 11) + 3]; // "%d:%d:%d\0" - char *hlpargs[3]; + char *hlpargs[4]; SpawnInfo sp; /* need to tell helper which fd is for receiving the childstuff @@ -500,11 +504,13 @@ spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) snprintf(buf1, sizeof(buf1), "%d:%d:%d", c->childenv[0], c->childenv[1], c->fail[1]); /* NULL-terminated argv array. * argv[0] contains path to jspawnhelper, to follow conventions. - * argv[1] contains the fd string as argument to jspawnhelper + * argv[1] contains the version string as argument to jspawnhelper + * argv[2] contains the fd string as argument to jspawnhelper */ hlpargs[0] = (char*)helperpath; - hlpargs[1] = buf1; - hlpargs[2] = NULL; + hlpargs[1] = VERSION_STRING; + hlpargs[2] = buf1; + hlpargs[3] = NULL; /* Following items are sent down the pipe to the helper * after it is spawned. @@ -556,8 +562,17 @@ spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) } offset = copystrings(buf, 0, &c->argv[0]); offset = copystrings(buf, offset, &c->envv[0]); - memcpy(buf+offset, c->pdir, sp.dirlen); - offset += sp.dirlen; + if (c->pdir != NULL) { + if (sp.dirlen > 0) { + memcpy(buf+offset, c->pdir, sp.dirlen); + offset += sp.dirlen; + } + } else { + if (sp.dirlen > 0) { + free(buf); + return -1; + } + } offset = copystrings(buf, offset, parentPathv); assert(offset == bufsize); diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index 660665392c12d..5c40fc8b3206e 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -559,14 +559,14 @@ getGMTOffsetID() // Ignore daylight saving settings to calculate current time difference localtm.tm_isdst = 0; int gmt_off = (int)(difftime(mktime(&localtm), mktime(&gmt)) / 60.0); - sprintf(buf, (const char *)"GMT%c%02.2d:%02.2d", + snprintf(buf, sizeof(buf), (const char *)"GMT%c%02.2d:%02.2d", gmt_off < 0 ? '-' : '+' , abs(gmt_off / 60), gmt_off % 60); #else if (strftime(offset, 6, "%z", &localtm) != 5) { return strdup("GMT"); } - sprintf(buf, (const char *)"GMT%c%c%c:%c%c", offset[0], offset[1], offset[2], + snprintf(buf, sizeof(buf), (const char *)"GMT%c%c%c:%c%c", offset[0], offset[1], offset[2], offset[3], offset[4]); #endif return strdup(buf); diff --git a/src/java.base/unix/native/libjli/java_md.c b/src/java.base/unix/native/libjli/java_md.c index 503a2457b0438..71c162b18132a 100644 --- a/src/java.base/unix/native/libjli/java_md.c +++ b/src/java.base/unix/native/libjli/java_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -388,7 +388,7 @@ CreateExecutionEnvironment(int *pargc, char ***pargv, if (lastslash) *lastslash = '\0'; - sprintf(new_runpath, LD_LIBRARY_PATH "=" + snprintf(new_runpath, new_runpath_size, LD_LIBRARY_PATH "=" "%s:" "%s/lib:" "%s/../lib", diff --git a/src/java.base/unix/native/libjsig/jsig.c b/src/java.base/unix/native/libjsig/jsig.c index 1a0a6bec892b5..409b9399e8d99 100644 --- a/src/java.base/unix/native/libjsig/jsig.c +++ b/src/java.base/unix/native/libjsig/jsig.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2015 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -102,9 +102,9 @@ static sa_handler_t call_os_signal(int sig, sa_handler_t disp, if (os_signal == NULL) { // Deprecation warning first time through - printf(HOTSPOT_VM_DISTRO " VM warning: the use of signal() and sigset() " - "for signal chaining was deprecated in version 16.0 and will " - "be removed in a future release. Use sigaction() instead.\n"); + fprintf(stderr, HOTSPOT_VM_DISTRO " VM warning: the use of signal() and sigset() " + "for signal chaining was deprecated in version 16.0 and will " + "be removed in a future release. Use sigaction() instead.\n"); if (!is_sigset) { os_signal = (signal_function_t)dlsym(RTLD_NEXT, "signal"); } else { diff --git a/src/java.base/unix/native/libnet/NetworkInterface.c b/src/java.base/unix/native/libnet/NetworkInterface.c index 990bc1bcc5101..dcaf3fd51d11c 100644 --- a/src/java.base/unix/native/libnet/NetworkInterface.c +++ b/src/java.base/unix/native/libnet/NetworkInterface.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1271,7 +1271,7 @@ static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) { char addr6[40]; struct sockaddr_in6 addr; - sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", + snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7]); diff --git a/src/java.base/unix/native/libnet/net_util_md.c b/src/java.base/unix/native/libnet/net_util_md.c index 4ec11a1362622..139cb27b4fb8b 100644 --- a/src/java.base/unix/native/libnet/net_util_md.c +++ b/src/java.base/unix/native/libnet/net_util_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -221,7 +221,7 @@ void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env, buf = (char *) malloc(size); if (buf) { jstring s; - sprintf(buf, format, hostname, error_string); + snprintf(buf, size, format, hostname, error_string); s = JNU_NewStringPlatform(env, buf); if (s != NULL) { jobject x = JNU_NewObjectByName(env, diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index be578a0bb4b85..7bc14a0da977f 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -154,9 +154,10 @@ struct my_statx #define STATX_MODE 0x00000002U #endif -#ifndef STATX_ALL -#define STATX_ALL (STATX_BTIME | STATX_BASIC_STATS) -#endif +// +// STATX_ALL is deprecated; use a different name to avoid confusion. +// +#define LOCAL_STATX_ALL (STATX_BASIC_STATS | STATX_BTIME) #ifndef AT_FDCWD #define AT_FDCWD -100 @@ -632,8 +633,19 @@ static void copy_statx_attributes(JNIEnv* env, struct my_statx* buf, jobject att (*env)->SetLongField(env, attrs, attrs_st_atime_sec, (jlong)buf->stx_atime.tv_sec); (*env)->SetLongField(env, attrs, attrs_st_mtime_sec, (jlong)buf->stx_mtime.tv_sec); (*env)->SetLongField(env, attrs, attrs_st_ctime_sec, (jlong)buf->stx_ctime.tv_sec); - (*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, (jlong)buf->stx_btime.tv_sec); - (*env)->SetLongField(env, attrs, attrs_st_birthtime_nsec, (jlong)buf->stx_btime.tv_nsec); + if ((buf->stx_mask & STATX_BTIME) != 0) { + // Birth time was filled in so use it + (*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, + (jlong)buf->stx_btime.tv_sec); + (*env)->SetLongField(env, attrs, attrs_st_birthtime_nsec, + (jlong)buf->stx_btime.tv_nsec); + } else { + // Birth time was not filled in: fall back to last modification time + (*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, + (jlong)buf->stx_mtime.tv_sec); + (*env)->SetLongField(env, attrs, attrs_st_birthtime_nsec, + (jlong)buf->stx_mtime.tv_nsec); + } (*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->stx_atime.tv_nsec); (*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->stx_mtime.tv_nsec); (*env)->SetLongField(env, attrs, attrs_st_ctime_nsec, (jlong)buf->stx_ctime.tv_nsec); @@ -687,18 +699,20 @@ Java_sun_nio_fs_UnixNativeDispatcher_stat0(JNIEnv* env, jclass this, #if defined(__linux__) struct my_statx statx_buf; int flags = AT_STATX_SYNC_AS_STAT; - unsigned int mask = STATX_ALL; + unsigned int mask = LOCAL_STATX_ALL; if (my_statx_func != NULL) { // Prefer statx over stat64 on Linux if it's available + // statx is not allowed on the old Docker versions and returns EPERM, + // fallback to stat64 in this case RESTARTABLE(statx_wrapper(AT_FDCWD, path, flags, mask, &statx_buf), err); if (err == 0) { copy_statx_attributes(env, &statx_buf, attrs); - } else { + return; + } else if (errno != EPERM) { throwUnixException(env, errno); + return; } - // statx was available, so return now - return; } #endif RESTARTABLE(stat64(path, &buf), err); @@ -722,10 +736,12 @@ Java_sun_nio_fs_UnixNativeDispatcher_stat1(JNIEnv* env, jclass this, jlong pathA if (my_statx_func != NULL) { // Prefer statx over stat64 on Linux if it's available + // statx is not allowed on the old Docker versions and returns EPERM, + // fallback to stat64 in this case RESTARTABLE(statx_wrapper(AT_FDCWD, path, flags, mask, &statx_buf), err); if (err == 0) { return (jint)statx_buf.stx_mode; - } else { + } else if (errno != EPERM) { return 0; } } @@ -748,18 +764,20 @@ Java_sun_nio_fs_UnixNativeDispatcher_lstat0(JNIEnv* env, jclass this, #if defined(__linux__) struct my_statx statx_buf; int flags = AT_STATX_SYNC_AS_STAT | AT_SYMLINK_NOFOLLOW; - unsigned int mask = STATX_ALL; + unsigned int mask = LOCAL_STATX_ALL; if (my_statx_func != NULL) { // Prefer statx over stat64 on Linux if it's available + // statx is not allowed on the old Docker versions and returns EPERM, + // fallback to lstat64 in this case RESTARTABLE(statx_wrapper(AT_FDCWD, path, flags, mask, &statx_buf), err); if (err == 0) { copy_statx_attributes(env, &statx_buf, attrs); - } else { + return; + } else if (errno != EPERM) { throwUnixException(env, errno); + return; } - // statx was available, so return now - return; } #endif RESTARTABLE(lstat64(path, &buf), err); @@ -779,19 +797,21 @@ Java_sun_nio_fs_UnixNativeDispatcher_fstat(JNIEnv* env, jclass this, jint fd, #if defined(__linux__) struct my_statx statx_buf; int flags = AT_EMPTY_PATH | AT_STATX_SYNC_AS_STAT; - unsigned int mask = STATX_ALL; + unsigned int mask = LOCAL_STATX_ALL; if (my_statx_func != NULL) { // statx supports FD use via dirfd iff pathname is an empty string and the // AT_EMPTY_PATH flag is specified in flags + // statx is not allowed on the old Docker versions and returns EPERM, + // fallback to fstat64 in this case RESTARTABLE(statx_wrapper((int)fd, "", flags, mask, &statx_buf), err); if (err == 0) { copy_statx_attributes(env, &statx_buf, attrs); - } else { + return; + } else if (errno != EPERM) { throwUnixException(env, errno); + return; } - // statx was available, so return now - return; } #endif RESTARTABLE(fstat64((int)fd, &buf), err); @@ -812,21 +832,23 @@ Java_sun_nio_fs_UnixNativeDispatcher_fstatat0(JNIEnv* env, jclass this, jint dfd #if defined(__linux__) struct my_statx statx_buf; int flags = AT_STATX_SYNC_AS_STAT; - unsigned int mask = STATX_ALL; + unsigned int mask = LOCAL_STATX_ALL; if (my_statx_func != NULL) { // Prefer statx over stat64 on Linux if it's available if (((int)flag & AT_SYMLINK_NOFOLLOW) > 0) { // flag set in java code flags |= AT_SYMLINK_NOFOLLOW; } + // statx is not allowed on the old Docker versions and returns EPERM, + // fallback to fstatat64 in this case RESTARTABLE(statx_wrapper((int)dfd, path, flags, mask, &statx_buf), err); if (err == 0) { copy_statx_attributes(env, &statx_buf, attrs); - } else { + return; + } else if (errno != EPERM) { throwUnixException(env, errno); + return; } - // statx was available, so return now - return; } #endif diff --git a/src/java.base/windows/classes/java/lang/ProcessImpl.java b/src/java.base/windows/classes/java/lang/ProcessImpl.java index 536f2a27cf765..f46c5ea777a29 100644 --- a/src/java.base/windows/classes/java/lang/ProcessImpl.java +++ b/src/java.base/windows/classes/java/lang/ProcessImpl.java @@ -216,13 +216,14 @@ private static String[] getTokensFromCommand(String command) { private static final int VERIFICATION_LEGACY = 3; // See Command shell overview for documentation of special characters. // https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490954(v=technet.10) - private static final char ESCAPE_VERIFICATION[][] = { + private static final String ESCAPE_VERIFICATION[] = { // We guarantee the only command file execution for implicit [cmd.exe] run. // http://technet.microsoft.com/en-us/library/bb490954.aspx - {' ', '\t', '\"', '<', '>', '&', '|', '^'}, - {' ', '\t', '\"', '<', '>'}, - {' ', '\t', '\"', '<', '>'}, - {' ', '\t'} + // All space characters require quoting are checked in needsEscaping(). + "\"<>&|^", + "\"<>", + "\"<>", + "" }; private static String createCommandLine(int verificationType, @@ -337,9 +338,14 @@ private static boolean needsEscaping(int verificationType, String arg) { } if (!argIsQuoted) { - char testEscape[] = ESCAPE_VERIFICATION[verificationType]; - for (int i = 0; i < testEscape.length; ++i) { - if (arg.indexOf(testEscape[i]) >= 0) { + for (int i = 0; i < arg.length(); i++) { + char ch = arg.charAt(i); + if (Character.isLetterOrDigit(ch)) + continue; // skip over common characters + // All space chars require quotes and other mode specific characters + if (Character.isSpaceChar(ch) || + Character.isWhitespace(ch) || + ESCAPE_VERIFICATION[verificationType].indexOf(ch) >= 0) { return true; } } diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java b/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java index 846585668733d..964e338466d70 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -374,8 +374,19 @@ public void checkAccess(Path obj, AccessMode... modes) throws IOException { } } + // check file exists only + if (!(r || w || x)) { + file.checkRead(); + try { + WindowsFileAttributes.get(file, true); + return; + } catch (WindowsException exc) { + exc.rethrowAsIOException(file); + } + } + // special-case read access to avoid needing to determine effective - // access to file; default if modes not specified + // access to file if (!w && !x) { checkReadAccess(file); return; diff --git a/src/java.base/windows/native/libjava/Console_md.c b/src/java.base/windows/native/libjava/Console_md.c index 02b56cc531acd..1c41d07d1a4db 100644 --- a/src/java.base/windows/native/libjava/Console_md.c +++ b/src/java.base/windows/native/libjava/Console_md.c @@ -56,11 +56,11 @@ Java_java_io_Console_encoding(JNIEnv *env, jclass cls) char buf[64]; int cp = GetConsoleCP(); if (cp >= 874 && cp <= 950) - sprintf(buf, "ms%d", cp); + snprintf(buf, sizeof(buf), "ms%d", cp); else if (cp == 65001) - sprintf(buf, "UTF-8"); + snprintf(buf, sizeof(buf), "UTF-8"); else - sprintf(buf, "cp%d", cp); + snprintf(buf, sizeof(buf), "cp%d", cp); return JNU_NewStringPlatform(env, buf); } diff --git a/src/java.base/windows/native/libjava/TimeZone_md.c b/src/java.base/windows/native/libjava/TimeZone_md.c index 061600c87a3aa..c5d0edcd73221 100644 --- a/src/java.base/windows/native/libjava/TimeZone_md.c +++ b/src/java.base/windows/native/libjava/TimeZone_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -122,7 +122,7 @@ getValueInRegistry(HKEY hKey, /* * Produces custom name "GMT+hh:mm" from the given bias in buffer. */ -static void customZoneName(LONG bias, char *buffer) { +static void customZoneName(LONG bias, char *buffer, size_t bufSize) { LONG gmtOffset; int sign; @@ -134,7 +134,7 @@ static void customZoneName(LONG bias, char *buffer) { sign = 1; } if (gmtOffset != 0) { - sprintf(buffer, "GMT%c%02d:%02d", + snprintf(buffer, bufSize, "GMT%c%02d:%02d", ((sign >= 0) ? '+' : '-'), gmtOffset / 60, gmtOffset % 60); @@ -146,7 +146,7 @@ static void customZoneName(LONG bias, char *buffer) { /* * Gets the current time zone entry in the "Time Zones" registry. */ -static int getWinTimeZone(char *winZoneName) +static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize) { DYNAMIC_TIME_ZONE_INFORMATION dtzi; DWORD timeType; @@ -173,7 +173,7 @@ static int getWinTimeZone(char *winZoneName) */ if (dtzi.TimeZoneKeyName[0] != 0) { if (dtzi.DynamicDaylightTimeDisabled) { - customZoneName(dtzi.Bias, winZoneName); + customZoneName(dtzi.Bias, winZoneName, winZoneNameBufSize); return VALUE_GMTOFFSET; } wcstombs(winZoneName, dtzi.TimeZoneKeyName, MAX_ZONE_CHAR); @@ -206,7 +206,7 @@ static int getWinTimeZone(char *winZoneName) * is disabled. */ if (val == 1) { - customZoneName(dtzi.Bias, winZoneName); + customZoneName(dtzi.Bias, winZoneName, winZoneNameBufSize); (void) RegCloseKey(hKey); return VALUE_GMTOFFSET; } @@ -251,7 +251,7 @@ static int getWinTimeZone(char *winZoneName) if (ret == ERROR_SUCCESS) { if (val == 1 && tzi.DaylightDate.wMonth != 0) { (void) RegCloseKey(hKey); - customZoneName(tzi.Bias, winZoneName); + customZoneName(tzi.Bias, winZoneName, winZoneNameBufSize); return VALUE_GMTOFFSET; } } @@ -519,7 +519,7 @@ char *findJavaTZ_md(const char *java_home_dir) char *std_timezone = NULL; int result; - result = getWinTimeZone(winZoneName); + result = getWinTimeZone(winZoneName, sizeof(winZoneName)); if (result != VALUE_UNKNOWN) { if (result == VALUE_GMTOFFSET) { @@ -569,6 +569,6 @@ getGMTOffsetID() } } - customZoneName(bias, zonename); + customZoneName(bias, zonename, sizeof(zonename)); return _strdup(zonename); } diff --git a/src/java.base/windows/native/libjava/java_props_md.c b/src/java.base/windows/native/libjava/java_props_md.c index 9fe8485171d03..bcbdee02976dd 100644 --- a/src/java.base/windows/native/libjava/java_props_md.c +++ b/src/java.base/windows/native/libjava/java_props_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -134,18 +134,19 @@ getEncodingInternal(LCID lcid) static char* getConsoleEncoding() { - char* buf = malloc(16); + size_t buflen = 16; + char* buf = malloc(buflen); int cp; if (buf == NULL) { return NULL; } cp = GetConsoleCP(); if (cp >= 874 && cp <= 950) - sprintf(buf, "ms%d", cp); + snprintf(buf, buflen, "ms%d", cp); else if (cp == 65001) - sprintf(buf, "UTF-8"); + snprintf(buf, buflen, "UTF-8"); else - sprintf(buf, "cp%d", cp); + snprintf(buf, buflen, "cp%d", cp); return buf; } @@ -473,6 +474,8 @@ GetJavaProperties(JNIEnv* env) * where (buildNumber > 17762) * Windows Server 2022 10 0 (!VER_NT_WORKSTATION) * where (buildNumber > 20347) + * Windows Server 2025 10 0 (!VER_NT_WORKSTATION) + * where (buildNumber > 26039) * * This mapping will presumably be augmented as new Windows * versions are released. @@ -556,7 +559,10 @@ GetJavaProperties(JNIEnv* env) case 0: /* Windows server 2019 GA 10/2018 build number is 17763 */ /* Windows server 2022 build number is 20348 */ - if (buildNumber > 20347) { + /* Windows server 2025 Preview build is 26040 */ + if (buildNumber > 26039) { + sprops.os_name = "Windows Server 2025"; + } else if (buildNumber > 20347) { sprops.os_name = "Windows Server 2022"; } else if (buildNumber > 17762) { sprops.os_name = "Windows Server 2019"; @@ -575,7 +581,7 @@ GetJavaProperties(JNIEnv* env) sprops.os_name = "Windows (unknown)"; break; } - sprintf(buf, "%d.%d", majorVersion, minorVersion); + snprintf(buf, sizeof(buf), "%d.%d", majorVersion, minorVersion); sprops.os_version = _strdup(buf); #if defined(_M_AMD64) sprops.os_arch = "amd64"; diff --git a/src/java.base/windows/native/libnet/net_util_md.c b/src/java.base/windows/native/libnet/net_util_md.c index 83e30287196a8..a5741a2c1ad39 100644 --- a/src/java.base/windows/native/libnet/net_util_md.c +++ b/src/java.base/windows/native/libnet/net_util_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -189,7 +189,7 @@ NET_ThrowNew(JNIEnv *env, int errorNum, char *msg) if (excP == NULL) { excP = "SocketException"; } - sprintf(exc, "%s%s", JNU_JAVANETPKG, excP); + snprintf(exc, sizeof(exc), "%s%s", JNU_JAVANETPKG, excP); JNU_ThrowByName(env, exc, fullMsg); } diff --git a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_CommonUtils.c b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_CommonUtils.c index 230be0c7c8fa1..3e29f2debc4d0 100644 --- a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_CommonUtils.c +++ b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_CommonUtils.c @@ -103,29 +103,29 @@ void decodeDeviceID(UINT32 deviceID, int* card, int* device, int* subdevice, } -void getDeviceString(char* buffer, int card, int device, int subdevice, - int usePlugHw, int isMidi) { +void getDeviceString(char* buffer, size_t bufferSize, int card, int device, + int subdevice, int usePlugHw, int isMidi) { if (needEnumerateSubdevices(isMidi)) { - sprintf(buffer, "%s:%d,%d,%d", + snprintf(buffer, bufferSize, "%s:%d,%d,%d", usePlugHw ? ALSA_PLUGHARDWARE : ALSA_HARDWARE, card, device, subdevice); } else { - sprintf(buffer, "%s:%d,%d", + snprintf(buffer, bufferSize, "%s:%d,%d", usePlugHw ? ALSA_PLUGHARDWARE : ALSA_HARDWARE, card, device); } } -void getDeviceStringFromDeviceID(char* buffer, UINT32 deviceID, - int usePlugHw, int isMidi) { +void getDeviceStringFromDeviceID(char* buffer, size_t bufferSize, + UINT32 deviceID, int usePlugHw, int isMidi) { int card, device, subdevice; if (deviceID == ALSA_DEFAULT_DEVICE_ID) { strcpy(buffer, ALSA_DEFAULT_DEVICE_NAME); } else { decodeDeviceID(deviceID, &card, &device, &subdevice, isMidi); - getDeviceString(buffer, card, device, subdevice, usePlugHw, isMidi); + getDeviceString(buffer, bufferSize, card, device, subdevice, usePlugHw, isMidi); } } diff --git a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_CommonUtils.h b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_CommonUtils.h index 792f7ec869e81..fb1501c8fde3e 100644 --- a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_CommonUtils.h +++ b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_CommonUtils.h @@ -73,8 +73,8 @@ UINT32 encodeDeviceID(int card, int device, int subdevice); void decodeDeviceID(UINT32 deviceID, int* card, int* device, int* subdevice, int isMidi); -void getDeviceStringFromDeviceID(char* buffer, UINT32 deviceID, - int usePlugHw, int isMidi); +void getDeviceStringFromDeviceID(char* buffer, size_t bufferSize, + UINT32 deviceID, int usePlugHw, int isMidi); void getALSAVersion(char* buffer, int len); diff --git a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_MidiIn.c b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_MidiIn.c index 82f564119baa3..adfb2e3e90ce1 100644 --- a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_MidiIn.c +++ b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_MidiIn.c @@ -218,7 +218,7 @@ MidiMessage* MIDI_IN_GetMessage(MidiDeviceHandle* handle) { return NULL; } } - jdk_message = (MidiMessage*) calloc(sizeof(MidiMessage), 1); + jdk_message = (MidiMessage*) calloc(1, sizeof(MidiMessage)); if (!jdk_message) { ERROR0("< ERROR: MIDI_IN_GetMessage(): out of memory\n"); return NULL; diff --git a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_MidiUtils.c b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_MidiUtils.c index 14475413f1c76..de988fd86b7f2 100644 --- a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_MidiUtils.c +++ b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_MidiUtils.c @@ -98,7 +98,7 @@ static int iterateRawmidiDevices(snd_rawmidi_stream_t direction, // try to get card info card = snd_rawmidi_info_get_card(rawmidi_info); if (card >= 0) { - sprintf(devname, ALSA_HARDWARE_CARD, card); + snprintf(devname, sizeof(devname), ALSA_HARDWARE_CARD, card); if (snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK) >= 0) { if (snd_ctl_card_info(handle, card_info) >= 0) { defcardinfo = card_info; @@ -121,7 +121,7 @@ static int iterateRawmidiDevices(snd_rawmidi_stream_t direction, if (snd_card_next(&card) >= 0) { TRACE1("Found card %d\n", card); while (doContinue && (card >= 0)) { - sprintf(devname, ALSA_HARDWARE_CARD, card); + snprintf(devname, sizeof(devname), ALSA_HARDWARE_CARD, card); TRACE1("Opening control for alsa rawmidi device \"%s\"...\n", devname); err = snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK); if (err < 0) { @@ -230,7 +230,7 @@ static int deviceInfoIterator(UINT32 deviceID, snd_rawmidi_info_t *rawmidi_info, buffer[0]=' '; buffer[1]='['; // buffer[300] is enough to store the actual device string w/o overrun - getDeviceStringFromDeviceID(&buffer[2], deviceID, usePlugHw, ALSA_RAWMIDI); + getDeviceStringFromDeviceID(&buffer[2], sizeof(buffer) - 2, deviceID, usePlugHw, ALSA_RAWMIDI); strncat(buffer, "]", sizeof(buffer) - strlen(buffer) - 1); strncpy(desc->name, (cardinfo != NULL) @@ -383,7 +383,7 @@ INT32 openMidiDevice(snd_rawmidi_stream_t direction, INT32 deviceIndex, TRACE0("> openMidiDevice()\n"); - (*handle) = (MidiDeviceHandle*) calloc(sizeof(MidiDeviceHandle), 1); + (*handle) = (MidiDeviceHandle*) calloc(1, sizeof(MidiDeviceHandle)); if (!(*handle)) { ERROR0("ERROR: openDevice: out of memory\n"); return MIDI_OUT_OF_MEMORY; @@ -392,7 +392,7 @@ INT32 openMidiDevice(snd_rawmidi_stream_t direction, INT32 deviceIndex, // TODO: iterate to get dev ID from index err = getMidiDeviceID(direction, deviceIndex, &deviceID); TRACE1(" openMidiDevice(): deviceID: %d\n", (int) deviceID); - getDeviceStringFromDeviceID(devicename, deviceID, + getDeviceStringFromDeviceID(devicename, sizeof(devicename), deviceID, usePlugHw, ALSA_RAWMIDI); TRACE1(" openMidiDevice(): deviceString: %s\n", devicename); diff --git a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_PCMUtils.c b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_PCMUtils.c index da7b9a8474505..c4d16f3e10e68 100644 --- a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_PCMUtils.c +++ b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_PCMUtils.c @@ -75,7 +75,7 @@ int iteratePCMDevices(DeviceIteratorPtr iterator, void* userData) { // try to get card info card = snd_pcm_info_get_card(pcminfo); if (card >= 0) { - sprintf(devname, ALSA_HARDWARE_CARD, card); + snprintf(devname, sizeof(devname), ALSA_HARDWARE_CARD, card); if (snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK) >= 0) { if (snd_ctl_card_info(handle, cardinfo) >= 0) { defcardinfo = cardinfo; @@ -101,7 +101,7 @@ int iteratePCMDevices(DeviceIteratorPtr iterator, void* userData) { if (card < 0) { break; } - sprintf(devname, ALSA_HARDWARE_CARD, card); + snprintf(devname, sizeof(devname), ALSA_HARDWARE_CARD, card); TRACE1("Opening alsa device \"%s\"...\n", devname); err = snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK); if (err < 0) { @@ -185,7 +185,7 @@ int deviceInfoIterator(UINT32 deviceID, snd_pcm_info_t* pcminfo, *desc->deviceID = deviceID; buffer[0]=' '; buffer[1]='['; // buffer[300] is enough to store the actual device string w/o overrun - getDeviceStringFromDeviceID(&buffer[2], deviceID, usePlugHw, ALSA_PCM); + getDeviceStringFromDeviceID(&buffer[2], sizeof(buffer) - 2, deviceID, usePlugHw, ALSA_PCM); strncat(buffer, "]", sizeof(buffer) - strlen(buffer) - 1); strncpy(desc->name, (cardinfo != NULL) @@ -217,7 +217,7 @@ int openPCMfromDeviceID(int deviceID, snd_pcm_t** handle, int isSource, int hard int ret; initAlsaSupport(); - getDeviceStringFromDeviceID(buffer, deviceID, !hardware, ALSA_PCM); + getDeviceStringFromDeviceID(buffer, sizeof(buffer), deviceID, !hardware, ALSA_PCM); TRACE1("Opening ALSA device %s\n", buffer); ret = snd_pcm_open(handle, buffer, diff --git a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_Ports.c b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_Ports.c index b973cc839fde5..bd1127c2e3198 100644 --- a/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_Ports.c +++ b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_Ports.c @@ -85,7 +85,7 @@ INT32 PORT_GetPortMixerCount() { mixerCount = 0; if (snd_card_next(&card) >= 0) { while (card >= 0) { - sprintf(devname, ALSA_HARDWARE_CARD, card); + snprintf(devname, sizeof(devname), ALSA_HARDWARE_CARD, card); TRACE1("PORT_GetPortMixerCount: Opening alsa device \"%s\"...\n", devname); err = snd_ctl_open(&handle, devname, 0); if (err < 0) { @@ -115,7 +115,7 @@ INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* descr TRACE0("> PORT_GetPortMixerDescription\n"); snd_ctl_card_info_malloc(&card_info); - sprintf(devname, ALSA_HARDWARE_CARD, (int) mixerIndex); + snprintf(devname, sizeof(devname), ALSA_HARDWARE_CARD, (int) mixerIndex); TRACE1("Opening alsa device \"%s\"...\n", devname); err = snd_ctl_open(&handle, devname, 0); if (err < 0) { @@ -127,7 +127,7 @@ INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* descr ERROR2("ERROR: snd_ctl_card_info, card=%d: %s\n", (int) mixerIndex, snd_strerror(err)); } strncpy(description->name, snd_ctl_card_info_get_id(card_info), PORT_STRING_LENGTH - 1); - sprintf(buffer, " [%s]", devname); + snprintf(buffer, sizeof(buffer), " [%s]", devname); strncat(description->name, buffer, PORT_STRING_LENGTH - 1 - strlen(description->name)); strncpy(description->vendor, "ALSA (http://www.alsa-project.org)", PORT_STRING_LENGTH - 1); strncpy(description->description, snd_ctl_card_info_get_name(card_info), PORT_STRING_LENGTH - 1); @@ -149,7 +149,7 @@ void* PORT_Open(INT32 mixerIndex) { PortMixer* handle; TRACE0("> PORT_Open\n"); - sprintf(devname, ALSA_HARDWARE_CARD, (int) mixerIndex); + snprintf(devname, sizeof(devname), ALSA_HARDWARE_CARD, (int) mixerIndex); if ((err = snd_mixer_open(&mixer_handle, 0)) < 0) { ERROR2("Mixer %s open error: %s", devname, snd_strerror(err)); return NULL; diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaComboBoxButton.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaComboBoxButton.java index 00ccc12fddc4a..b79cc98be9d55 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaComboBoxButton.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaComboBoxButton.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,13 +25,32 @@ package com.apple.laf; -import java.awt.*; - -import javax.swing.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; + +import javax.accessibility.AccessibleContext; +import javax.swing.ButtonModel; +import javax.swing.CellRendererPane; +import javax.swing.DefaultButtonModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; +import javax.swing.UIManager; import javax.swing.plaf.UIResource; +import apple.laf.JRSUIConstants.AlignmentHorizontal; +import apple.laf.JRSUIConstants.AlignmentVertical; +import apple.laf.JRSUIConstants.ArrowsOnly; +import apple.laf.JRSUIConstants.Focused; +import apple.laf.JRSUIConstants.IndicatorOnly; +import apple.laf.JRSUIConstants.Size; +import apple.laf.JRSUIConstants.State; +import apple.laf.JRSUIConstants.Widget; import apple.laf.JRSUIState; -import apple.laf.JRSUIConstants.*; @SuppressWarnings("serial") // Superclass is not serializable across versions class AquaComboBoxButton extends JButton { @@ -169,12 +188,15 @@ public void paintComponent(final Graphics g) { } } - protected void doRendererPaint(final Graphics g, final ButtonModel buttonModel, final boolean editable, final Insets insets, int left, int top, int width, int height) { + private Component getRendererComponent() { final ListCellRenderer renderer = comboBox.getRenderer(); + return renderer.getListCellRendererComponent(list, comboBox.getSelectedItem(), -1, false, false); + } + + protected void doRendererPaint(final Graphics g, final ButtonModel buttonModel, final boolean editable, final Insets insets, int left, int top, int width, int height) { // fake it out! not renderPressed - final Component c = renderer.getListCellRendererComponent(list, comboBox.getSelectedItem(), -1, false, false); - // System.err.println("Renderer: " + renderer); + final Component c = getRendererComponent(); if (!editable && !AquaComboBoxUI.isTableCellEditor(comboBox)) { final int indentLeft = 10; @@ -233,4 +255,25 @@ protected void doRendererPaint(final Graphics g, final ButtonModel buttonModel, // Remove component from renderer pane, allowing it to be gc'ed. rendererPane.remove(c); } + + @Override + public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) { + accessibleContext = new AccessibleAquaComboBoxButton(); + } + return accessibleContext; + } + + private final class AccessibleAquaComboBoxButton extends AccessibleJButton { + @Override + public String getAccessibleName() { + String name = super.getAccessibleName(); + if ((name == null || name.isEmpty()) + && (!comboBox.isEditable() && comboBox.getSelectedItem() != null)) { + Component c = getRendererComponent(); + name = c.getAccessibleContext().getAccessibleName(); + } + return name; + } + } } diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaComboBoxUI.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaComboBoxUI.java index a7e3991fcb21f..6238bb43bac67 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaComboBoxUI.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -729,4 +729,35 @@ public AquaComboBoxUI convertJComponentToTarget(final JComboBox combo) { static ClientPropertyApplicator, AquaComboBoxUI> getApplicator() { return APPLICATOR.get(); } + + @Override + public int getAccessibleChildrenCount(JComponent c) { + return 2; + } + + @Override + public Accessible getAccessibleChild(JComponent c, int i) { + // 0 = the popup + // 1 = the editor for editable combobox and the arrow button for non-editable combobox + switch ( i ) { + case 0: + if (popup instanceof Accessible accessiblePopup) { + AccessibleContext ac = accessiblePopup.getAccessibleContext(); + ac.setAccessibleParent(comboBox); + return accessiblePopup; + } + break; + case 1: + if (comboBox.isEditable() + && (editor instanceof Accessible accessibleEditor)) { + AccessibleContext ac = accessibleEditor.getAccessibleContext(); + ac.setAccessibleParent(comboBox); + return accessibleEditor; + } else if (!comboBox.isEditable()) { + return arrowButton; + } + break; + } + return null; + } } diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaFileView.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaFileView.java index d9f9a66e44c28..4177e32f63d81 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaFileView.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaFileView.java @@ -25,8 +25,11 @@ package com.apple.laf; -import java.io.*; -import java.util.*; +import java.io.File; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; import java.util.Map.Entry; import javax.swing.Icon; @@ -34,6 +37,8 @@ import com.apple.laf.AquaUtils.RecyclableSingleton; +import static java.nio.charset.StandardCharsets.UTF_8; + @SuppressWarnings("serial") // JDK implementation class class AquaFileView extends FileView { private static final boolean DEBUG = false; @@ -111,11 +116,7 @@ static class FileInfo { FileInfo(final File file){ isDirectory = file.isDirectory(); absolutePath = file.getAbsolutePath(); - try { - pathBytes = absolutePath.getBytes("UTF-8"); - } catch (final UnsupportedEncodingException e) { - pathBytes = new byte[0]; - } + pathBytes = absolutePath.getBytes(UTF_8); } } diff --git a/src/java.desktop/macosx/classes/sun/font/CStrike.java b/src/java.desktop/macosx/classes/sun/font/CStrike.java index 31047d2f5f5c8..068a1b46af3fb 100644 --- a/src/java.desktop/macosx/classes/sun/font/CStrike.java +++ b/src/java.desktop/macosx/classes/sun/font/CStrike.java @@ -201,7 +201,7 @@ void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) { getGlyphImageBounds(glyphCode, pt.x, pt.y, floatRect); if (floatRect.width == 0 && floatRect.height == 0) { - result.setRect(0, 0, -1, -1); + result.setRect(0, 0, 0, 0); return; } diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java index 19ac4818928cc..0ab38fc06b4ec 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,10 +35,11 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.annotation.Native; -import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.Arrays; @@ -64,7 +65,6 @@ import javax.swing.JList; import javax.swing.JTree; import javax.swing.KeyStroke; -import javax.swing.tree.TreePath; import sun.awt.AWTAccessor; import sun.lwawt.LWWindowPeer; @@ -742,21 +742,6 @@ private static Object[] getChildrenAndRolesImpl(Accessible a, Component c, int w return new Object[]{childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1)}; } - private static Accessible createAccessibleTreeNode(JTree t, TreePath p) { - Accessible a = null; - - try { - Class accessibleJTreeNodeClass = Class.forName("javax.swing.JTree$AccessibleJTree$AccessibleJTreeNode"); - Constructor constructor = accessibleJTreeNodeClass.getConstructor(t.getAccessibleContext().getClass(), JTree.class, TreePath.class, Accessible.class); - constructor.setAccessible(true); - a = ((Accessible) constructor.newInstance(t.getAccessibleContext(), t, p, null)); - } catch (Exception e) { - e.printStackTrace(); - } - - return a; - } - // This method is called from the native // Each child takes up three entries in the array: one for itself, one for its role, and one for the recursion level private static Object[] getChildrenAndRolesRecursive(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored, final int level) { @@ -764,62 +749,21 @@ private static Object[] getChildrenAndRolesRecursive(final Accessible a, final C return invokeAndWait(new Callable() { public Object[] call() throws Exception { ArrayList allChildren = new ArrayList(); - - Accessible at = null; - if (a instanceof CAccessible) { - at = CAccessible.getSwingAccessible(a); - } else { - at = a; - } - - if (at instanceof JTree) { - JTree tree = ((JTree) at); - - if (whichChildren == JAVA_AX_ALL_CHILDREN) { - int count = tree.getRowCount(); - for (int i = 0; i < count; i++) { - TreePath path = tree.getPathForRow(i); - Accessible an = createAccessibleTreeNode(tree, path); - if (an != null) { - AccessibleContext ac = an.getAccessibleContext(); - if (ac != null) { - allChildren.add(an); - allChildren.add(ac.getAccessibleRole());; - allChildren.add(String.valueOf((tree.isRootVisible() ? path.getPathCount() : path.getPathCount() - 1))); - } - } - } - } - - if (whichChildren == JAVA_AX_SELECTED_CHILDREN) { - int count = tree.getSelectionCount(); - for (int i = 0; i < count; i++) { - TreePath path = tree.getSelectionPaths()[i]; - Accessible an = createAccessibleTreeNode(tree, path); - if (an != null) { - AccessibleContext ac = an.getAccessibleContext(); - if (ac != null) { - allChildren.add(an); - allChildren.add(ac.getAccessibleRole()); - allChildren.add(String.valueOf((tree.isRootVisible() ? path.getPathCount() : path.getPathCount() - 1))); - } - } - } - } - - return allChildren.toArray(); - } - ArrayList currentLevelChildren = new ArrayList(); ArrayList parentStack = new ArrayList(); + HashMap> childrenOfParent = new HashMap<>(); parentStack.add(a); ArrayList indexses = new ArrayList(); Integer index = 0; int currentLevel = level; while (!parentStack.isEmpty()) { Accessible p = parentStack.get(parentStack.size() - 1); - - currentLevelChildren.addAll(Arrays.asList(getChildrenAndRolesImpl(p, c, JAVA_AX_ALL_CHILDREN, allowIgnored, ChildrenOperations.COMMON))); + if (!childrenOfParent.containsKey(p)) { + childrenOfParent.put(p, Arrays.asList(getChildrenAndRolesImpl(p, + c, JAVA_AX_ALL_CHILDREN, allowIgnored, + ChildrenOperations.COMMON))); + } + currentLevelChildren.addAll(childrenOfParent.get(p)); if ((currentLevelChildren.size() == 0) || (index >= currentLevelChildren.size())) { if (!parentStack.isEmpty()) parentStack.remove(parentStack.size() - 1); if (!indexses.isEmpty()) index = indexses.remove(indexses.size() - 1); @@ -862,7 +806,6 @@ public Object[] call() throws Exception { currentLevel += 1; continue; } - } return allChildren.toArray(); diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDataTransferer.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDataTransferer.java index 795e4296c7070..0ea70549d430b 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDataTransferer.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDataTransferer.java @@ -1,6 +1,5 @@ - /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,20 +25,27 @@ package sun.lwawt.macosx; -import java.awt.*; - -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; +import java.awt.Image; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.net.URL; import java.nio.charset.Charset; import java.text.Normalizer; import java.text.Normalizer.Form; -import java.util.*; -import java.util.regex.*; -import java.awt.datatransfer.*; -import java.nio.charset.StandardCharsets; -import sun.awt.datatransfer.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import sun.awt.datatransfer.DataTransferer; +import sun.awt.datatransfer.ToolkitThreadBlockedHandler; + +import static java.nio.charset.StandardCharsets.UTF_8; public class CDataTransferer extends DataTransferer { private static final Map predefinedClipboardNameMap; @@ -133,7 +139,7 @@ public Object translateBytes(byte[] bytes, DataFlavor flavor, String charset = Charset.defaultCharset().name(); if (transferable != null && transferable.isDataFlavorSupported(javaTextEncodingFlavor)) { try { - charset = new String((byte[]) transferable.getTransferData(javaTextEncodingFlavor), StandardCharsets.UTF_8); + charset = new String((byte[]) transferable.getTransferData(javaTextEncodingFlavor), UTF_8); } catch (UnsupportedFlavorException cannotHappen) { } } @@ -160,7 +166,8 @@ public Object translateBytes(byte[] bytes, DataFlavor flavor, // class by base method format = CF_STRING; } else if (format == CF_STRING) { - bytes = Normalizer.normalize(new String(bytes, "UTF8"), Form.NFC).getBytes("UTF8"); + String src = new String(bytes, UTF_8); + bytes = Normalizer.normalize(src, Form.NFC).getBytes(UTF_8); } return super.translateBytes(bytes, flavor, format, transferable); diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java index 587612dafea38..dd231e84f3a14 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java @@ -42,6 +42,7 @@ import java.awt.Window; import java.awt.event.FocusEvent; import java.awt.event.WindowEvent; +import java.awt.event.WindowStateListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.reflect.InvocationTargetException; @@ -964,6 +965,33 @@ public boolean isFullScreenMode() { return isFullScreenMode; } + private void waitForWindowState(int state) { + if (peer.getState() == state) { + return; + } + + Object lock = new Object(); + WindowStateListener wsl = new WindowStateListener() { + public void windowStateChanged(WindowEvent e) { + synchronized (lock) { + if (e.getNewState() == state) { + lock.notifyAll(); + } + } + } + }; + + target.addWindowStateListener(wsl); + if (peer.getState() != state) { + synchronized (lock) { + try { + lock.wait(); + } catch (InterruptedException ie) {} + } + } + target.removeWindowStateListener(wsl); + } + @Override public void setWindowState(int windowState) { if (peer == null || !peer.isVisible()) { @@ -985,6 +1013,7 @@ public void setWindowState(int windowState) { // let's return into the normal states first // the zoom call toggles between the normal and the max states unmaximize(); + waitForWindowState(Frame.NORMAL); } execute(CWrapper.NSWindow::miniaturize); break; @@ -992,6 +1021,8 @@ public void setWindowState(int windowState) { if (prevWindowState == Frame.ICONIFIED) { // let's return into the normal states first execute(CWrapper.NSWindow::deminiaturize); + waitForWindowState(Frame.NORMAL); + } maximize(); break; diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java index 1b037510299e4..a3281ce7b6850 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -454,7 +454,7 @@ protected void initializeDesktopProperties() { desktopProperties.put("awt.multiClickInterval", getMultiClickTime()); // These DnD properties must be set, otherwise Swing ends up spewing NPEs - // all over the place. The values came straight off of MToolkit. + // all over the place. The values came straight off of XToolkit. desktopProperties.put("DnD.Autoscroll.initialDelay", Integer.valueOf(50)); desktopProperties.put("DnD.Autoscroll.interval", Integer.valueOf(50)); desktopProperties.put("DnD.Autoscroll.cursorHysteresis", Integer.valueOf(5)); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m index 8e44052333c2c..20d417dbcc00d 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m @@ -38,6 +38,9 @@ // keyboard layout static NSString *kbdLayout; +// Constant for keyman layouts +#define KEYMAN_LAYOUT "keyman" + @interface AWTView() @property (retain) CDropTarget *_dropTarget; @property (retain) CDragSource *_dragSource; @@ -259,7 +262,7 @@ - (void) scrollWheel: (NSEvent*) event { - (void) keyDown: (NSEvent *)event { fProcessingKeystroke = YES; - fKeyEventsNeeded = YES; + fKeyEventsNeeded = ![(NSString *)kbdLayout containsString:@KEYMAN_LAYOUT]; // Allow TSM to look at the event and potentially send back NSTextInputClient messages. [self interpretKeyEvents:[NSArray arrayWithObject:event]]; @@ -961,7 +964,7 @@ - (void) insertText:(id)aString replacementRange:(NSRange)replacementRange if ((utf16Length > 2) || ((utf8Length > 1) && [self isCodePointInUnicodeBlockNeedingIMEvent:codePoint]) || - ((codePoint == 0x5c) && ([(NSString *)kbdLayout containsString:@"Kotoeri"]))) { + [(NSString *)kbdLayout containsString:@KEYMAN_LAYOUT]) { aStringIsComplex = YES; } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m index 5826cbf31d10e..79c5abdbc2c9e 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1695,6 +1695,7 @@ + (AWTWindow *) lastKeyWindow { int shieldLevel = CGShieldingWindowLevel(); window.preFullScreenLevel = [nsWindow level]; [nsWindow setLevel: shieldLevel]; + [nsWindow makeKeyAndOrderFront: nil]; NSRect screenRect = [[nsWindow screen] frame]; [nsWindow setFrame:screenRect display:YES]; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m index 545138de71739..9cbd48bf843fb 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,7 +63,7 @@ GET_CPRINTERDIALOG_CLASS_RETURN(ret); \ GET_FIELD_RETURN(sjm_printerJob, sjc_CPrinterDialog, "fPrinterJob", "Lsun/lwawt/macosx/CPrinterJob;", ret); -static NSPrintInfo* createDefaultNSPrintInfo(); +static NSPrintInfo* createDefaultNSPrintInfo(JNIEnv* env, jstring printer); static void makeBestFit(NSPrintInfo* src); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m index f9f13ca207f38..e8b28d9306854 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m @@ -32,9 +32,13 @@ @implementation MenuAccessibility - (NSAccessibilityRole _Nonnull)accessibilityRole { - return [[[self parent] javaRole] isEqualToString:@"combobox"] - ? NSAccessibilityPopUpButtonRole - : NSAccessibilityMenuRole; + if ([[[self parent] javaRole] isEqualToString:@"combobox"]) { + return NSAccessibilityPopUpButtonRole; + } else if ([[[self parent] javaRole] isEqualToString:@"menubar"]) { + return NSAccessibilityMenuBarItemRole; + } else { + return NSAccessibilityMenuRole; + } } - (BOOL)isAccessibilityElement diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h index 2992d82cbe4f5..8281f0b0213db 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, JetBrains s.r.o.. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,12 @@ // This is a tree representation. See: https://developer.apple.com/documentation/appkit/nsoutlineview @interface OutlineAccessibility : ListAccessibility - +{ + NSMutableArray> *rowCache; + BOOL rowCacheValid; + NSMutableArray> *selectedRowCache; + BOOL selectedRowCacheValid; +} @property(readonly) BOOL isTreeRootVisible; @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m index cdf6dbbd4ab77..08240614bf774 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.m @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, JetBrains s.r.o.. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,4 +55,88 @@ - (NSString *)accessibilityLabel return [[super accessibilityLabel] isEqualToString:@"list"] ? @"tree" : [super accessibilityLabel]; } +- (nullable NSArray> *)accessibilityRows +{ + return [self accessibilityChildren]; +} + +- (nullable NSArray> *)accessibilitySelectedRows +{ + return [self accessibilitySelectedChildren]; +} + +- (nullable NSArray> *)accessibilityChildren +{ + if (![self isCacheValid]) { + NSArray *t = [super accessibilityChildren]; + if (t != nil) { + rowCache = [[NSMutableArray arrayWithArray:t] retain]; + } else { + rowCache = nil; + } + rowCacheValid = YES; + } + return rowCache; +} + +- (nullable NSArray> *)accessibilitySelectedChildren +{ + if (!selectedRowCacheValid) { + NSArray *t = [super accessibilitySelectedChildren]; + if (t != nil) { + selectedRowCache = [[NSMutableArray arrayWithArray:t] retain]; + } else { + selectedRowCache = nil; + } + selectedRowCacheValid = YES; + } + return selectedRowCache; +} + +- (BOOL)isCacheValid +{ + if (rowCacheValid && [[self parent] respondsToSelector:NSSelectorFromString(@"isCacheValid")]) { + return [[self parent] isCacheValid]; + } + return rowCacheValid; +} + +- (void)invalidateCache +{ + rowCacheValid = NO; +} + +- (void)invalidateSelectionCache +{ + selectedRowCacheValid = NO; +} + +- (void)postSelectionChanged +{ + AWT_ASSERT_APPKIT_THREAD; + [self invalidateSelectionCache]; + [super postSelectionChanged]; +} + +- (void)postTreeNodeCollapsed +{ + AWT_ASSERT_APPKIT_THREAD; + [self invalidateCache]; + [super postTreeNodeCollapsed]; +} + +- (void)postTreeNodeExpanded +{ + AWT_ASSERT_APPKIT_THREAD; + [self invalidateCache]; + [super postTreeNodeExpanded]; +} + +- (void)postSelectedCellsChanged +{ + AWT_ASSERT_APPKIT_THREAD; + [self invalidateSelectionCache]; + [super postSelectedCellsChanged]; +} + @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m index a35d5401474d9..1e2ba23d9aa40 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m @@ -102,7 +102,7 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont #define AWT_FONT_CLEANUP_FINISH \ if (_fontThrowJavaException == YES) { \ char s[512]; \ - sprintf(s, "%s-%s:%d", __FILE__, __FUNCTION__, __LINE__); \ + snprintf(s, sizeof(s), "%s-%s:%d", __FILE__, __FUNCTION__, __LINE__); \ JNU_ThrowByName(env, "java/lang/RuntimeException", s); \ } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m index c745e8715c1db..fdd855b699417 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m @@ -1020,4 +1020,20 @@ @implementation CGGI_GlyphCanvas CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, glyphs, advances, count); } } -} \ No newline at end of file + int MAX_SIZE = 1 << 30; + if (bboxes) { + for (int i = 0; i < count; i++) { + if (bboxes[i].origin.x > (double)MAX_SIZE) bboxes[i].origin.x = 0; + if (bboxes[i].origin.y > (double)MAX_SIZE) bboxes[i].origin.y = 0; + if (bboxes[i].size.width > (double)MAX_SIZE) bboxes[i].size.width = 0; + if (bboxes[i].size.height > (double)MAX_SIZE) bboxes[i].size.height = 0; + } + } + if (advances) { + for (int i = 0; i < count; i++) { + if (advances[i].width > (double)MAX_SIZE) advances[i].width = 0; + if (advances[i].height > (double)MAX_SIZE) advances[i].height = 0; + } + } +} + diff --git a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp index ed2de311c22e7..5f868f6e408e9 100644 --- a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp +++ b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp @@ -635,7 +635,7 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { if (channelName == NULL) { return; } - sprintf(channelName, "Ch %d", ch); + snprintf(channelName, 16, "Ch %d", ch); } void* jControls[2]; diff --git a/src/java.desktop/share/classes/com/sun/beans/introspect/MethodInfo.java b/src/java.desktop/share/classes/com/sun/beans/introspect/MethodInfo.java index 5216f4423d474..25c9598839357 100644 --- a/src/java.desktop/share/classes/com/sun/beans/introspect/MethodInfo.java +++ b/src/java.desktop/share/classes/com/sun/beans/introspect/MethodInfo.java @@ -25,18 +25,35 @@ package com.sun.beans.introspect; +import java.io.Closeable; +import java.io.Externalizable; +import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Set; import com.sun.beans.TypeResolver; import com.sun.beans.finder.MethodFinder; final class MethodInfo { + + // These are some common interfaces that we know a priori + // will not contain any bean property getters or setters. + static final Set> IGNORABLE_INTERFACES = Set.of( + AutoCloseable.class, + Cloneable.class, + Closeable.class, + Comparable.class, + Externalizable.class, + Serializable.class + ); + final Method method; final Class type; @@ -66,6 +83,8 @@ static Class resolve(Method method, Type type) { static List get(Class type) { List list = null; if (type != null) { + + // Add declared methods boolean inaccessible = !Modifier.isPublic(type.getModifiers()); for (Method method : type.getMethods()) { if (method.getDeclaringClass().equals(type)) { @@ -81,10 +100,19 @@ static List get(Class type) { } } if (method != null) { - if (list == null) { - list = new ArrayList<>(); - } - list.add(method); + (list = createIfNeeded(list)).add(method); + } + } + } + + // Add default methods inherited from interfaces + for (Class iface : type.getInterfaces()) { + if (IGNORABLE_INTERFACES.contains(iface)) { + continue; + } + for (Method method : iface.getMethods()) { + if (!Modifier.isAbstract(method.getModifiers())) { + (list = createIfNeeded(list)).add(method); } } } @@ -96,6 +124,10 @@ static List get(Class type) { return Collections.emptyList(); } + private static List createIfNeeded(List list) { + return list != null ? list : new ArrayList<>(); + } + /** * A comparator that defines a total order so that methods have the same * name and identical signatures appear next to each others. diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPMetadata.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPMetadata.java index 4510882ad4b88..5e5a4a52d35b1 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPMetadata.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,19 +25,15 @@ package com.sun.imageio.plugins.bmp; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import javax.imageio.ImageTypeSpecifier; import javax.imageio.metadata.IIOMetadata; -import javax.imageio.metadata.IIOMetadataNode; -import javax.imageio.metadata.IIOMetadataFormat; import javax.imageio.metadata.IIOMetadataFormatImpl; -import org.w3c.dom.Node; -import com.sun.imageio.plugins.common.I18N; +import javax.imageio.metadata.IIOMetadataNode; +import com.sun.imageio.plugins.common.I18N; import com.sun.imageio.plugins.common.ImageUtil; +import org.w3c.dom.Node; + +import static java.nio.charset.StandardCharsets.ISO_8859_1; public class BMPMetadata extends IIOMetadata implements BMPConstants { public static final String nativeMetadataFormatName = @@ -114,11 +110,7 @@ public Node getAsTree(String formatName) { } private String toISO8859(byte[] data) { - try { - return new String(data, "ISO-8859-1"); - } catch (UnsupportedEncodingException e) { - return ""; - } + return new String(data, ISO_8859_1); } private Node getNativeTree() { diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/gif/GIFImageMetadata.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/gif/GIFImageMetadata.java index d865a58d431dc..061f3b1eaf017 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/gif/GIFImageMetadata.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/gif/GIFImageMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,18 +25,17 @@ package com.sun.imageio.plugins.gif; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import javax.imageio.ImageTypeSpecifier; + import javax.imageio.metadata.IIOInvalidTreeException; -import javax.imageio.metadata.IIOMetadata; -import javax.imageio.metadata.IIOMetadataNode; -import javax.imageio.metadata.IIOMetadataFormat; import javax.imageio.metadata.IIOMetadataFormatImpl; +import javax.imageio.metadata.IIOMetadataNode; + import org.w3c.dom.Node; +import static java.nio.charset.StandardCharsets.ISO_8859_1; + public class GIFImageMetadata extends GIFMetadata { // package scope @@ -132,11 +131,7 @@ public Node getAsTree(String formatName) { } private String toISO8859(byte[] data) { - try { - return new String(data, "ISO-8859-1"); - } catch (UnsupportedEncodingException e) { - return ""; - } + return new String(data, ISO_8859_1); } private Node getNativeTree() { @@ -384,12 +379,7 @@ public IIOMetadataNode getStandardTextNode() { while (commentIter.hasNext()) { byte[] comment = commentIter.next(); - String s = null; - try { - s = new String(comment, "ISO-8859-1"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Encoding ISO-8859-1 unknown!"); - } + String s = new String(comment, ISO_8859_1); node = new IIOMetadataNode("TextEntry"); node.setAttribute("value", s); diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/gif/GIFWritableImageMetadata.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/gif/GIFWritableImageMetadata.java index dc780602a902e..8566d45f6a482 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/gif/GIFWritableImageMetadata.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/gif/GIFWritableImageMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,19 +25,17 @@ package com.sun.imageio.plugins.gif; -import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import javax.imageio.ImageTypeSpecifier; + import javax.imageio.metadata.IIOInvalidTreeException; -import javax.imageio.metadata.IIOMetadata; -import javax.imageio.metadata.IIOMetadataNode; -import javax.imageio.metadata.IIOMetadataFormat; import javax.imageio.metadata.IIOMetadataFormatImpl; +import javax.imageio.metadata.IIOMetadataNode; + import org.w3c.dom.Node; +import static java.nio.charset.StandardCharsets.ISO_8859_1; + class GIFWritableImageMetadata extends GIFImageMetadata { // package scope @@ -95,11 +93,7 @@ public void reset() { } private byte[] fromISO8859(String data) { - try { - return data.getBytes("ISO-8859-1"); - } catch (UnsupportedEncodingException e) { - return "".getBytes(); - } + return data.getBytes(ISO_8859_1); } protected void mergeNativeTree(Node root) throws IIOInvalidTreeException { diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/AdobeMarkerSegment.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/AdobeMarkerSegment.java index f7063bdc25f9c..551f4dc4d725f 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/AdobeMarkerSegment.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/AdobeMarkerSegment.java @@ -55,15 +55,17 @@ class AdobeMarkerSegment extends MarkerSegment { AdobeMarkerSegment(JPEGBuffer buffer) throws IOException { super(buffer); - buffer.bufPtr += ID_SIZE; // Skip the id - version = (buffer.buf[buffer.bufPtr++] & 0xff) << 8; - version |= buffer.buf[buffer.bufPtr++] & 0xff; - flags0 = (buffer.buf[buffer.bufPtr++] & 0xff) << 8; - flags0 |= buffer.buf[buffer.bufPtr++] & 0xff; - flags1 = (buffer.buf[buffer.bufPtr++] & 0xff) << 8; - flags1 |= buffer.buf[buffer.bufPtr++] & 0xff; - transform = buffer.buf[buffer.bufPtr++] & 0xff; + int markPtr = buffer.bufPtr; + markPtr += ID_SIZE; // Skip the id + version = (buffer.buf[markPtr++] & 0xff) << 8; + version |= buffer.buf[markPtr++] & 0xff; + flags0 = (buffer.buf[markPtr++] & 0xff) << 8; + flags0 |= buffer.buf[markPtr++] & 0xff; + flags1 = (buffer.buf[markPtr++] & 0xff) << 8; + flags1 |= buffer.buf[markPtr++] & 0xff; + transform = buffer.buf[markPtr++] & 0xff; buffer.bufAvail -= length; + buffer.bufPtr += length; } AdobeMarkerSegment(Node node) throws IIOInvalidTreeException { diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/COMMarkerSegment.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/COMMarkerSegment.java index d4a996ee3eeca..f28e7b356581c 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/COMMarkerSegment.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/COMMarkerSegment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,15 +25,16 @@ package com.sun.imageio.plugins.jpeg; +import java.io.IOException; + +import javax.imageio.metadata.IIOInvalidTreeException; import javax.imageio.metadata.IIOMetadataNode; import javax.imageio.stream.ImageOutputStream; -import javax.imageio.metadata.IIOInvalidTreeException; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; import org.w3c.dom.Node; +import static java.nio.charset.StandardCharsets.ISO_8859_1; + /** * A Comment marker segment. Retains an array of bytes representing the * comment data as it is read from the stream. If the marker segment is @@ -45,7 +46,6 @@ * byte array, again assuming the default local encoding. */ class COMMarkerSegment extends MarkerSegment { - private static final String ENCODING = "ISO-8859-1"; /** * Constructs a marker segment from the given buffer, which contains @@ -96,10 +96,7 @@ class COMMarkerSegment extends MarkerSegment { * consulted directly. */ String getComment() { - try { - return new String (data, ENCODING); - } catch (UnsupportedEncodingException e) {} // Won't happen - return null; + return new String(data, ISO_8859_1); } /** diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JFIFMarkerSegment.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JFIFMarkerSegment.java index 1de4f53a81398..e944847ed57d7 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JFIFMarkerSegment.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JFIFMarkerSegment.java @@ -25,40 +25,41 @@ package com.sun.imageio.plugins.jpeg; -import javax.imageio.IIOException; -import javax.imageio.IIOImage; -import javax.imageio.ImageTypeSpecifier; -import javax.imageio.ImageReader; -import javax.imageio.metadata.IIOInvalidTreeException; -import javax.imageio.metadata.IIOMetadataNode; -import javax.imageio.metadata.IIOMetadata; -import javax.imageio.stream.ImageInputStream; -import javax.imageio.stream.ImageOutputStream; -import javax.imageio.stream.MemoryCacheImageOutputStream; -import javax.imageio.event.IIOReadProgressListener; - import java.awt.Graphics; -import java.awt.color.ICC_Profile; -import java.awt.color.ICC_ColorSpace; import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; +import java.awt.image.BufferedImage; import java.awt.image.ColorModel; -import java.awt.image.SampleModel; -import java.awt.image.IndexColorModel; import java.awt.image.ComponentColorModel; -import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; +import java.awt.image.IndexColorModel; import java.awt.image.Raster; +import java.awt.image.SampleModel; import java.awt.image.WritableRaster; -import java.io.IOException; import java.io.ByteArrayOutputStream; -import java.util.List; +import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; +import java.util.List; +import javax.imageio.IIOException; +import javax.imageio.IIOImage; +import javax.imageio.ImageReader; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.event.IIOReadProgressListener; +import javax.imageio.metadata.IIOInvalidTreeException; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageOutputStream; +import javax.imageio.stream.MemoryCacheImageOutputStream; + +import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import org.w3c.dom.NamedNodeMap; + +import static java.nio.charset.StandardCharsets.US_ASCII; /** * A JFIF (JPEG File Interchange Format) APP0 (Application-Specific) @@ -1353,7 +1354,7 @@ static void writeICC(ICC_Profile profile, ImageOutputStream ios) ios.write(0xff); ios.write(JPEG.APP2); MarkerSegment.write2bytes(ios, segLength); - byte [] id = ID.getBytes("US-ASCII"); + byte[] id = ID.getBytes(US_ASCII); ios.write(id); ios.write(0); // Null-terminate the string ios.write(chunkNum++); diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java index 8576419a4bf30..8cb93eaf727e6 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ import java.awt.image.WritableRaster; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.EOFException; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.SequenceInputStream; import java.util.ArrayList; import java.util.Arrays; @@ -47,19 +48,23 @@ import java.util.Iterator; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; + import javax.imageio.IIOException; -import javax.imageio.ImageReader; import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; import javax.imageio.ImageTypeSpecifier; import javax.imageio.metadata.IIOMetadata; import javax.imageio.spi.ImageReaderSpi; import javax.imageio.stream.ImageInputStream; + import com.sun.imageio.plugins.common.InputStreamAdapter; import com.sun.imageio.plugins.common.ReaderUtil; import com.sun.imageio.plugins.common.SubImageInputStream; -import java.io.ByteArrayOutputStream; import sun.awt.image.ByteInterleavedRaster; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.UTF_8; + class PNGImageDataEnumeration implements Enumeration { boolean firstTime = true; @@ -486,9 +491,9 @@ private void parse_iTXt_chunk(int chunkLength) throws IOException { stream.readFully(b); if (compressionFlag == 1) { // Decompress the text - text = new String(inflate(b), "UTF8"); + text = new String(inflate(b), UTF_8); } else { - text = new String(b, "UTF8"); + text = new String(b, UTF_8); } metadata.iTXt_text.add(text); @@ -589,7 +594,7 @@ private void parse_tEXt_chunk(int chunkLength) throws IOException { byte[] b = new byte[textLength]; stream.readFully(b); - metadata.tEXt_text.add(new String(b, "ISO-8859-1")); + metadata.tEXt_text.add(new String(b, ISO_8859_1)); // Check if the text chunk contains image creation time if (keyword.equals(PNGMetadata.tEXt_creationTimeKey)) { @@ -663,18 +668,9 @@ private void parse_tRNS_chunk(int chunkLength) throws IOException { private static byte[] inflate(byte[] b) throws IOException { InputStream bais = new ByteArrayInputStream(b); - InputStream iis = new InflaterInputStream(bais); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - int c; - try { - while ((c = iis.read()) != -1) { - baos.write(c); - } - } finally { - iis.close(); + try (InputStream iis = new InflaterInputStream(bais)) { + return iis.readAllBytes(); } - return baos.toByteArray(); } private void parse_zTXt_chunk(int chunkLength) throws IOException { @@ -690,7 +686,7 @@ private void parse_zTXt_chunk(int chunkLength) throws IOException { byte[] b = new byte[textLength]; stream.readFully(b); - metadata.zTXt_text.add(new String(inflate(b), "ISO-8859-1")); + metadata.zTXt_text.add(new String(inflate(b), ISO_8859_1)); // Check if the text chunk contains image creation time if (keyword.equals(PNGMetadata.tEXt_creationTimeKey)) { diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java index 9dc60991d131d..449661b8c9180 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,15 +28,16 @@ import java.awt.Rectangle; import java.awt.image.IndexColorModel; import java.awt.image.Raster; -import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Iterator; import java.util.Locale; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; + import javax.imageio.IIOException; import javax.imageio.IIOImage; import javax.imageio.ImageTypeSpecifier; @@ -47,6 +48,9 @@ import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStreamImpl; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.UTF_8; + final class CRC { private static final int[] crcTable = new int[256]; @@ -801,15 +805,14 @@ private void write_iTXt() throws IOException { cs.writeBytes(languageIter.next()); cs.writeByte(0); - - cs.write(translatedKeywordIter.next().getBytes("UTF8")); + cs.write(translatedKeywordIter.next().getBytes(UTF_8)); cs.writeByte(0); String text = textIter.next(); if (compressed) { - cs.write(deflate(text.getBytes("UTF8"))); + cs.write(deflate(text.getBytes(UTF_8))); } else { - cs.write(text.getBytes("UTF8")); + cs.write(text.getBytes(UTF_8)); } cs.finish(); } @@ -833,7 +836,7 @@ private void write_zTXt() throws IOException { cs.writeByte(compressionMethod); String text = textIter.next(); - cs.write(deflate(text.getBytes("ISO-8859-1"))); + cs.write(deflate(text.getBytes(ISO_8859_1))); cs.finish(); } } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java index 5c0334abbef89..3d307e9ad1b84 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java @@ -22,24 +22,27 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.imageio.plugins.tiff; import java.io.EOFException; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; + import javax.imageio.IIOException; -import javax.imageio.stream.ImageInputStream; -import javax.imageio.stream.ImageOutputStream; import javax.imageio.plugins.tiff.BaselineTIFFTagSet; import javax.imageio.plugins.tiff.TIFFDirectory; import javax.imageio.plugins.tiff.TIFFField; import javax.imageio.plugins.tiff.TIFFTag; import javax.imageio.plugins.tiff.TIFFTagSet; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageOutputStream; + +import static java.nio.charset.StandardCharsets.US_ASCII; public class TIFFIFD extends TIFFDirectory { private static final long MAX_SAMPLES_PER_PIXEL = 0xffff; @@ -283,8 +286,7 @@ private static int readFieldValue(ImageInputStream stream, if (inString) { // end of string String s = new String(bvalues, prevIndex, - index - prevIndex, - StandardCharsets.US_ASCII); + index - prevIndex, US_ASCII); v.add(s); inString = false; } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java index fdf9d49b46773..1d2cff4231105 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.imageio.plugins.tiff; import java.awt.Point; @@ -34,8 +35,8 @@ import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; -import java.awt.image.RenderedImage; import java.awt.image.Raster; +import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.EOFException; @@ -44,27 +45,30 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; + import javax.imageio.IIOException; import javax.imageio.IIOImage; +import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; -import javax.imageio.ImageTypeSpecifier; import javax.imageio.metadata.IIOInvalidTreeException; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataFormatImpl; -import javax.imageio.spi.ImageWriterSpi; -import javax.imageio.stream.ImageOutputStream; -import org.w3c.dom.Node; -import com.sun.imageio.plugins.common.ImageUtil; import javax.imageio.plugins.tiff.BaselineTIFFTagSet; import javax.imageio.plugins.tiff.ExifParentTIFFTagSet; import javax.imageio.plugins.tiff.ExifTIFFTagSet; import javax.imageio.plugins.tiff.TIFFField; import javax.imageio.plugins.tiff.TIFFTag; import javax.imageio.plugins.tiff.TIFFTagSet; +import javax.imageio.spi.ImageWriterSpi; +import javax.imageio.stream.ImageOutputStream; + +import com.sun.imageio.plugins.common.ImageUtil; import com.sun.imageio.plugins.common.SimpleRenderedImage; import com.sun.imageio.plugins.common.SingleTileRenderedImage; -import java.nio.charset.StandardCharsets; +import org.w3c.dom.Node; + +import static java.nio.charset.StandardCharsets.US_ASCII; public class TIFFImageWriter extends ImageWriter { @@ -1512,7 +1516,7 @@ void setupMetadata(ColorModel cm, SampleModel sm, (exifTags.getTag(ExifTIFFTagSet.TAG_EXIF_VERSION), TIFFTag.TIFF_UNDEFINED, 4, - ExifTIFFTagSet.EXIF_VERSION_2_2.getBytes(StandardCharsets.US_ASCII)); + ExifTIFFTagSet.EXIF_VERSION_2_2.getBytes(US_ASCII)); exifIFD.addTIFFField(f); } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/wbmp/WBMPImageReader.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/wbmp/WBMPImageReader.java index 5676733e65f33..ac62269b2d9b6 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/wbmp/WBMPImageReader.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/wbmp/WBMPImageReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -244,8 +244,8 @@ public BufferedImage read(int imageIndex, ImageReadParam param) } // If noTransform is necessary, read the data. - iis.read(((DataBufferByte)tile.getDataBuffer()).getData(), - 0, height*sm.getScanlineStride()); + iis.readFully(((DataBufferByte)tile.getDataBuffer()).getData(), + 0, height*sm.getScanlineStride()); processImageUpdate(bi, 0, 0, width, height, 1, 1, @@ -280,7 +280,7 @@ public BufferedImage read(int imageIndex, ImageReadParam param) if (abortRequested()) break; - iis.read(buf, 0, len); + iis.readFully(buf, 0, len); for (int i = 0; i < destinationRegion.width; i++) { //get the bit and assign to the data buffer of the raster int v = (buf[srcPos[i]] >> srcOff[i]) & 1; diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/wbmp/WBMPMetadata.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/wbmp/WBMPMetadata.java index 28ad467eadd9a..3a13bcca94ff9 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/wbmp/WBMPMetadata.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/wbmp/WBMPMetadata.java @@ -25,19 +25,13 @@ package com.sun.imageio.plugins.wbmp; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import javax.imageio.ImageTypeSpecifier; import javax.imageio.metadata.IIOMetadata; -import javax.imageio.metadata.IIOMetadataNode; -import javax.imageio.metadata.IIOMetadataFormat; import javax.imageio.metadata.IIOMetadataFormatImpl; -import org.w3c.dom.Node; -import com.sun.imageio.plugins.common.I18N; +import javax.imageio.metadata.IIOMetadataNode; +import com.sun.imageio.plugins.common.I18N; import com.sun.imageio.plugins.common.ImageUtil; +import org.w3c.dom.Node; public class WBMPMetadata extends IIOMetadata { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java index 7bb07cce2152a..eb32ca630ff3d 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java @@ -337,7 +337,8 @@ static WidgetType getWidgetType(JComponent c, Region id) { return widgets[0]; } } else if (id == Region.ARROW_BUTTON) { - if (c.getParent() instanceof JScrollBar) { + if (c.getParent() instanceof JScrollBar + || c.getParent() instanceof JTabbedPane) { Integer prop = (Integer) c.getClientProperty("__arrow_direction__"); int dir = (prop != null) ? diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java index 042ca1878747d..f3c3353180f65 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java @@ -22,29 +22,76 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.sun.java.swing.plaf.gtk; -import sun.swing.SwingUtilities2; -import com.sun.java.swing.plaf.gtk.GTKConstants.ArrowType; -import com.sun.java.swing.plaf.gtk.GTKConstants.ShadowType; +package com.sun.java.swing.plaf.gtk; +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Component; +import java.awt.Composite; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Insets; +import java.awt.LayoutManager; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RectangularShape; +import java.awt.image.FilteredImageSource; +import java.awt.image.ImageProducer; +import java.awt.image.RGBImageFilter; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JInternalFrame; import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.basic.BasicInternalFrameTitlePane; -import javax.swing.plaf.synth.*; - -import java.awt.*; -import java.awt.geom.*; -import java.awt.image.*; -import java.io.*; -import java.net.*; -import java.security.*; -import java.util.*; +import javax.swing.plaf.synth.ColorType; +import javax.swing.plaf.synth.SynthConstants; +import javax.swing.plaf.synth.SynthContext; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; -import javax.swing.*; - -import javax.xml.parsers.*; +import com.sun.java.swing.plaf.gtk.GTKConstants.ArrowType; +import com.sun.java.swing.plaf.gtk.GTKConstants.ShadowType; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import org.xml.sax.SAXException; -import org.w3c.dom.*; +import sun.swing.SwingUtilities2; + +import static java.nio.charset.StandardCharsets.ISO_8859_1; /** */ @@ -536,14 +583,16 @@ public Object run() { URL url = new URL(new File(userHome).toURI().toURL(), ".gconf/apps/metacity/general/%25gconf.xml"); // Pending: verify character encoding spec for gconf - Reader reader = new InputStreamReader(url.openStream(), "ISO-8859-1"); - char[] buf = new char[1024]; StringBuilder sb = new StringBuilder(); - int n; - while ((n = reader.read(buf)) >= 0) { - sb.append(buf, 0, n); + try (InputStream in = url.openStream(); + Reader reader = new InputStreamReader(in, ISO_8859_1)) + { + char[] buf = new char[1024]; + int n; + while ((n = reader.read(buf)) >= 0) { + sb.append(buf, 0, n); + } } - reader.close(); String str = sb.toString(); if (str != null) { String strLowerCase = str.toLowerCase(); diff --git a/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java b/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java index eac30372a4ff7..1330e48aae51d 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java @@ -48,10 +48,8 @@ public final class AudioFileSoundbankReader extends SoundbankReader { @Override public Soundbank getSoundbank(URL url) throws InvalidMidiDataException, IOException { - try { - AudioInputStream ais = AudioSystem.getAudioInputStream(url); + try (AudioInputStream ais = AudioSystem.getAudioInputStream(url)) { Soundbank sbk = getSoundbank(ais); - ais.close(); return sbk; } catch (UnsupportedAudioFileException e) { return null; diff --git a/src/java.desktop/share/classes/com/sun/media/sound/DLSSoundbank.java b/src/java.desktop/share/classes/com/sun/media/sound/DLSSoundbank.java index 55f937f033331..a0f1cd9357ea1 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/DLSSoundbank.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/DLSSoundbank.java @@ -47,6 +47,8 @@ import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; +import static java.nio.charset.StandardCharsets.US_ASCII; + /** * A DLS Level 1 and Level 2 soundbank reader (from files/url/streams). * @@ -189,22 +191,16 @@ public DLSSoundbank() { } public DLSSoundbank(URL url) throws IOException { - InputStream is = url.openStream(); - try { + try (InputStream is = url.openStream()) { readSoundbank(is); - } finally { - is.close(); } } public DLSSoundbank(File file) throws IOException { largeFormat = true; sampleFile = file; - InputStream is = new FileInputStream(file); - try { + try (InputStream is = new FileInputStream(file)) { readSoundbank(is); - } finally { - is.close(); } } @@ -873,15 +869,21 @@ private void readWaveInfoChunk(DLSSample dlssample, RIFFReader riff) } public void save(String name) throws IOException { - writeSoundbank(new RIFFWriter(name, "DLS ")); + try (RIFFWriter writer = new RIFFWriter(name, "DLS ")) { + writeSoundbank(writer); + } } public void save(File file) throws IOException { - writeSoundbank(new RIFFWriter(file, "DLS ")); + try (RIFFWriter writer = new RIFFWriter(file, "DLS ")) { + writeSoundbank(writer); + } } public void save(OutputStream out) throws IOException { - writeSoundbank(new RIFFWriter(out, "DLS ")); + try (RIFFWriter writer = new RIFFWriter(out, "DLS ")) { + writeSoundbank(writer); + } } private void writeSoundbank(RIFFWriter writer) throws IOException { @@ -921,8 +923,6 @@ private void writeSoundbank(RIFFWriter writer) throws IOException { writer.seek(bak); writeInfo(writer.writeList("INFO"), info); - - writer.close(); } private void writeSample(RIFFWriter writer, DLSSample sample) @@ -1147,7 +1147,7 @@ private void writeInfoStringChunk(RIFFWriter writer, return; RIFFWriter chunk = writer.writeChunk(name); chunk.writeString(value); - int len = value.getBytes("ascii").length; + int len = value.getBytes(US_ASCII).length; chunk.write(0); len++; if (len % 2 != 0) diff --git a/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java b/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java index f64c9a2327c3a..6447e654f600e 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java @@ -63,8 +63,7 @@ public final class JARSoundbankReader extends SoundbankReader { private static boolean isZIP(URL url) { boolean ok = false; try { - InputStream stream = url.openStream(); - try { + try (InputStream stream = url.openStream()) { byte[] buff = new byte[4]; ok = stream.read(buff) == 4; if (ok) { @@ -73,8 +72,6 @@ private static boolean isZIP(URL url) { && buff[2] == 0x03 && buff[3] == 0x04); } - } finally { - stream.close(); } } catch (IOException e) { } @@ -95,8 +92,7 @@ public Soundbank getSoundbank(URL url) "META-INF/services/javax.sound.midi.Soundbank"); if (stream == null) return null; - try - { + try (stream) { BufferedReader r = new BufferedReader(new InputStreamReader(stream)); String line = r.readLine(); while (line != null) { @@ -114,10 +110,6 @@ public Soundbank getSoundbank(URL url) line = r.readLine(); } } - finally - { - stream.close(); - } if (soundbanks.size() == 0) return null; if (soundbanks.size() == 1) diff --git a/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBuffer.java b/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBuffer.java index 13cbf54fe6d27..2476ff09bbcc7 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBuffer.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBuffer.java @@ -315,11 +315,13 @@ public void load() throws IOException { "No file associated with this ByteBuffer!"); } - DataInputStream is = new DataInputStream(getInputStream()); - buffer = new byte[(int) capacity()]; - offset = 0; - is.readFully(buffer); - is.close(); + try (InputStream is = getInputStream(); + DataInputStream dis = new DataInputStream(is)) + { + buffer = new byte[(int) capacity()]; + offset = 0; + dis.readFully(buffer); + } } diff --git a/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java b/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java index 45fa29542117d..bc8829d288c6f 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -182,18 +182,12 @@ public AudioFormat getFormat() { if (format == null) { if (buffer == null) return null; - InputStream is = buffer.getInputStream(); AudioFormat format = null; - try { + try (InputStream is = buffer.getInputStream()) { format = AudioSystem.getAudioFileFormat(is).getFormat(); } catch (Exception e) { //e.printStackTrace(); } - try { - is.close(); - } catch (IOException e) { - //e.printStackTrace(); - } return format; } return format; diff --git a/src/java.desktop/share/classes/com/sun/media/sound/RIFFReader.java b/src/java.desktop/share/classes/com/sun/media/sound/RIFFReader.java index 4266fbe819393..62789e4493ffb 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/RIFFReader.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/RIFFReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ import java.io.IOException; import java.io.InputStream; +import static java.nio.charset.StandardCharsets.US_ASCII; + /** * Resource Interchange File Format (RIFF) stream decoder. * @@ -76,14 +78,14 @@ public RIFFReader(final InputStream stream) throws IOException { byte[] fourcc = new byte[4]; fourcc[0] = (byte) b; readFully(fourcc, 1, 3); - this.fourcc = new String(fourcc, "ascii"); + this.fourcc = new String(fourcc, US_ASCII); ckSize = readUnsignedInt(); avail = ckSize; if (getFormat().equals("RIFF") || getFormat().equals("LIST")) { byte[] format = new byte[4]; readFully(format); - this.riff_type = new String(format, "ascii"); + this.riff_type = new String(format, US_ASCII); } } @@ -227,10 +229,10 @@ public String readString(final int len) throws IOException { readFully(buff); for (int i = 0; i < buff.length; i++) { if (buff[i] == 0) { - return new String(buff, 0, i, "ascii"); + return new String(buff, 0, i, US_ASCII); } } - return new String(buff, "ascii"); + return new String(buff, US_ASCII); } // Read 8 bit signed integer from stream diff --git a/src/java.desktop/share/classes/com/sun/media/sound/RIFFWriter.java b/src/java.desktop/share/classes/com/sun/media/sound/RIFFWriter.java index f6943c9ef3c79..6650e413613e9 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/RIFFWriter.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/RIFFWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,8 @@ import java.io.OutputStream; import java.io.RandomAccessFile; +import static java.nio.charset.StandardCharsets.US_ASCII; + /** * Resource Interchange File Format (RIFF) stream encoder. * @@ -208,11 +210,11 @@ private RIFFWriter(RandomAccessWriter raf, String format, int chunktype) raf.write(0); if (chunktype == 0) - raf.write("RIFF".getBytes("ascii")); + raf.write("RIFF".getBytes(US_ASCII)); else if (chunktype == 1) - raf.write("LIST".getBytes("ascii")); + raf.write("LIST".getBytes(US_ASCII)); else - raf.write((format + " ").substring(0, 4).getBytes("ascii")); + raf.write((format + " ").substring(0, 4).getBytes(US_ASCII)); chunksizepointer = raf.getPointer(); this.chunktype = 2; @@ -220,8 +222,7 @@ else if (chunktype == 1) this.chunktype = chunktype; startpointer = raf.getPointer(); if (chunktype != 2) - raf.write((format + " ").substring(0, 4).getBytes("ascii")); - + raf.write((format + " ").substring(0, 4).getBytes(US_ASCII)); } public void seek(long pos) throws IOException { diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SF2Soundbank.java b/src/java.desktop/share/classes/com/sun/media/sound/SF2Soundbank.java index 7d7cff3637756..831423664e69c 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/SF2Soundbank.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/SF2Soundbank.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,8 @@ import javax.sound.midi.Soundbank; import javax.sound.midi.SoundbankResource; +import static java.nio.charset.StandardCharsets.US_ASCII; + /** * A SoundFont 2.04 soundbank reader. * @@ -90,23 +92,16 @@ public SF2Soundbank() { } public SF2Soundbank(URL url) throws IOException { - - InputStream is = url.openStream(); - try { + try (InputStream is = url.openStream()) { readSoundbank(is); - } finally { - is.close(); } } public SF2Soundbank(File file) throws IOException { largeFormat = true; sampleFile = file; - InputStream is = new FileInputStream(file); - try { + try (InputStream is = new FileInputStream(file)) { readSoundbank(is); - } finally { - is.close(); } } @@ -515,22 +510,27 @@ private void readPdtaChunk(RIFFReader riff) throws IOException { } public void save(String name) throws IOException { - writeSoundbank(new RIFFWriter(name, "sfbk")); + try (RIFFWriter writer = new RIFFWriter(name, "sfbk")) { + writeSoundbank(writer); + } } public void save(File file) throws IOException { - writeSoundbank(new RIFFWriter(file, "sfbk")); + try (RIFFWriter writer = new RIFFWriter(file, "sfbk")) { + writeSoundbank(writer); + } } public void save(OutputStream out) throws IOException { - writeSoundbank(new RIFFWriter(out, "sfbk")); + try (RIFFWriter writer = new RIFFWriter(out, "sfbk")) { + writeSoundbank(writer); + } } private void writeSoundbank(RIFFWriter writer) throws IOException { writeInfo(writer.writeList("INFO")); writeSdtaChunk(writer.writeList("sdta")); writePdtaChunk(writer.writeList("pdta")); - writer.close(); } private void writeInfoStringChunk(RIFFWriter writer, String name, @@ -539,7 +539,7 @@ private void writeInfoStringChunk(RIFFWriter writer, String name, return; RIFFWriter chunk = writer.writeChunk(name); chunk.writeString(value); - int len = value.getBytes("ascii").length; + int len = value.getBytes(US_ASCII).length; chunk.write(0); len++; if (len % 2 != 0) diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SoftMainMixer.java b/src/java.desktop/share/classes/com/sun/media/sound/SoftMainMixer.java index f603375f3b51d..616332f105f32 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/SoftMainMixer.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/SoftMainMixer.java @@ -124,102 +124,126 @@ public double[] get(int instance, String name) { private void processSystemExclusiveMessage(byte[] data) { synchronized (synth.control_mutex) { activity(); - + if (data.length < 3 || (data[1] & 0xFF) != 0x7E && (data[1] & 0xFF) != 0x7F ) { + // Not enough data to determine SysEx type or SysEx type is not supported + return; + } // Universal Non-Real-Time SysEx if ((data[1] & 0xFF) == 0x7E) { int deviceID = data[2] & 0xFF; if (deviceID == 0x7F || deviceID == synth.getDeviceID()) { + if (data.length < 4) { + return; + } int subid1 = data[3] & 0xFF; int subid2; switch (subid1) { - case 0x08: // MIDI Tuning Standard - subid2 = data[4] & 0xFF; - switch (subid2) { - case 0x01: // BULK TUNING DUMP - { - // http://www.midi.org/about-midi/tuning.shtml - SoftTuning tuning = synth.getTuning(new Patch(0, - data[5] & 0xFF)); - tuning.load(data); - break; - } - case 0x04: // KEY-BASED TUNING DUMP - case 0x05: // SCALE/OCTAVE TUNING DUMP, 1 byte format - case 0x06: // SCALE/OCTAVE TUNING DUMP, 2 byte format - case 0x07: // SINGLE NOTE TUNING CHANGE (NON REAL-TIME) + case 0x08: // MIDI Tuning Standard + if (data.length < 5) { + break; + } + subid2 = data[4] & 0xFF; + switch (subid2) { + case 0x01: // BULK TUNING DUMP + { + if (data.length < 6) { + break; + } + // http://www.midi.org/about-midi/tuning.shtml + SoftTuning tuning = synth.getTuning(new Patch(0, + data[5] & 0xFF)); + tuning.load(data); + break; + } + case 0x04: // KEY-BASED TUNING DUMP + case 0x05: // SCALE/OCTAVE TUNING DUMP, 1 byte format + case 0x06: // SCALE/OCTAVE TUNING DUMP, 2 byte format + case 0x07: // SINGLE NOTE TUNING CHANGE (NON REAL-TIME) // (BANK) - { - // http://www.midi.org/about-midi/tuning_extens.shtml - SoftTuning tuning = synth.getTuning(new Patch( - data[5] & 0xFF, data[6] & 0xFF)); - tuning.load(data); - break; - } - case 0x08: // scale/octave tuning 1-byte form (Non + { + if (data.length < 7) { + break; + } + // http://www.midi.org/about-midi/tuning_extens.shtml + SoftTuning tuning = synth.getTuning(new Patch( + data[5] & 0xFF, data[6] & 0xFF)); + tuning.load(data); // Check inside! + break; + } + case 0x08: // scale/octave tuning 1-byte form (Non // Real-Time) - case 0x09: // scale/octave tuning 2-byte form (Non + case 0x09: // scale/octave tuning 2-byte form (Non // Real-Time) - { - // http://www.midi.org/about-midi/tuning-scale.shtml - SoftTuning tuning = new SoftTuning(data); - int channelmask = (data[5] & 0xFF) * 16384 - + (data[6] & 0xFF) * 128 + (data[7] & 0xFF); - SoftChannel[] channels = synth.channels; - for (int i = 0; i < channels.length; i++) - if ((channelmask & (1 << i)) != 0) - channels[i].tuning = tuning; - break; - } - default: - break; - } - break; - case 0x09: // General Midi Message - subid2 = data[4] & 0xFF; - switch (subid2) { - case 0x01: // General Midi 1 On - synth.setGeneralMidiMode(1); - reset(); - break; - case 0x02: // General Midi Off - synth.setGeneralMidiMode(0); - reset(); - break; - case 0x03: // General MidI Level 2 On - synth.setGeneralMidiMode(2); - reset(); - break; - default: - break; - } - break; - case 0x0A: // DLS Message - subid2 = data[4] & 0xFF; - switch (subid2) { - case 0x01: // DLS On - if (synth.getGeneralMidiMode() == 0) - synth.setGeneralMidiMode(1); - synth.voice_allocation_mode = 1; - reset(); - break; - case 0x02: // DLS Off - synth.setGeneralMidiMode(0); - synth.voice_allocation_mode = 0; - reset(); + { + if (data.length < 8) { + break; + } + // http://www.midi.org/about-midi/tuning-scale.shtml + SoftTuning tuning = new SoftTuning(data); + int channelmask = (data[5] & 0xFF) * 16384 + + (data[6] & 0xFF) * 128 + (data[7] & 0xFF); + SoftChannel[] channels = synth.channels; + for (int i = 0; i < channels.length; i++) + if ((channelmask & (1 << i)) != 0) + channels[i].tuning = tuning; + break; + } + default: + break; + } break; - case 0x03: // DLS Static Voice Allocation Off - synth.voice_allocation_mode = 0; + case 0x09: // General Midi Message + if (data.length < 5) { + break; + } + subid2 = data[4] & 0xFF; + switch (subid2) { + case 0x01: // General Midi 1 On + synth.setGeneralMidiMode(1); + reset(); + break; + case 0x02: // General Midi Off + synth.setGeneralMidiMode(0); + reset(); + break; + case 0x03: // General MidI Level 2 On + synth.setGeneralMidiMode(2); + reset(); + break; + default: + break; + } break; - case 0x04: // DLS Static Voice Allocation On - synth.voice_allocation_mode = 1; + case 0x0A: // DLS Message + if (data.length < 5) { + break; + } + subid2 = data[4] & 0xFF; + switch (subid2) { + case 0x01: // DLS On + if (synth.getGeneralMidiMode() == 0) + synth.setGeneralMidiMode(1); + synth.voice_allocation_mode = 1; + reset(); + break; + case 0x02: // DLS Off + synth.setGeneralMidiMode(0); + synth.voice_allocation_mode = 0; + reset(); + break; + case 0x03: // DLS Static Voice Allocation Off + synth.voice_allocation_mode = 0; + break; + case 0x04: // DLS Static Voice Allocation On + synth.voice_allocation_mode = 1; + break; + default: + break; + } break; + default: break; - } - break; - - default: - break; } } } @@ -228,197 +252,240 @@ private void processSystemExclusiveMessage(byte[] data) { if ((data[1] & 0xFF) == 0x7F) { int deviceID = data[2] & 0xFF; if (deviceID == 0x7F || deviceID == synth.getDeviceID()) { + if (data.length < 4) { + return; + } int subid1 = data[3] & 0xFF; int subid2; switch (subid1) { - case 0x04: // Device Control - - subid2 = data[4] & 0xFF; - switch (subid2) { - case 0x01: // Master Volume - case 0x02: // Master Balane - case 0x03: // Master fine tuning - case 0x04: // Master coarse tuning - int val = (data[5] & 0x7F) - + ((data[6] & 0x7F) * 128); - if (subid2 == 0x01) - setVolume(val); - else if (subid2 == 0x02) - setBalance(val); - else if (subid2 == 0x03) - setFineTuning(val); - else if (subid2 == 0x04) - setCoarseTuning(val); - break; - case 0x05: // Global Parameter Control - int ix = 5; - int slotPathLen = (data[ix++] & 0xFF); - int paramWidth = (data[ix++] & 0xFF); - int valueWidth = (data[ix++] & 0xFF); - int[] slotPath = new int[slotPathLen]; - for (int i = 0; i < slotPathLen; i++) { - int msb = (data[ix++] & 0xFF); - int lsb = (data[ix++] & 0xFF); - slotPath[i] = msb * 128 + lsb; + case 0x04: // Device Control + if (data.length < 5) { + break; } - int paramCount = (data.length - 1 - ix) - / (paramWidth + valueWidth); - long[] params = new long[paramCount]; - long[] values = new long[paramCount]; - for (int i = 0; i < paramCount; i++) { - values[i] = 0; - for (int j = 0; j < paramWidth; j++) - params[i] = params[i] * 128 - + (data[ix++] & 0xFF); - for (int j = 0; j < valueWidth; j++) - values[i] = values[i] * 128 - + (data[ix++] & 0xFF); - + subid2 = data[4] & 0xFF; + switch (subid2) { + case 0x01: // Master Volume + case 0x02: // Master Balane + case 0x03: // Master fine tuning + case 0x04: // Master coarse tuning + if (data.length < 7) { + break; + } + int val = (data[5] & 0x7F) + + ((data[6] & 0x7F) * 128); + if (subid2 == 0x01) + setVolume(val); + else if (subid2 == 0x02) + setBalance(val); + else if (subid2 == 0x03) + setFineTuning(val); + else if (subid2 == 0x04) + setCoarseTuning(val); + break; + case 0x05: // Global Parameter Control + if (data.length < 6) { + break; + } + int ix = 5; + int slotPathLen = (data[ix++] & 0xFF); + if (data.length < slotPathLen * 2 + 8) { + break; + } + int paramWidth = (data[ix++] & 0xFF); + int valueWidth = (data[ix++] & 0xFF); + int[] slotPath = new int[slotPathLen]; + for (int i = 0; i < slotPathLen; i++) { + int msb = (data[ix++] & 0xFF); + int lsb = (data[ix++] & 0xFF); + slotPath[i] = msb * 128 + lsb; + } + int paramCount = (data.length - 1 - ix) + / (paramWidth + valueWidth); + if (paramCount < 1) { + break; + } + long[] params = new long[paramCount]; + long[] values = new long[paramCount]; + for (int i = 0; i < paramCount; i++) { + values[i] = 0; + for (int j = 0; j < paramWidth; j++) + params[i] = params[i] * 128 + + (data[ix++] & 0xFF); + for (int j = 0; j < valueWidth; j++) + values[i] = values[i] * 128 + + (data[ix++] & 0xFF); + + } + globalParameterControlChange(slotPath, params, values); + break; + default: + break; } - globalParameterControlChange(slotPath, params, values); - break; - default: break; - } - break; - case 0x08: // MIDI Tuning Standard - subid2 = data[4] & 0xFF; - switch (subid2) { - case 0x02: // SINGLE NOTE TUNING CHANGE (REAL-TIME) - { - // http://www.midi.org/about-midi/tuning.shtml - SoftTuning tuning = synth.getTuning(new Patch(0, - data[5] & 0xFF)); - tuning.load(data); - SoftVoice[] voices = synth.getVoices(); - for (int i = 0; i < voices.length; i++) - if (voices[i].active) - if (voices[i].tuning == tuning) - voices[i].updateTuning(tuning); - break; - } - case 0x07: // SINGLE NOTE TUNING CHANGE (REAL-TIME) + case 0x08: // MIDI Tuning Standard + if (data.length < 5) { + break; + } + subid2 = data[4] & 0xFF; + switch (subid2) { + case 0x02: // SINGLE NOTE TUNING CHANGE (REAL-TIME) + { + // http://www.midi.org/about-midi/tuning.shtml + if (data.length < 6) { + break; + } + SoftTuning tuning = synth.getTuning(new Patch(0, + data[5] & 0xFF)); + tuning.load(data); + SoftVoice[] voices = synth.getVoices(); + for (int i = 0; i < voices.length; i++) + if (voices[i].active) + if (voices[i].tuning == tuning) + voices[i].updateTuning(tuning); + break; + } + case 0x07: // SINGLE NOTE TUNING CHANGE (REAL-TIME) // (BANK) - { - // http://www.midi.org/about-midi/tuning_extens.shtml - SoftTuning tuning = synth.getTuning(new Patch( - data[5] & 0xFF, data[6] & 0xFF)); - tuning.load(data); - SoftVoice[] voices = synth.getVoices(); - for (int i = 0; i < voices.length; i++) - if (voices[i].active) - if (voices[i].tuning == tuning) - voices[i].updateTuning(tuning); - break; - } - case 0x08: // scale/octave tuning 1-byte form + { + // http://www.midi.org/about-midi/tuning_extens.shtml + if (data.length < 7) { + break; + } + SoftTuning tuning = synth.getTuning(new Patch( + data[5] & 0xFF, data[6] & 0xFF)); + tuning.load(data); + SoftVoice[] voices = synth.getVoices(); + for (int i = 0; i < voices.length; i++) + if (voices[i].active) + if (voices[i].tuning == tuning) + voices[i].updateTuning(tuning); + break; + } + case 0x08: // scale/octave tuning 1-byte form //(Real-Time) - case 0x09: // scale/octave tuning 2-byte form + case 0x09: // scale/octave tuning 2-byte form // (Real-Time) - { - // http://www.midi.org/about-midi/tuning-scale.shtml - SoftTuning tuning = new SoftTuning(data); - int channelmask = (data[5] & 0xFF) * 16384 - + (data[6] & 0xFF) * 128 + (data[7] & 0xFF); - SoftChannel[] channels = synth.channels; - for (int i = 0; i < channels.length; i++) - if ((channelmask & (1 << i)) != 0) - channels[i].tuning = tuning; - SoftVoice[] voices = synth.getVoices(); - for (int i = 0; i < voices.length; i++) - if (voices[i].active) - if ((channelmask & (1 << (voices[i].channel))) != 0) - voices[i].updateTuning(tuning); - break; - } - default: - break; - } - break; - case 0x09: // Control Destination Settings - subid2 = data[4] & 0xFF; - switch (subid2) { - case 0x01: // Channel Pressure - { - int[] destinations = new int[(data.length - 7) / 2]; - int[] ranges = new int[(data.length - 7) / 2]; - int ix = 0; - for (int j = 6; j < data.length - 1; j += 2) { - destinations[ix] = data[j] & 0xFF; - ranges[ix] = data[j + 1] & 0xFF; - ix++; + { + // http://www.midi.org/about-midi/tuning-scale.shtml + if (data.length < 8) { + break; + } + SoftTuning tuning = new SoftTuning(data); + int channelmask = (data[5] & 0xFF) * 16384 + + (data[6] & 0xFF) * 128 + (data[7] & 0xFF); + SoftChannel[] channels = synth.channels; + for (int i = 0; i < channels.length; i++) + if ((channelmask & (1 << i)) != 0) + channels[i].tuning = tuning; + SoftVoice[] voices = synth.getVoices(); + for (int i = 0; i < voices.length; i++) + if (voices[i].active) + if ((channelmask & (1 << (voices[i].channel))) != 0) + voices[i].updateTuning(tuning); + break; + } + default: + break; } - int channel = data[5] & 0xFF; - SoftChannel softchannel = synth.channels[channel]; - softchannel.mapChannelPressureToDestination( - destinations, ranges); break; - } - case 0x02: // Poly Pressure - { - int[] destinations = new int[(data.length - 7) / 2]; - int[] ranges = new int[(data.length - 7) / 2]; - int ix = 0; - for (int j = 6; j < data.length - 1; j += 2) { - destinations[ix] = data[j] & 0xFF; - ranges[ix] = data[j + 1] & 0xFF; - ix++; + case 0x09: // Control Destination Settings + if (data.length < 5) { + break; } - int channel = data[5] & 0xFF; - SoftChannel softchannel = synth.channels[channel]; - softchannel.mapPolyPressureToDestination( - destinations, ranges); - break; - } - case 0x03: // Control Change - { - int[] destinations = new int[(data.length - 7) / 2]; - int[] ranges = new int[(data.length - 7) / 2]; - int ix = 0; - for (int j = 7; j < data.length - 1; j += 2) { - destinations[ix] = data[j] & 0xFF; - ranges[ix] = data[j + 1] & 0xFF; - ix++; + subid2 = data[4] & 0xFF; + switch (subid2) { + case 0x01: // Channel Pressure + { + if (data.length < 8) { + break; + } + int[] destinations = new int[(data.length - 6) / 2]; + int[] ranges = new int[(data.length - 6) / 2]; + int ix = 0; + for (int j = 6; j < data.length - 1; j += 2) { + destinations[ix] = data[j] & 0xFF; + ranges[ix] = data[j + 1] & 0xFF; + ix++; + } + int channel = data[5] & 0xFF; + SoftChannel softchannel = synth.channels[channel]; + softchannel.mapChannelPressureToDestination( + destinations, ranges); + break; + } + case 0x02: // Poly Pressure + { + if (data.length < 8) { + break; + } + int[] destinations = new int[(data.length - 6) / 2]; + int[] ranges = new int[(data.length - 6) / 2]; + int ix = 0; + for (int j = 6; j < data.length - 1; j += 2) { + destinations[ix] = data[j] & 0xFF; + ranges[ix] = data[j + 1] & 0xFF; + ix++; + } + int channel = data[5] & 0xFF; + SoftChannel softchannel = synth.channels[channel]; + softchannel.mapPolyPressureToDestination( + destinations, ranges); + break; + } + case 0x03: // Control Change + { + if (data.length < 8) { + break; + } + int[] destinations = new int[(data.length - 7) / 2]; + int[] ranges = new int[(data.length - 7) / 2]; + int ix = 0; + for (int j = 7; j < data.length - 1; j += 2) { + destinations[ix] = data[j] & 0xFF; + ranges[ix] = data[j + 1] & 0xFF; + ix++; + } + int channel = data[5] & 0xFF; + SoftChannel softchannel = synth.channels[channel]; + int control = data[6] & 0xFF; + softchannel.mapControlToDestination(control, + destinations, ranges); + break; + } + default: + break; } - int channel = data[5] & 0xFF; - SoftChannel softchannel = synth.channels[channel]; - int control = data[6] & 0xFF; - softchannel.mapControlToDestination(control, - destinations, ranges); - break; - } - default: break; - } - break; - case 0x0A: // Key Based Instrument Control - { - subid2 = data[4] & 0xFF; - switch (subid2) { - case 0x01: // Basic Message - int channel = data[5] & 0xFF; - int keynumber = data[6] & 0xFF; - SoftChannel softchannel = synth.channels[channel]; - for (int j = 7; j < data.length - 1; j += 2) { - int controlnumber = data[j] & 0xFF; - int controlvalue = data[j + 1] & 0xFF; - softchannel.controlChangePerNote(keynumber, - controlnumber, controlvalue); + case 0x0A: // Key Based Instrument Control + { + if (data.length < 8 || (data[4] & 0xFF) != 0x01) { + break; + } + subid2 = data[4] & 0xFF; + switch (subid2) { + case 0x01: // Basic Message + int channel = data[5] & 0xFF; + int keynumber = data[6] & 0xFF; + SoftChannel softchannel = synth.channels[channel]; + for (int j = 7; j < data.length - 1; j += 2) { + int controlnumber = data[j] & 0xFF; + int controlvalue = data[j + 1] & 0xFF; + softchannel.controlChangePerNote(keynumber, + controlnumber, controlvalue); + } + break; + default: + break; } break; + } default: break; - } - break; - } - default: - break; } } } - } } diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java b/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java index 70b8b622df9c3..ee37cb7198bc6 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java @@ -289,12 +289,11 @@ private boolean loadInstruments(List instruments) { c.current_instrument = null; c.current_director = null; } - for (Instrument instrument : instruments) { + for (ModelInstrument instrument : instruments) { String pat = patchToString(instrument.getPatch()); - SoftInstrument softins - = new SoftInstrument((ModelInstrument) instrument); + SoftInstrument softins = new SoftInstrument(instrument); inslist.put(pat, softins); - loadedlist.put(pat, (ModelInstrument) instrument); + loadedlist.put(pat, instrument); } } @@ -756,10 +755,8 @@ public InputStream run() { InputStream is = AccessController.doPrivileged(action); if(is == null) continue; Soundbank sbk; - try { + try (is) { sbk = MidiSystem.getSoundbank(new BufferedInputStream(is)); - } finally { - is.close(); } if (sbk != null) { defaultSoundBank = sbk; @@ -803,9 +800,8 @@ public InputStream run() { return null; }); if (out != null) { - try { + try (out) { ((SF2Soundbank) defaultSoundBank).save(out); - out.close(); } catch (final IOException ignored) { } } diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SoftTuning.java b/src/java.desktop/share/classes/com/sun/media/sound/SoftTuning.java index 9ed8871f52379..b951ffacc23ea 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/SoftTuning.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/SoftTuning.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,12 @@ package com.sun.media.sound; -import java.io.UnsupportedEncodingException; import java.util.Arrays; import javax.sound.midi.Patch; +import static java.nio.charset.StandardCharsets.US_ASCII; + /** * A tuning program container, for use with MIDI Tuning. * See: http://www.midi.org @@ -88,10 +89,21 @@ private boolean checksumOK2(byte[] data) { */ public void load(byte[] data) { // Universal Non-Real-Time / Real-Time SysEx + if (data.length < 2) { + return; + } + if ((data[1] & 0xFF) == 0x7E || (data[1] & 0xFF) == 0x7F) { + if (data.length < 4) { + return; + } + int subid1 = data[3] & 0xFF; switch (subid1) { case 0x08: // MIDI Tuning Standard + if (data.length < 5) { + break; + } int subid2 = data[4] & 0xFF; switch (subid2) { case 0x01: // BULK TUNING DUMP (NON-REAL-TIME) @@ -99,12 +111,11 @@ public void load(byte[] data) { // http://www.midi.org/about-midi/tuning.shtml //if (!checksumOK2(data)) // break; - try { - name = new String(data, 6, 16, "ascii"); - } catch (UnsupportedEncodingException e) { - name = null; - } int r = 22; + if (data.length < 128 * 3 + r) { + break; + } + name = new String(data, 6, 16, US_ASCII); for (int i = 0; i < 128; i++) { int xx = data[r++] & 0xFF; int yy = data[r++] & 0xFF; @@ -118,8 +129,14 @@ public void load(byte[] data) { case 0x02: // SINGLE NOTE TUNING CHANGE (REAL-TIME) { // http://www.midi.org/about-midi/tuning.shtml + if (data.length < 7) { + break; + } int ll = data[6] & 0xFF; int r = 7; + if (data.length < ll * 4 + r) { + break; + } for (int i = 0; i < ll; i++) { int kk = data[r++] & 0xFF; int xx = data[r++] & 0xFF; @@ -135,11 +152,10 @@ public void load(byte[] data) { // http://www.midi.org/about-midi/tuning_extens.shtml if (!checksumOK(data)) break; - try { - name = new String(data, 7, 16, "ascii"); - } catch (UnsupportedEncodingException e) { - name = null; + if (data.length < 407) { + break; } + name = new String(data, 7, 16, US_ASCII); int r = 23; for (int i = 0; i < 128; i++) { int xx = data[r++] & 0xFF; @@ -156,11 +172,10 @@ public void load(byte[] data) { // http://www.midi.org/about-midi/tuning_extens.shtml if (!checksumOK(data)) break; - try { - name = new String(data, 7, 16, "ascii"); - } catch (UnsupportedEncodingException e) { - name = null; + if (data.length < 35) { + break; } + name = new String(data, 7, 16, US_ASCII); int[] octave_tuning = new int[12]; for (int i = 0; i < 12; i++) octave_tuning[i] = (data[i + 23] & 0xFF) - 64; @@ -174,11 +189,10 @@ public void load(byte[] data) { // http://www.midi.org/about-midi/tuning_extens.shtml if (!checksumOK(data)) break; - try { - name = new String(data, 7, 16, "ascii"); - } catch (UnsupportedEncodingException e) { - name = null; + if (data.length < 47) { + break; } + name = new String(data, 7, 16, US_ASCII); double[] octave_tuning = new double[12]; for (int i = 0; i < 12; i++) { int v = (data[i * 2 + 23] & 0xFF) * 128 @@ -192,7 +206,13 @@ public void load(byte[] data) { case 0x07: // SINGLE NOTE TUNING CHANGE (NON // REAL-TIME/REAL-TIME) (BANK) // http://www.midi.org/about-midi/tuning_extens.shtml + if (data.length < 8) { + break; + } int ll = data[7] & 0xFF; + if (data.length < ll * 4 + 8) { + break; + } int r = 8; for (int i = 0; i < ll; i++) { int kk = data[r++] & 0xFF; @@ -208,6 +228,9 @@ public void load(byte[] data) { // Real-Time/REAL-TIME) { // http://www.midi.org/about-midi/tuning-scale.shtml + if (data.length < 20) { + break; + } int[] octave_tuning = new int[12]; for (int i = 0; i < 12; i++) octave_tuning[i] = (data[i + 8] & 0xFF) - 64; @@ -219,6 +242,9 @@ public void load(byte[] data) { // Real-Time/REAL-TIME) { // http://www.midi.org/about-midi/tuning-scale.shtml + if (data.length < 32) { + break; + } double[] octave_tuning = new double[12]; for (int i = 0; i < 12; i++) { int v = (data[i * 2 + 8] & 0xFF) * 128 diff --git a/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileReader.java b/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileReader.java index 85f31acb0d5d9..fb24eca661157 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileReader.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -146,34 +146,27 @@ private MidiFileFormat getMidiFileFormatFromStream(InputStream stream, @Override public MidiFileFormat getMidiFileFormat(URL url) throws InvalidMidiDataException, IOException { - InputStream urlStream = url.openStream(); // throws IOException - BufferedInputStream bis = new BufferedInputStream( urlStream, bisBufferSize ); - MidiFileFormat fileFormat = null; - try { - fileFormat = getMidiFileFormat( bis ); // throws InvalidMidiDataException - } finally { - bis.close(); + try (InputStream urlStream = url.openStream(); // throws IOException + BufferedInputStream bis = new BufferedInputStream(urlStream, bisBufferSize)) + { + MidiFileFormat fileFormat = getMidiFileFormat(bis); // throws InvalidMidiDataException + return fileFormat; } - return fileFormat; } @Override public MidiFileFormat getMidiFileFormat(File file) throws InvalidMidiDataException, IOException { - FileInputStream fis = new FileInputStream(file); // throws IOException - BufferedInputStream bis = new BufferedInputStream(fis, bisBufferSize); - - // $$fb 2002-04-17: part of fix for 4635286: MidiSystem.getMidiFileFormat() returns format having invalid length - long length = file.length(); - if (length > Integer.MAX_VALUE) { - length = MidiFileFormat.UNKNOWN_LENGTH; - } - MidiFileFormat fileFormat = null; - try { - fileFormat = getMidiFileFormatFromStream(bis, (int) length, null); - } finally { - bis.close(); + try (FileInputStream fis = new FileInputStream(file); // throws IOException + BufferedInputStream bis = new BufferedInputStream(fis, bisBufferSize)) + { + // $$fb 2002-04-17: part of fix for 4635286: MidiSystem.getMidiFileFormat() returns format having invalid length + long length = file.length(); + if (length > Integer.MAX_VALUE) { + length = MidiFileFormat.UNKNOWN_LENGTH; + } + MidiFileFormat fileFormat = getMidiFileFormatFromStream(bis, (int) length, null); + return fileFormat; } - return fileFormat; } @Override @@ -204,28 +197,22 @@ public Sequence getSequence(InputStream stream) throws InvalidMidiDataException, @Override public Sequence getSequence(URL url) throws InvalidMidiDataException, IOException { - InputStream is = url.openStream(); // throws IOException - is = new BufferedInputStream(is, bisBufferSize); - Sequence seq = null; - try { - seq = getSequence(is); - } finally { - is.close(); + try (InputStream is = url.openStream(); // throws IOException + BufferedInputStream bis = new BufferedInputStream(is, bisBufferSize)) + { + Sequence seq = getSequence(bis); + return seq; } - return seq; } @Override public Sequence getSequence(File file) throws InvalidMidiDataException, IOException { - InputStream is = new FileInputStream(file); // throws IOException - is = new BufferedInputStream(is, bisBufferSize); - Sequence seq = null; - try { - seq = getSequence(is); - } finally { - is.close(); + try (InputStream is = new FileInputStream(file); // throws IOException + BufferedInputStream bis = new BufferedInputStream(is, bisBufferSize)) + { + Sequence seq = getSequence(bis); + return seq; } - return seq; } } diff --git a/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java b/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java index a46a2e4e46783..7d96bfaebfc3d 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java @@ -129,10 +129,10 @@ public int write(Sequence in, int type, OutputStream out) throws IOException { @Override public int write(Sequence in, int type, File out) throws IOException { Objects.requireNonNull(in); - FileOutputStream fos = new FileOutputStream(out); // throws IOException - int bytesWritten = write( in, type, fos ); - fos.close(); - return bytesWritten; + try (FileOutputStream fos = new FileOutputStream(out)) { // throws IOException + int bytesWritten = write(in, type, fos); + return bytesWritten; + } } //================================================================================= diff --git a/src/java.desktop/share/classes/java/awt/Font.java b/src/java.desktop/share/classes/java/awt/Font.java index 78373aa4f377d..502962945af20 100644 --- a/src/java.desktop/share/classes/java/awt/Font.java +++ b/src/java.desktop/share/classes/java/awt/Font.java @@ -1131,7 +1131,7 @@ public OutputStream run() throws IOException { if (tracker != null) { tracker.set(tFile, outStream); } - try { + try (outStream) { /* don't close the input stream */ byte[] buf = new byte[8192]; for (;;) { int bytesRead = fontStream.read(buf); @@ -1152,9 +1152,6 @@ public OutputStream run() throws IOException { } outStream.write(buf, 0, bytesRead); } - /* don't close the input stream */ - } finally { - outStream.close(); } /* After all references to a Font2D are dropped, the file * will be removed. To support long-lived AppContexts, diff --git a/src/java.desktop/share/classes/java/awt/Robot.java b/src/java.desktop/share/classes/java/awt/Robot.java index 692da44c95665..2054954fee3d1 100644 --- a/src/java.desktop/share/classes/java/awt/Robot.java +++ b/src/java.desktop/share/classes/java/awt/Robot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,6 +68,43 @@ *

    * Applications that use Robot for purposes other than self-testing should * handle these error conditions gracefully. + *

    + * Platforms and desktop environments may impose restrictions or limitations + * on the access required to implement all functionality in the Robot class. + * For example: + *

      + *
    • preventing access to the contents of any part of a desktop + * or Window on the desktop that is not owned by the running application.
    • + *
    • treating window decorations as non-owned content.
    • + *
    • ignoring or limiting specific requests to manipulate windows.
    • + *
    • ignoring or limiting specific requests for Robot generated (synthesized) + * events related to keyboard and mouse etc.
    • + *
    • requiring specific or global permissions to any access to window + * contents, even application owned content,or to perform even limited + * synthesizing of events.
    • + *
    + * + * The Robot API specification requires that approvals for these be granted + * for full operation. + * If they are not granted, the API will be degraded as discussed here. + * Relevant specific API methods may document more specific limitations + * and requirements. + * Depending on the policies of the desktop environment, + * the approvals mentioned above may: + *
      + *
    • be required every time
    • + *
    • or persistent for the lifetime of an application,
    • + *
    • or persistent across multiple user desktop sessions
    • + *
    • be fine-grained permissions
    • + *
    • be associated with a specific binary application, + * or a class of binary applications.
    • + *
    + * + * When such approvals need to given interactively, it may impede the normal + * operation of the application until approved, and if approval is denied + * or not possible, or cannot be made persistent then it will degrade + * the functionality of this class and in turn any part of the operation + * of the application which is dependent on it. * * @author Robi Khan * @since 1.3 @@ -189,6 +226,11 @@ private static void checkIsScreenDevice(GraphicsDevice device) { /** * Moves mouse pointer to given screen coordinates. + *

    + * The mouse pointer may not visually move on some platforms, + * while the subsequent mousePress and mouseRelease can be + * delivered to the correct location + * * @param x X position * @param y Y position */ @@ -383,8 +425,22 @@ private static void checkKeycodeArgument(int keycode) { /** * Returns the color of a pixel at the given screen coordinates. + *

    + * If the desktop environment requires that permissions be granted + * to capture screen content, and the required permissions are not granted, + * then a {@code SecurityException} may be thrown, + * or the content of the returned {@code Color} is undefined. + *

    + * @apiNote It is recommended to avoid calling this method on + * the AWT Event Dispatch Thread since screen capture may be a lengthy + * operation, particularly if acquiring permissions is needed and involves + * user interaction. + * * @param x X position of pixel * @param y Y position of pixel + * @throws SecurityException if {@code readDisplayPixels} permission + * is not granted, or access to the screen is denied + * by the desktop environment * @return Color of the pixel */ public synchronized Color getPixelColor(int x, int y) { @@ -395,12 +451,25 @@ public synchronized Color getPixelColor(int x, int y) { } /** - * Creates an image containing pixels read from the screen. This image does - * not include the mouse cursor. + * Creates an image containing pixels read from the screen. + *

    + * If the desktop environment requires that permissions be granted + * to capture screen content, and the required permissions are not granted, + * then a {@code SecurityException} may be thrown, + * or the contents of the returned {@code BufferedImage} are undefined. + *

    + * @apiNote It is recommended to avoid calling this method on + * the AWT Event Dispatch Thread since screen capture may be a lengthy + * operation, particularly if acquiring permissions is needed and involves + * user interaction. + * * @param screenRect Rect to capture in screen coordinates * @return The captured image - * @throws IllegalArgumentException if {@code screenRect} width and height are not greater than zero - * @throws SecurityException if {@code readDisplayPixels} permission is not granted + * @throws IllegalArgumentException if {@code screenRect} width and height + * are not greater than zero + * @throws SecurityException if {@code readDisplayPixels} permission + * is not granted, or access to the screen is denied + * by the desktop environment * @see SecurityManager#checkPermission * @see AWTPermission */ @@ -410,7 +479,6 @@ public synchronized BufferedImage createScreenCapture(Rectangle screenRect) { /** * Creates an image containing pixels read from the screen. - * This image does not include the mouse cursor. * This method can be used in case there is a scaling transform * from user space to screen (device) space. * Typically this means that the display is a high resolution screen, @@ -443,8 +511,11 @@ public synchronized BufferedImage createScreenCapture(Rectangle screenRect) { * } * @param screenRect Rect to capture in screen coordinates * @return The captured image - * @throws IllegalArgumentException if {@code screenRect} width and height are not greater than zero - * @throws SecurityException if {@code readDisplayPixels} permission is not granted + * @throws IllegalArgumentException if {@code screenRect} width and height + * are not greater than zero + * @throws SecurityException if {@code readDisplayPixels} permission + * is not granted, or access to the screen is denied + * by the desktop environment * @see SecurityManager#checkPermission * @see AWTPermission * diff --git a/src/java.desktop/share/classes/java/awt/Toolkit.java b/src/java.desktop/share/classes/java/awt/Toolkit.java index 354b5b3f3f3a3..a028034516e74 100644 --- a/src/java.desktop/share/classes/java/awt/Toolkit.java +++ b/src/java.desktop/share/classes/java/awt/Toolkit.java @@ -413,12 +413,10 @@ public String run() { File propsFile = new File( System.getProperty("user.home") + sep + ".accessibility.properties"); - FileInputStream in = - new FileInputStream(propsFile); - - // Inputstream has been buffered in Properties class - properties.load(in); - in.close(); + try (FileInputStream in = new FileInputStream(propsFile)) { + // Inputstream has been buffered in Properties class + properties.load(in); + } } catch (Exception e) { // Per-user accessibility properties file does not exist } @@ -431,12 +429,10 @@ public String run() { File propsFile = new File( System.getProperty("java.home") + sep + "conf" + sep + "accessibility.properties"); - FileInputStream in = - new FileInputStream(propsFile); - - // Inputstream has been buffered in Properties class - properties.load(in); - in.close(); + try (FileInputStream in = new FileInputStream(propsFile)) { + // Inputstream has been buffered in Properties class + properties.load(in); + } } catch (Exception e) { // System-wide accessibility properties file does // not exist; diff --git a/src/java.desktop/share/classes/java/beans/Beans.java b/src/java.desktop/share/classes/java/beans/Beans.java index 3be687b10e5cb..dbe678b70927e 100644 --- a/src/java.desktop/share/classes/java/beans/Beans.java +++ b/src/java.desktop/share/classes/java/beans/Beans.java @@ -201,7 +201,7 @@ public static Object instantiate(ClassLoader cls, String beanName, else ins = cls.getResourceAsStream(serName); if (ins != null) { - try { + try (ins) { if (cls == null) { oins = new ObjectInputStream(ins); } else { @@ -211,13 +211,9 @@ public static Object instantiate(ClassLoader cls, String beanName, serialized = true; oins.close(); } catch (IOException ex) { - ins.close(); // Drop through and try opening the class. But remember // the exception in case we can't find the class either. serex = ex; - } catch (ClassNotFoundException ex) { - ins.close(); - throw ex; } } diff --git a/src/java.desktop/share/classes/javax/imageio/ImageIO.java b/src/java.desktop/share/classes/javax/imageio/ImageIO.java index 5ac7c293e798c..e44c0a5b53597 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageIO.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageIO.java @@ -39,8 +39,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Set; import javax.imageio.spi.IIORegistry; import javax.imageio.spi.ImageReaderSpi; import javax.imageio.spi.ImageReaderWriterSpi; @@ -1402,7 +1400,7 @@ public static BufferedImage read(URL input) throws IOException { throw new IllegalArgumentException("input == null!"); } - InputStream istream = null; + InputStream istream; try { istream = input.openStream(); } catch (IOException e) { @@ -1418,13 +1416,11 @@ public static BufferedImage read(URL input) throws IOException { throw new IIOException("Can't create an ImageInputStream!"); } BufferedImage bi; - try { + try (istream) { bi = read(stream); if (bi == null) { stream.close(); } - } finally { - istream.close(); } return bi; } @@ -1466,13 +1462,12 @@ public static BufferedImage read(ImageInputStream stream) ImageReadParam param = reader.getDefaultReadParam(); reader.setInput(stream, true, true); BufferedImage bi; - try { + try (stream) { bi = reader.read(0, param); } catch (RuntimeException e) { throw new IIOException(e.toString(), e); } finally { reader.dispose(); - stream.close(); } return bi; } @@ -1554,10 +1549,8 @@ public static boolean write(RenderedImage im, if (stream == null) { throw new IIOException("Can't create an ImageOutputStream!"); } - try { + try (stream) { return doWrite(im, writer, stream); - } finally { - stream.close(); } } @@ -1594,10 +1587,8 @@ public static boolean write(RenderedImage im, if (stream == null) { throw new IIOException("Can't create an ImageOutputStream!"); } - try { + try (stream) { return doWrite(im, getWriter(im, formatName), stream); - } finally { - stream.close(); } } diff --git a/src/java.desktop/share/classes/javax/swing/DefaultListCellRenderer.java b/src/java.desktop/share/classes/javax/swing/DefaultListCellRenderer.java index 64b24c8b9f510..a0750a13bfaa2 100644 --- a/src/java.desktop/share/classes/javax/swing/DefaultListCellRenderer.java +++ b/src/java.desktop/share/classes/javax/swing/DefaultListCellRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,15 +25,15 @@ package javax.swing; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.border.*; - -import java.awt.Component; import java.awt.Color; +import java.awt.Component; import java.awt.Rectangle; - import java.io.Serializable; + +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.synth.SynthListUI; + import sun.swing.DefaultLookup; import sun.swing.SwingUtilities2; @@ -157,7 +157,11 @@ public Component getListCellRendererComponent( setText((value == null) ? "" : value.toString()); } - setEnabled(list.isEnabled()); + if (list.getName() == null || !list.getName().equals("ComboBox.list") + || !(list.getUI() instanceof SynthListUI)) { + setEnabled(list.isEnabled()); + } + setFont(list.getFont()); Border border = null; diff --git a/src/java.desktop/share/classes/javax/swing/JEditorPane.java b/src/java.desktop/share/classes/javax/swing/JEditorPane.java index 6d708f1b8b8d9..cf74014f46fc4 100644 --- a/src/java.desktop/share/classes/javax/swing/JEditorPane.java +++ b/src/java.desktop/share/classes/javax/swing/JEditorPane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; +import java.io.OutputStream; import java.io.Reader; import java.io.Serial; import java.io.StringReader; @@ -618,35 +619,37 @@ void read(InputStream in, Document doc) throws IOException { String charset = (String) getClientProperty("charset"); try(Reader r = (charset != null) ? new InputStreamReader(in, charset) : new InputStreamReader(in)) { - kit.read(r, doc, 0); - } catch (BadLocationException e) { - throw new IOException(e.getMessage()); - } catch (ChangedCharSetException changedCharSetException) { - String charSetSpec = changedCharSetException.getCharSetSpec(); - if (changedCharSetException.keyEqualsCharSet()) { - putClientProperty("charset", charSetSpec); - } else { - setCharsetFromContentTypeParameters(charSetSpec); - } try { - in.reset(); - } catch (IOException exception) { - //mark was invalidated - in.close(); - URL url = (URL)doc.getProperty(Document.StreamDescriptionProperty); - if (url != null) { - URLConnection conn = url.openConnection(); - in = conn.getInputStream(); + kit.read(r, doc, 0); + } catch (BadLocationException e) { + throw new IOException(e.getMessage()); + } catch (ChangedCharSetException changedCharSetException) { + String charSetSpec = changedCharSetException.getCharSetSpec(); + if (changedCharSetException.keyEqualsCharSet()) { + putClientProperty("charset", charSetSpec); } else { - //there is nothing we can do to recover stream - throw changedCharSetException; + setCharsetFromContentTypeParameters(charSetSpec); } + try { + in.reset(); + } catch (IOException exception) { + //mark was invalidated + in.close(); + URL url = (URL)doc.getProperty(Document.StreamDescriptionProperty); + if (url != null) { + URLConnection conn = url.openConnection(); + in = conn.getInputStream(); + } else { + //there is nothing we can do to recover stream + throw changedCharSetException; + } + } + try { + doc.remove(0, doc.getLength()); + } catch (BadLocationException e) {} + doc.putProperty("IgnoreCharsetDirective", Boolean.valueOf(true)); + read(in, doc); } - try { - doc.remove(0, doc.getLength()); - } catch (BadLocationException e) {} - doc.putProperty("IgnoreCharsetDirective", Boolean.valueOf(true)); - read(in, doc); } } @@ -852,16 +855,12 @@ private Object getPostData() { private void handlePostData(HttpURLConnection conn, Object postData) throws IOException { conn.setDoOutput(true); - DataOutputStream os = null; - try { - conn.setRequestProperty("Content-Type", - "application/x-www-form-urlencoded"); - os = new DataOutputStream(conn.getOutputStream()); - os.writeBytes((String) postData); - } finally { - if (os != null) { - os.close(); - } + conn.setRequestProperty("Content-Type", + "application/x-www-form-urlencoded"); + try (OutputStream os = conn.getOutputStream(); + DataOutputStream dos = new DataOutputStream(os)) + { + dos.writeBytes((String)postData); } } diff --git a/src/java.desktop/share/classes/javax/swing/JFormattedTextField.java b/src/java.desktop/share/classes/javax/swing/JFormattedTextField.java index 9ee465e374463..51516f79c99ca 100644 --- a/src/java.desktop/share/classes/javax/swing/JFormattedTextField.java +++ b/src/java.desktop/share/classes/javax/swing/JFormattedTextField.java @@ -868,11 +868,11 @@ private AbstractFormatterFactory getDefaultFormatterFactory(Object type) { return new DefaultFormatterFactory(new DateFormatter()); } if (type instanceof Number) { - AbstractFormatter displayFormatter = new NumberFormatter(); - ((NumberFormatter)displayFormatter).setValueClass(type.getClass()); - AbstractFormatter editFormatter = new NumberFormatter( + NumberFormatter displayFormatter = new NumberFormatter(); + displayFormatter.setValueClass(type.getClass()); + NumberFormatter editFormatter = new NumberFormatter( new DecimalFormat("#.#")); - ((NumberFormatter)editFormatter).setValueClass(type.getClass()); + editFormatter.setValueClass(type.getClass()); return new DefaultFormatterFactory(displayFormatter, displayFormatter,editFormatter); diff --git a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java index e4eb45c28a9dc..115a39893b372 100644 --- a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java +++ b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; +import java.awt.Window; import java.awt.event.FocusEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; @@ -757,6 +758,16 @@ public void pack() { } } + private Window getMenuInvoker() { + if (invoker instanceof Window menuInvoker) { + return menuInvoker; + } else { + return invoker == null + ? null + : SwingUtilities.getWindowAncestor(invoker); + } + } + /** * Sets the visibility of the popup menu. * @@ -798,14 +809,24 @@ public void setVisible(boolean b) { } } - if(b) { + if (b) { firePopupMenuWillBecomeVisible(); + + if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) { + sunToolkit.dismissPopupOnFocusLostIfNeeded(getMenuInvoker()); + } + showPopup(); firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE); - } else if(popup != null) { + } else if (popup != null) { firePopupMenuWillBecomeInvisible(); + + if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) { + sunToolkit.dismissPopupOnFocusLostIfNeededCleanUp(getMenuInvoker()); + } + popup.hide(); popup = null; firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE); diff --git a/src/java.desktop/share/classes/javax/swing/JRootPane.java b/src/java.desktop/share/classes/javax/swing/JRootPane.java index e3eb7ac75a4d3..dcc6d6bb4efae 100644 --- a/src/java.desktop/share/classes/javax/swing/JRootPane.java +++ b/src/java.desktop/share/classes/javax/swing/JRootPane.java @@ -24,18 +24,13 @@ */ package javax.swing; -import java.applet.Applet; import java.awt.*; -import java.awt.event.*; import java.beans.*; import java.security.AccessController; import javax.accessibility.*; import javax.swing.plaf.RootPaneUI; -import java.util.Vector; import java.io.Serializable; -import javax.swing.border.*; -import sun.awt.AWTAccessor; import sun.security.action.GetBooleanAction; @@ -511,10 +506,10 @@ public void addLayoutComponent(Component comp, Object constraints) { * @return the default glassPane */ protected Component createGlassPane() { - JComponent c = new JPanel(); + JPanel c = new JPanel(); c.setName(this.getName()+".glassPane"); c.setVisible(false); - ((JPanel)c).setOpaque(false); + c.setOpaque(false); return c; } diff --git a/src/java.desktop/share/classes/javax/swing/JTabbedPane.java b/src/java.desktop/share/classes/javax/swing/JTabbedPane.java index af5c917ee7698..3406e6ea94557 100644 --- a/src/java.desktop/share/classes/javax/swing/JTabbedPane.java +++ b/src/java.desktop/share/classes/javax/swing/JTabbedPane.java @@ -55,6 +55,7 @@ import javax.accessibility.AccessibleSelection; import javax.accessibility.AccessibleState; import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleValue; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.TabbedPaneUI; @@ -2074,7 +2075,7 @@ public void selectAllAccessibleSelection() { } private class Page extends AccessibleContext - implements Serializable, Accessible, AccessibleComponent { + implements Serializable, Accessible, AccessibleComponent, AccessibleValue { String title; Color background; Color foreground; @@ -2168,7 +2169,6 @@ public AccessibleContext getAccessibleContext() { return this; } - // AccessibleContext methods public String getAccessibleName() { @@ -2202,6 +2202,43 @@ public AccessibleStateSet getAccessibleStateSet() { return states; } + @Override + public AccessibleValue getAccessibleValue() { + return this; + } + + @Override + public Number getCurrentAccessibleValue() { + return (getPageIndex() == parent.getSelectedIndex() ? + Integer.valueOf(1) : Integer.valueOf(0)); + } + + @Override + public boolean setCurrentAccessibleValue(Number n) { + if (getPageIndex() != parent.getSelectedIndex()) { + if (n.intValue() != 0) { + // Set current page selected + parent.setSelectedIndex(getPageIndex()); + } + } else { + if (n.intValue() == 0) { + // Can not "deselect" because what page should i select instead? + return false; + } + } + return true; + } + + @Override + public Number getMinimumAccessibleValue() { + return Integer.valueOf(0); + } + + @Override + public Number getMaximumAccessibleValue() { + return Integer.valueOf(1); + } + public int getAccessibleIndexInParent() { return getPageIndex(); } @@ -2368,7 +2405,7 @@ public void removeFocusListener(FocusListener l) { * one exists and the page is disabled. Otherwise, null * is returned. */ - public AccessibleIcon [] getAccessibleIcon() { + public AccessibleIcon[] getAccessibleIcon() { AccessibleIcon accessibleIcon = null; if (enabled && icon instanceof ImageIcon) { AccessibleContext ac = diff --git a/src/java.desktop/share/classes/javax/swing/JTree.java b/src/java.desktop/share/classes/javax/swing/JTree.java index f1ba5b9d3d31b..e48c1b2933372 100644 --- a/src/java.desktop/share/classes/javax/swing/JTree.java +++ b/src/java.desktop/share/classes/javax/swing/JTree.java @@ -2033,7 +2033,7 @@ public Enumeration getExpandedDescendants(TreePath parent) { Enumeration toggledPaths = expandedState.keys(); Vector elements = null; TreePath path; - Object value; + Boolean value; if(toggledPaths != null) { while(toggledPaths.hasMoreElements()) { @@ -2042,8 +2042,7 @@ public Enumeration getExpandedDescendants(TreePath parent) { // Add the path if it is expanded, a descendant of parent, // and it is visible (all parents expanded). This is rather // expensive! - if(path != parent && value != null && - ((Boolean)value).booleanValue() && + if (path != parent && value != null && value && parent.isDescendant(path) && isVisible(path)) { if (elements == null) { elements = new Vector(); @@ -2081,11 +2080,11 @@ public boolean isExpanded(TreePath path) { if(path == null) return false; - Object value; + Boolean value; do{ value = expandedState.get(path); - if(value == null || !((Boolean)value).booleanValue()) + if (value == null || !value) return false; } while( (path=path.getParentPath())!=null ); @@ -2109,7 +2108,7 @@ public boolean isExpanded(int row) { if(path != null) { Boolean value = expandedState.get(path); - return (value != null && value.booleanValue()); + return (value != null && value); } } return false; @@ -3729,9 +3728,9 @@ protected void setExpandedState(TreePath path, boolean state) { } if(!state) { // collapse last path. - Object cValue = expandedState.get(path); + Boolean cValue = expandedState.get(path); - if(cValue != null && ((Boolean)cValue).booleanValue()) { + if (cValue != null && cValue) { try { fireTreeWillCollapse(path); } @@ -3753,9 +3752,9 @@ protected void setExpandedState(TreePath path, boolean state) { } else { // Expand last path. - Object cValue = expandedState.get(path); + Boolean cValue = expandedState.get(path); - if(cValue == null || !((Boolean)cValue).booleanValue()) { + if (cValue == null || !cValue) { try { fireTreeWillExpand(path); } diff --git a/src/java.desktop/share/classes/javax/swing/UIManager.java b/src/java.desktop/share/classes/javax/swing/UIManager.java index f4645aa7b2904..0648ccc312b02 100644 --- a/src/java.desktop/share/classes/javax/swing/UIManager.java +++ b/src/java.desktop/share/classes/javax/swing/UIManager.java @@ -57,7 +57,6 @@ import sun.awt.OSInfo; import sun.security.action.GetPropertyAction; import sun.swing.SwingUtilities2; -import java.lang.reflect.Method; import java.util.HashMap; import java.util.Objects; import sun.awt.AppContext; @@ -1301,9 +1300,9 @@ public Object run() { if (file.exists()) { // InputStream has been buffered in Properties // class - FileInputStream ins = new FileInputStream(file); - props.load(ins); - ins.close(); + try (FileInputStream ins = new FileInputStream(file)) { + props.load(ins); + } } } catch (Exception e) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java index 050f27e72b4d4..197f3238bb133 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboPopup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -921,7 +921,7 @@ public void mousePressed(MouseEvent e) { if (e.getSource() == list) { return; } - if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled()) + if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled() || !comboBox.isShowing()) return; if ( comboBox.isEditable() ) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java index 34c53ca915fcc..16cbaad53a754 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -98,10 +98,13 @@ public void propertyChange(PropertyChangeEvent e) { * This method is used to interrupt file loading thread. */ public void invalidateFileCache() { - if (filesLoader != null) { - filesLoader.loadThread.interrupt(); - filesLoader.cancelRunnables(); - filesLoader = null; + synchronized (this) { + if (filesLoader != null) { + filesLoader.loadThread.interrupt(); + filesLoader = null; + // Increment fetch ID to invalidate pending DoChangeContents + fetchID.incrementAndGet(); + } } } @@ -156,14 +159,15 @@ public void validateFileCache() { if (currentDirectory == null) { return; } - if (filesLoader != null) { - filesLoader.loadThread.interrupt(); - filesLoader.cancelRunnables(); - } + synchronized (this) { + if (filesLoader != null) { + filesLoader.loadThread.interrupt(); + } - int fid = fetchID.incrementAndGet(); - setBusy(true, fid); - filesLoader = new FilesLoader(currentDirectory, fid); + int fid = fetchID.incrementAndGet(); + setBusy(true, fid); + filesLoader = new FilesLoader(currentDirectory, fid); + } } /** @@ -276,7 +280,6 @@ private final class FilesLoader implements Runnable { private final boolean fileSelectionEnabled; private final int fid; private final File currentDirectory; - private volatile DoChangeContents runnable; private final Thread loadThread; private FilesLoader(File currentDirectory, int fid) { @@ -297,22 +300,20 @@ public void run() { } private void run0() { - FileSystemView fileSystem = fileSystemView; - if (loadThread.isInterrupted()) { return; } - File[] list = fileSystem.getFiles(currentDirectory, useFileHiding); + File[] list = fileSystemView.getFiles(currentDirectory, useFileHiding); if (loadThread.isInterrupted()) { return; } final Vector newFileCache = new Vector(); - Vector newFiles = new Vector(); + final Vector newFiles = new Vector(); - // run through the file list, add directories and selectable files to fileCache + // Run through the file list, add directories and selectable files to fileCache // Note that this block must be OUTSIDE of Invoker thread because of // deadlock possibility with custom synchronized FileSystemView for (File file : list) { @@ -339,60 +340,68 @@ private void run0() { // To avoid loads of synchronizations with Invoker and improve performance we // execute the whole block on the COM thread - runnable = ShellFolder.invoke(new Callable() { + DoChangeContents runnable = ShellFolder.invoke(new Callable() { public DoChangeContents call() { - int newSize = newFileCache.size(); - int oldSize = fileCache.size(); - - if (newSize > oldSize) { - //see if interval is added - int start = oldSize; - int end = newSize; - for (int i = 0; i < oldSize; i++) { - if (!newFileCache.get(i).equals(fileCache.get(i))) { - start = i; - for (int j = i; j < newSize; j++) { - if (newFileCache.get(j).equals(fileCache.get(i))) { - end = j; - break; + synchronized (fileCache) { + int newSize = newFileCache.size(); + int oldSize = fileCache.size(); + + if (newSize > oldSize) { + //see if interval is added + int start = oldSize; + int end = newSize; + for (int i = 0; i < oldSize; i++) { + if (!newFileCache.get(i).equals(fileCache.get(i))) { + start = i; + for (int j = i; j < newSize; j++) { + if (newFileCache.get(j).equals(fileCache.get(i))) { + end = j; + break; + } } + break; } - break; } - } - if (start >= 0 && end > start - && newFileCache.subList(end, newSize).equals(fileCache.subList(start, oldSize))) { - if (loadThread.isInterrupted()) { - return null; + + if (start >= 0 && end > start + && newFileCache.subList(end, newSize) + .equals(fileCache.subList(start, oldSize))) { + if (loadThread.isInterrupted()) { + return null; + } + return new DoChangeContents(newFileCache.subList(start, end), + start, null, 0, fid); } - return new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid); - } - } else if (newSize < oldSize) { - //see if interval is removed - int start = -1; - int end = -1; - for (int i = 0; i < newSize; i++) { - if (!newFileCache.get(i).equals(fileCache.get(i))) { - start = i; - end = i + oldSize - newSize; - break; + } else if (newSize < oldSize) { + //see if interval is removed + int start = -1; + int end = -1; + for (int i = 0; i < newSize; i++) { + if (!newFileCache.get(i).equals(fileCache.get(i))) { + start = i; + end = i + oldSize - newSize; + break; + } + } + + if (start >= 0 && end > start + && fileCache.subList(end, oldSize) + .equals(newFileCache.subList(start, newSize))) { + if (loadThread.isInterrupted()) { + return null; + } + return new DoChangeContents(null, 0, + new Vector<>(fileCache.subList(start, end)), start, fid); } } - if (start >= 0 && end > start - && fileCache.subList(end, oldSize).equals(newFileCache.subList(start, newSize))) { + if (!fileCache.equals(newFileCache)) { if (loadThread.isInterrupted()) { return null; } - return new DoChangeContents(null, 0, new Vector<>(fileCache.subList(start, end)), start, fid); + return new DoChangeContents(newFileCache, 0, fileCache, 0, fid); } + return null; } - if (!fileCache.equals(newFileCache)) { - if (loadThread.isInterrupted()) { - cancelRunnables(); - } - return new DoChangeContents(newFileCache, 0, fileCache, 0, fid); - } - return null; } }); @@ -400,12 +409,6 @@ public DoChangeContents call() { SwingUtilities.invokeLater(runnable); } } - - private void cancelRunnables() { - if (runnable != null) { - runnable.cancel(); - } - } } @@ -514,13 +517,13 @@ public void run() { private final class DoChangeContents implements Runnable { private final List addFiles; private final List remFiles; - private boolean doFire = true; private final int fid; - private int addStart = 0; - private int remStart = 0; + private final int addStart; + private final int remStart; - DoChangeContents(List addFiles, int addStart, List remFiles, - int remStart, int fid) { + private DoChangeContents(List addFiles, int addStart, + List remFiles, int remStart, + int fid) { this.addFiles = addFiles; this.addStart = addStart; this.remFiles = remFiles; @@ -528,31 +531,32 @@ private final class DoChangeContents implements Runnable { this.fid = fid; } - synchronized void cancel() { - doFire = false; - } + @Override + public void run() { + if (fetchID.get() != fid) { + return; + } - public synchronized void run() { - if (fetchID.get() == fid && doFire) { - int remSize = (remFiles == null) ? 0 : remFiles.size(); - int addSize = (addFiles == null) ? 0 : addFiles.size(); - synchronized(fileCache) { - if (remSize > 0) { - fileCache.removeAll(remFiles); - } - if (addSize > 0) { - fileCache.addAll(addStart, addFiles); - } - files = null; - directories = null; + final int remSize = (remFiles == null) ? 0 : remFiles.size(); + final int addSize = (addFiles == null) ? 0 : addFiles.size(); + final int cacheSize; + synchronized (fileCache) { + if (remSize > 0) { + fileCache.removeAll(remFiles); } - if (remSize > 0 && addSize == 0) { - fireIntervalRemoved(BasicDirectoryModel.this, remStart, remStart + remSize - 1); - } else if (addSize > 0 && remSize == 0 && addStart + addSize <= fileCache.size()) { - fireIntervalAdded(BasicDirectoryModel.this, addStart, addStart + addSize - 1); - } else { - fireContentsChanged(); + if (addSize > 0) { + fileCache.addAll(addStart, addFiles); } + files = null; + directories = null; + cacheSize = fileCache.size(); + } + if (remSize > 0 && addSize == 0) { + fireIntervalRemoved(BasicDirectoryModel.this, remStart, remStart + remSize - 1); + } else if (addSize > 0 && remSize == 0 && addStart + addSize <= cacheSize) { + fireIntervalAdded(BasicDirectoryModel.this, addStart, addStart + addSize - 1); + } else { + fireContentsChanged(); } } } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java index 452707fd126c0..a4b389d4b6a6a 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ import javax.swing.ComboBoxEditor; import javax.swing.DefaultButtonModel; +import javax.swing.DefaultListCellRenderer; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComboBox; @@ -108,6 +109,8 @@ public class SynthComboBoxUI extends BasicComboBoxUI implements */ private EditorFocusHandler editorFocusHandler; + private DlcrEnabledHandler dlcrEnabledHandler; + /** * If true, then the cell renderer will be forced to be non-opaque when * used for rendering the selected item in the combo box (not in the list), @@ -187,6 +190,7 @@ protected void installListeners() { comboBox.addPropertyChangeListener(this); comboBox.addMouseListener(buttonHandler); editorFocusHandler = new EditorFocusHandler(comboBox); + dlcrEnabledHandler = new DlcrEnabledHandler(comboBox); super.installListeners(); } @@ -219,6 +223,7 @@ protected void uninstallDefaults() { @Override protected void uninstallListeners() { editorFocusHandler.unregister(); + dlcrEnabledHandler.unregister(); comboBox.removePropertyChangeListener(this); comboBox.removeMouseListener(buttonHandler); buttonHandler.pressed = false; @@ -794,4 +799,36 @@ public void propertyChange(PropertyChangeEvent evt) { } } } + + /** + * Handler for updating combobox enabled status when renderer enabled + * status changes + */ + private static class DlcrEnabledHandler implements PropertyChangeListener { + private JComboBox comboBox; + + private DlcrEnabledHandler(JComboBox comboBox) { + this.comboBox = comboBox; + comboBox.addPropertyChangeListener("enabled",this); + } + + public void unregister() { + comboBox.removePropertyChangeListener("enabled", this); + } + + /** + * Called when the combos enabled status changes + * + * @param evt A PropertyChangeEvent object describing the event source + * and the property that has changed. + */ + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("enabled")) { + if (comboBox.getRenderer() instanceof DefaultListCellRenderer) { + ((DefaultListCellRenderer) comboBox.getRenderer()) + .setEnabled((boolean) evt.getNewValue()); + } + } + } + } } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthParser.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthParser.java index e053fe3d54cfc..4c212bc1ad811 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthParser.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1024,10 +1024,7 @@ private void addPainterOrMerge(List painters, Stri painter, direction); - for (Object infoObject: painters) { - ParsedSynthStyle.PainterInfo info; - info = (ParsedSynthStyle.PainterInfo) infoObject; - + for (ParsedSynthStyle.PainterInfo info: painters) { if (painterInfo.equalsPainter(info)) { info.addPainter(painter); return; diff --git a/src/java.desktop/share/classes/javax/swing/text/DefaultCaret.java b/src/java.desktop/share/classes/javax/swing/text/DefaultCaret.java index a980268bd941d..91cf3adb8a51b 100644 --- a/src/java.desktop/share/classes/javax/swing/text/DefaultCaret.java +++ b/src/java.desktop/share/classes/javax/swing/text/DefaultCaret.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -366,6 +366,8 @@ protected void moveCaret(MouseEvent e) { } } + private int savedBlinkRate = 0; + private boolean isBlinkRateSaved = false; // --- FocusListener methods -------------------------- /** @@ -379,8 +381,21 @@ protected void moveCaret(MouseEvent e) { public void focusGained(FocusEvent e) { if (component.isEnabled()) { if (component.isEditable()) { - setVisible(true); + if (isBlinkRateSaved) { + setBlinkRate(savedBlinkRate); + savedBlinkRate = 0; + isBlinkRateSaved = false; + } + } else { + if (getBlinkRate() != 0) { + if (!isBlinkRateSaved) { + savedBlinkRate = getBlinkRate(); + isBlinkRateSaved = true; + } + setBlinkRate(0); + } } + setVisible(true); setSelectionVisible(true); updateSystemSelection(); } @@ -1031,17 +1046,29 @@ public void setVisible(boolean e) { * @see Caret#setBlinkRate */ public void setBlinkRate(int rate) { + if (rate < 0) { + throw new IllegalArgumentException("Invalid blink rate: " + rate); + } if (rate != 0) { - if (flasher == null) { - flasher = new Timer(rate, handler); + if (component != null && component.isEditable()) { + if (flasher == null) { + flasher = new Timer(rate, handler); + } + flasher.setDelay(rate); + } else { + savedBlinkRate = rate; + isBlinkRateSaved = true; } - flasher.setDelay(rate); } else { if (flasher != null) { flasher.stop(); flasher.removeActionListener(handler); flasher = null; } + if ((component == null || component.isEditable()) && isBlinkRateSaved) { + savedBlinkRate = 0; + isBlinkRateSaved = false; + } } } @@ -1053,6 +1080,9 @@ public void setBlinkRate(int rate) { * @see Caret#getBlinkRate */ public int getBlinkRate() { + if (isBlinkRateSaved) { + return savedBlinkRate; + } return (flasher == null) ? 0 : flasher.getDelay(); } diff --git a/src/java.desktop/share/classes/javax/swing/text/html/AccessibleHTML.java b/src/java.desktop/share/classes/javax/swing/text/html/AccessibleHTML.java index cc7f3d9c29093..db3fe75136e5c 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/AccessibleHTML.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/AccessibleHTML.java @@ -317,14 +317,12 @@ public AccessibleContext getAccessibleContext() { */ public AccessibleStateSet getAccessibleStateSet() { AccessibleStateSet states = new AccessibleStateSet(); - Component comp = getTextComponent(); + JTextComponent comp = getTextComponent(); if (comp.isEnabled()) { states.add(AccessibleState.ENABLED); } - if (comp instanceof JTextComponent && - ((JTextComponent)comp).isEditable()) { - + if (comp.isEditable()) { states.add(AccessibleState.EDITABLE); states.add(AccessibleState.FOCUSABLE); } @@ -742,11 +740,9 @@ private ElementInfo getElementInfoAt(ElementInfo elementInfo, Point p) { * @see AccessibleStateSet */ public boolean isFocusTraversable() { - Component comp = getTextComponent(); - if (comp instanceof JTextComponent) { - if (((JTextComponent)comp).isEditable()) { - return true; - } + JTextComponent comp = getTextComponent(); + if (comp != null && comp.isEditable()) { + return true; } return false; } @@ -763,8 +759,8 @@ public void requestFocus() { return; } - Component comp = getTextComponent(); - if (comp instanceof JTextComponent) { + JTextComponent comp = getTextComponent(); + if (comp != null) { comp.requestFocusInWindow(); @@ -772,7 +768,7 @@ public void requestFocus() { if (elementInfo.validateIfNecessary()) { // set the caret position to the start of this component Element elem = elementInfo.getElement(); - ((JTextComponent)comp).setCaretPosition(elem.getStartOffset()); + comp.setCaretPosition(elem.getStartOffset()); // fire a AccessibleState.FOCUSED property change event AccessibleContext ac = editor.getAccessibleContext(); diff --git a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index c14b5a126e4f5..58eb7c9c8eb95 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -841,6 +841,22 @@ Object getInternalCSSValue(CSS.Attribute key, String value) { return r != null ? r : conv.parseCssValue(key.getDefaultValue()); } + static Object mergeTextDecoration(String value) { + if (value.startsWith("none")) { + return null; + } + + boolean underline = value.contains("underline"); + boolean strikeThrough = value.contains("line-through"); + if (!underline && !strikeThrough) { + return null; + } + String newValue = underline && strikeThrough + ? "underline,line-through" + : (underline ? "underline" : "line-through"); + return new StringValue().parseCssValue(newValue); + } + /** * Maps from a StyleConstants to a CSS Attribute. */ diff --git a/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java b/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java index a100581567fba..c5ae88a94937f 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/HTMLDocument.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,15 +25,43 @@ package javax.swing.text.html; import java.awt.font.TextAttribute; -import java.util.*; -import java.net.URL; +import java.io.IOException; +import java.io.StringReader; import java.net.MalformedURLException; -import java.io.*; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.text.*; -import javax.swing.undo.*; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Stack; +import java.util.Vector; + +import javax.swing.ButtonGroup; +import javax.swing.DefaultButtonModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.DefaultListModel; +import javax.swing.JToggleButton; +import javax.swing.ListSelectionModel; +import javax.swing.event.DocumentEvent; +import javax.swing.event.EventListenerList; +import javax.swing.event.UndoableEditEvent; +import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultEditorKit; +import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.ElementIterator; +import javax.swing.text.GapContent; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.PlainDocument; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.undo.UndoableEdit; + import sun.swing.SwingUtilities2; + import static sun.swing.SwingUtilities2.IMPLIED_CR; /** @@ -2473,9 +2501,9 @@ public HTMLReader(int offset, int popDepth, int pushDepth, tagMap.put(HTML.Tag.SCRIPT, ha); tagMap.put(HTML.Tag.SELECT, fa); tagMap.put(HTML.Tag.SMALL, ca); - tagMap.put(HTML.Tag.SPAN, ca); + tagMap.put(HTML.Tag.SPAN, new ConvertSpanAction()); tagMap.put(HTML.Tag.STRIKE, conv); - tagMap.put(HTML.Tag.S, ca); + tagMap.put(HTML.Tag.S, conv); tagMap.put(HTML.Tag.STRONG, ca); tagMap.put(HTML.Tag.STYLE, new StyleAction()); tagMap.put(HTML.Tag.SUB, conv); @@ -3398,11 +3426,43 @@ public void start(HTML.Tag t, MutableAttributeSet attr) { if (styleAttributes != null) { charAttr.addAttributes(styleAttributes); } + + convertAttributes(t, attr); } public void end(HTML.Tag t) { popCharacterStyle(); } + + /** + * Converts HTML tags to CSS attributes. + * @param t the current HTML tag + * @param attr the attributes of the HTML tag + */ + void convertAttributes(HTML.Tag t, MutableAttributeSet attr) { + } + } + + final class ConvertSpanAction extends CharacterAction { + @Override + void convertAttributes(HTML.Tag t, MutableAttributeSet attr) { + Object newDecoration = attr.getAttribute(CSS.Attribute.TEXT_DECORATION); + Object previousDecoration = + charAttrStack.peek() + .getAttribute(CSS.Attribute.TEXT_DECORATION); + + if (newDecoration != null + && !"none".equals(newDecoration.toString()) + && previousDecoration != null + && !"none".equals(previousDecoration.toString())) { + StyleSheet sheet = getStyleSheet(); + sheet.addCSSAttribute(charAttr, + CSS.Attribute.TEXT_DECORATION, + CSS.mergeTextDecoration(newDecoration + "," + + previousDecoration) + .toString()); + } + } } /** @@ -3410,35 +3470,9 @@ public void end(HTML.Tag t) { * mappings that have a corresponding StyleConstants * and CSS mapping. The conversion is to CSS attributes. */ - class ConvertAction extends TagAction { - - public void start(HTML.Tag t, MutableAttributeSet attr) { - pushCharacterStyle(); - if (!foundInsertTag) { - // Note that the third argument should really be based off - // inParagraph and impliedP. If we're wrong (that is - // insertTagDepthDelta shouldn't be changed), we'll end up - // removing an extra EndSpec, which won't matter anyway. - boolean insert = canInsertTag(t, attr, false); - if (foundInsertTag) { - if (!inParagraph) { - inParagraph = impliedP = true; - } - } - if (!insert) { - return; - } - } - if (attr.isDefined(IMPLIED)) { - attr.removeAttribute(IMPLIED); - } - if (styleAttributes != null) { - charAttr.addAttributes(styleAttributes); - } - // We also need to add attr, otherwise we lose custom - // attributes, including class/id for style lookups, and - // further confuse style lookup (doesn't have tag). - charAttr.addAttribute(t, attr.copyAttributes()); + final class ConvertAction extends CharacterAction { + @Override + void convertAttributes(HTML.Tag t, MutableAttributeSet attr) { StyleSheet sheet = getStyleSheet(); if (t == HTML.Tag.B) { sheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_WEIGHT, "bold"); @@ -3449,7 +3483,7 @@ public void start(HTML.Tag t, MutableAttributeSet attr) { String value = "underline"; value = (v != null) ? value + "," + v.toString() : value; sheet.addCSSAttribute(charAttr, CSS.Attribute.TEXT_DECORATION, value); - } else if (t == HTML.Tag.STRIKE) { + } else if (t == HTML.Tag.STRIKE || t == HTML.Tag.S) { Object v = charAttr.getAttribute(CSS.Attribute.TEXT_DECORATION); String value = "line-through"; value = (v != null) ? value + "," + v.toString() : value; @@ -3479,11 +3513,6 @@ public void start(HTML.Tag t, MutableAttributeSet attr) { } } } - - public void end(HTML.Tag t) { - popCharacterStyle(); - } - } class AnchorAction extends CharacterAction { @@ -4196,7 +4225,7 @@ private void foundInsertTag(boolean isBlockTag) { try { if (offset == 0 || !getText(offset - 1, 1).equals("\n")) { // Need to insert a newline. - AttributeSet newAttrs = null; + SimpleAttributeSet newAttrs = null; boolean joinP = true; if (offset != 0) { @@ -4230,9 +4259,8 @@ private void foundInsertTag(boolean isBlockTag) { // sure and set the name (otherwise it will be // inherited). newAttrs = new SimpleAttributeSet(); - ((SimpleAttributeSet)newAttrs).addAttribute - (StyleConstants.NameAttribute, - HTML.Tag.CONTENT); + newAttrs.addAttribute(StyleConstants.NameAttribute, + HTML.Tag.CONTENT); } ElementSpec es = new ElementSpec(newAttrs, ElementSpec.ContentType, NEWLINE, 0, diff --git a/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java b/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java index 4ceb656a0a3bb..fc1ffba202329 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java @@ -22,27 +22,82 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package javax.swing.text.html; -import sun.awt.AppContext; +package javax.swing.text.html; -import java.awt.*; -import java.awt.event.*; -import java.io.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.io.StringReader; +import java.io.Writer; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URL; -import javax.swing.text.*; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.TextUI; -import java.util.*; -import javax.accessibility.*; -import java.lang.ref.*; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Enumeration; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.swing.Action; +import javax.swing.JEditorPane; +import javax.swing.JViewport; +import javax.swing.SizeRequirements; +import javax.swing.SwingUtilities; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.event.HyperlinkEvent; +import javax.swing.plaf.TextUI; +import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.BoxView; +import javax.swing.text.ComponentView; +import javax.swing.text.DefaultHighlighter; +import javax.swing.text.Document; +import javax.swing.text.EditorKit; +import javax.swing.text.Element; +import javax.swing.text.ElementIterator; +import javax.swing.text.Highlighter; +import javax.swing.text.IconView; +import javax.swing.text.JTextComponent; +import javax.swing.text.LabelView; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.Position; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledDocument; +import javax.swing.text.StyledEditorKit; +import javax.swing.text.TextAction; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; import javax.swing.text.html.parser.ParserDelegator; + +import sun.awt.AppContext; import sun.swing.SwingAccessor; +import static java.nio.charset.StandardCharsets.ISO_8859_1; + /** * The Swing JEditorPane text component supports different kinds * of content via a plug-in mechanism called an EditorKit. Because @@ -401,12 +456,11 @@ public StyleSheet getStyleSheet() { if (defaultStyles == null) { defaultStyles = new StyleSheet(); appContext.put(DEFAULT_STYLES_KEY, defaultStyles); - try { - InputStream is = HTMLEditorKit.getResourceAsStream(DEFAULT_CSS); - Reader r = new BufferedReader( - new InputStreamReader(is, "ISO-8859-1")); + try (InputStream is = HTMLEditorKit.getResourceAsStream(DEFAULT_CSS); + InputStreamReader isr = new InputStreamReader(is, ISO_8859_1); + Reader r = new BufferedReader(isr)) + { defaultStyles.loadRules(r, null); - r.close(); } catch (Throwable e) { // on error we simply have no styles... the html // will look mighty wrong but still function. @@ -793,13 +847,12 @@ private String getMapHREF(JEditorPane html, HTMLDocument hdoc, Rectangle bounds; TextUI ui = html.getUI(); try { - Shape lBounds = ui.modelToView(html, offset, + Rectangle lBounds = ui.modelToView(html, offset, Position.Bias.Forward); - Shape rBounds = ui.modelToView(html, offset + 1, + Rectangle rBounds = ui.modelToView(html, offset + 1, Position.Bias.Backward); - bounds = lBounds.getBounds(); - bounds.add((rBounds instanceof Rectangle) ? - (Rectangle)rBounds : rBounds.getBounds()); + bounds = lBounds; + bounds.add(rBounds); } catch (BadLocationException ble) { bounds = null; } @@ -830,18 +883,14 @@ private boolean doesElementContainLocation(JEditorPane editor, if (e != null && offset > 0 && e.getStartOffset() == offset) { try { TextUI ui = editor.getUI(); - Shape s1 = ui.modelToView(editor, offset, - Position.Bias.Forward); - if (s1 == null) { + Rectangle r1 = ui.modelToView(editor, offset, + Position.Bias.Forward); + if (r1 == null) { return false; } - Rectangle r1 = (s1 instanceof Rectangle) ? (Rectangle)s1 : - s1.getBounds(); - Shape s2 = ui.modelToView(editor, e.getEndOffset(), - Position.Bias.Backward); - if (s2 != null) { - Rectangle r2 = (s2 instanceof Rectangle) ? (Rectangle)s2 : - s2.getBounds(); + Rectangle r2 = ui.modelToView(editor, e.getEndOffset(), + Position.Bias.Backward); + if (r2 != null) { r1.add(r2); } return r1.contains(x, y); @@ -1466,9 +1515,9 @@ public void setParent(View parent) { //if parent == null unregister component listener if (parent == null) { if (cachedViewPort != null) { - Object cachedObject; + JViewport cachedObject; if ((cachedObject = cachedViewPort.get()) != null) { - ((JComponent)cachedObject).removeComponentListener(this); + cachedObject.removeComponentListener(this); } cachedViewPort = null; } diff --git a/src/java.desktop/share/classes/javax/swing/text/html/MuxingAttributeSet.java b/src/java.desktop/share/classes/javax/swing/text/html/MuxingAttributeSet.java index 9d423dcded932..4d6ae0e0ae14d 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/MuxingAttributeSet.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/MuxingAttributeSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,9 +24,16 @@ */ package javax.swing.text.html; -import javax.swing.text.*; import java.io.Serializable; -import java.util.*; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.swing.text.AttributeSet; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; /** * An implementation of AttributeSet that can multiplex @@ -196,15 +203,24 @@ public AttributeSet copyAttributes() { * @see AttributeSet#getAttribute */ public Object getAttribute(Object key) { - AttributeSet[] as = getAttributes(); - int n = as.length; - for (int i = 0; i < n; i++) { - Object o = as[i].getAttribute(key); - if (o != null) { - return o; + final AttributeSet[] as = getAttributes(); + final int n = as.length; + if (key != CSS.Attribute.TEXT_DECORATION) { + for (int i = 0; i < n; i++) { + Object o = as[i].getAttribute(key); + if (o != null) { + return o; + } } + return null; } - return null; + + String values = Arrays.stream(as) + .map(a -> a.getAttribute(key)) + .filter(Objects::nonNull) + .map(Object::toString) + .collect(Collectors.joining(",")); + return CSS.mergeTextDecoration(values); } /** diff --git a/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java b/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java index 958b3a899a0cc..34e326261a8c1 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,17 +24,53 @@ */ package javax.swing.text.html; -import sun.swing.SwingUtilities2; -import java.util.*; -import java.awt.*; -import java.io.*; -import java.net.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.io.StringReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.EmptyStackException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Stack; +import java.util.StringTokenizer; +import java.util.Vector; + import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.UIManager; -import javax.swing.border.*; +import javax.swing.border.BevelBorder; +import javax.swing.border.Border; import javax.swing.event.ChangeListener; -import javax.swing.text.*; +import javax.swing.text.AttributeSet; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.Style; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; +import javax.swing.text.StyledDocument; +import javax.swing.text.View; + +import sun.swing.SwingUtilities2; /** * Support for defining the visual characteristics of @@ -478,15 +514,12 @@ public StyleSheet[] getStyleSheets() { * @since 1.3 */ public void importStyleSheet(URL url) { - try { - InputStream is; - - is = url.openStream(); - Reader r = new BufferedReader(new InputStreamReader(is)); + try (InputStream is = url.openStream(); + InputStreamReader isr = new InputStreamReader(is); + Reader r = new BufferedReader(isr)) + { CssParser parser = new CssParser(); parser.parse(url, r, false, true); - r.close(); - is.close(); } catch (Throwable e) { // on error we simply have no styles... the html // will look mighty wrong but still function. @@ -2822,18 +2855,40 @@ public Object getAttribute(Object key) { return doGetAttribute(key); } + /** + * Merges the current value of the 'text-decoration' property + * with the value from parent. + */ + private Object getTextDecoration(Object value) { + AttributeSet parent = getResolveParent(); + if (parent == null) { + return value; + } + + Object parentValue = parent.getAttribute(CSS.Attribute.TEXT_DECORATION); + return parentValue == null + ? value + : CSS.mergeTextDecoration(value + "," + parentValue); + } + Object doGetAttribute(Object key) { - if (key == CSS.Attribute.FONT_SIZE && !isDefined(key)) { + Object retValue = super.getAttribute(key); + if (retValue != null) { + if (key != CSS.Attribute.TEXT_DECORATION) { + return retValue; + } else { + // Merge current value with parent + return getTextDecoration(retValue); + } + } + + if (key == CSS.Attribute.FONT_SIZE) { // CSS.FontSize represents a specified value and we need // to inherit a computed value so don't resolve percentage // value from parent. return fontSizeInherit(); } - Object retValue = super.getAttribute(key); - if (retValue != null) { - return retValue; - } // didn't find it... try parent if it's a css attribute // that is inherited. if (key instanceof CSS.Attribute) { diff --git a/src/java.desktop/share/classes/javax/swing/text/html/default.css b/src/java.desktop/share/classes/javax/swing/text/html/default.css index eb37a12b6d81b..32f54231f465e 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/default.css +++ b/src/java.desktop/share/classes/javax/swing/text/html/default.css @@ -102,7 +102,7 @@ dd {margin-left-ltr: 40; margin-bottom: 0} dd p {margin-left: 0; - margin-rigth: 0; + margin-right: 0; margin-top: 0; margin-bottom: 0} diff --git a/src/java.desktop/share/classes/javax/swing/text/rtf/RTFGenerator.java b/src/java.desktop/share/classes/javax/swing/text/rtf/RTFGenerator.java index 7a98fd80ef1b5..f60f4ade3e82e 100644 --- a/src/java.desktop/share/classes/javax/swing/text/rtf/RTFGenerator.java +++ b/src/java.desktop/share/classes/javax/swing/text/rtf/RTFGenerator.java @@ -27,7 +27,6 @@ import java.lang.*; import java.util.*; import java.awt.Color; -import java.awt.Font; import java.io.OutputStream; import java.io.IOException; @@ -516,13 +515,13 @@ void updateSectionAttributes(MutableAttributeSet current, { if (emitStyleChanges) { Object oldStyle = current.getAttribute("sectionStyle"); - Object newStyle = findStyleNumber(newAttributes, Constants.STSection); + Integer newStyle = findStyleNumber(newAttributes, Constants.STSection); if (oldStyle != newStyle) { if (oldStyle != null) { resetSectionAttributes(current); } if (newStyle != null) { - writeControlWord("ds", ((Integer)newStyle).intValue()); + writeControlWord("ds", newStyle); current.addAttribute("sectionStyle", newStyle); } else { current.removeAttribute("sectionStyle"); @@ -555,8 +554,8 @@ void updateParagraphAttributes(MutableAttributeSet current, boolean emitStyleChanges) throws IOException { - Object parm; - Object oldStyle, newStyle; + Object oldStyle; + Integer newStyle; /* The only way to get rid of tabs or styles is with the \pard keyword, emitted by resetParagraphAttributes(). Ideally we should avoid @@ -588,7 +587,7 @@ emitted by resetParagraphAttributes(). Ideally we should avoid } if (oldStyle != newStyle && newStyle != null) { - writeControlWord("s", ((Integer)newStyle).intValue()); + writeControlWord("s", newStyle); current.addAttribute("paragraphStyle", newStyle); } @@ -707,14 +706,14 @@ void updateCharacterAttributes(MutableAttributeSet current, if (updateStyleChanges) { Object oldStyle = current.getAttribute("characterStyle"); - Object newStyle = findStyleNumber(newAttributes, + Integer newStyle = findStyleNumber(newAttributes, Constants.STCharacter); if (oldStyle != newStyle) { if (oldStyle != null) { resetCharacterAttributes(current); } if (newStyle != null) { - writeControlWord("cs", ((Integer)newStyle).intValue()); + writeControlWord("cs", newStyle.intValue()); current.addAttribute("characterStyle", newStyle); } else { current.removeAttribute("characterStyle"); diff --git a/src/java.desktop/share/classes/javax/swing/text/rtf/RTFReader.java b/src/java.desktop/share/classes/javax/swing/text/rtf/RTFReader.java index e870bb49b045b..7e9d8f173425e 100644 --- a/src/java.desktop/share/classes/javax/swing/text/rtf/RTFReader.java +++ b/src/java.desktop/share/classes/javax/swing/text/rtf/RTFReader.java @@ -22,12 +22,16 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package javax.swing.text.rtf; -import java.lang.*; -import java.util.*; -import java.io.*; import java.awt.Color; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.StreamTokenizer; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; @@ -35,7 +39,24 @@ import java.nio.charset.CodingErrorAction; import java.security.AccessController; import java.security.PrivilegedAction; -import javax.swing.text.*; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.Style; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; +import javax.swing.text.StyledDocument; +import javax.swing.text.TabStop; import static java.nio.charset.StandardCharsets.ISO_8859_1; @@ -648,7 +669,7 @@ static char[] readCharset(InputStream strm) char[] values = new char[256]; int i; StreamTokenizer in = new StreamTokenizer(new BufferedReader( - new InputStreamReader(strm, "ISO-8859-1"))); + new InputStreamReader(strm, ISO_8859_1))); in.eolIsSignificant(false); in.commentChar('#'); diff --git a/src/java.desktop/share/classes/sun/awt/DebugSettings.java b/src/java.desktop/share/classes/sun/awt/DebugSettings.java index 345f26b377047..0921913e02065 100644 --- a/src/java.desktop/share/classes/sun/awt/DebugSettings.java +++ b/src/java.desktop/share/classes/sun/awt/DebugSettings.java @@ -173,9 +173,9 @@ private void loadFileProperties() { File propFile = new File(propPath); try { println("Reading debug settings from '" + propFile.getCanonicalPath() + "'..."); - FileInputStream fin = new FileInputStream(propFile); - props.load(fin); - fin.close(); + try (FileInputStream fin = new FileInputStream(propFile)) { + props.load(fin); + } } catch ( FileNotFoundException fne ) { println("Did not find settings file."); } catch ( IOException ioe ) { diff --git a/src/java.desktop/share/classes/sun/awt/FontConfiguration.java b/src/java.desktop/share/classes/sun/awt/FontConfiguration.java index 48fb0b5b66748..e9f4ee4f7723d 100644 --- a/src/java.desktop/share/classes/sun/awt/FontConfiguration.java +++ b/src/java.desktop/share/classes/sun/awt/FontConfiguration.java @@ -48,7 +48,6 @@ import java.util.Vector; import sun.font.CompositeFontDescriptor; import sun.font.SunFontManager; -import sun.font.FontManagerFactory; import sun.font.FontUtilities; import sun.util.logging.PlatformLogger; @@ -204,14 +203,12 @@ private void readFontConfigFile(File f) { getInstalledFallbackFonts(javaLib); if (f != null) { - try { - FileInputStream in = new FileInputStream(f.getPath()); + try (FileInputStream in = new FileInputStream(f.getPath())) { if (isProperties) { loadProperties(in); } else { loadBinary(in); } - in.close(); if (FontUtilities.debugFonts()) { logger.config("Read logical font configuration from " + f); } diff --git a/src/java.desktop/share/classes/sun/awt/FontDescriptor.java b/src/java.desktop/share/classes/sun/awt/FontDescriptor.java index e934c27a4c1ce..046ecce16daa7 100644 --- a/src/java.desktop/share/classes/sun/awt/FontDescriptor.java +++ b/src/java.desktop/share/classes/sun/awt/FontDescriptor.java @@ -22,14 +22,17 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package sun.awt; import java.io.ByteArrayOutputStream; -import java.io.OutputStreamWriter; import java.io.IOException; +import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; -import java.nio.charset.StandardCharsets; + +import static java.nio.charset.StandardCharsets.UTF_16BE; +import static java.nio.charset.StandardCharsets.UTF_16LE; public class FontDescriptor implements Cloneable { @@ -109,9 +112,8 @@ public String toString() { public boolean useUnicode() { if (useUnicode && unicodeEncoder == null) { try { - this.unicodeEncoder = isLE? - StandardCharsets.UTF_16LE.newEncoder(): - StandardCharsets.UTF_16BE.newEncoder(); + this.unicodeEncoder = isLE ? UTF_16LE.newEncoder(): + UTF_16BE.newEncoder(); } catch (IllegalArgumentException x) {} } return useUnicode; diff --git a/src/java.desktop/share/classes/sun/awt/SunToolkit.java b/src/java.desktop/share/classes/sun/awt/SunToolkit.java index 195c246b2ffb3..b6c8a7183ce86 100644 --- a/src/java.desktop/share/classes/sun/awt/SunToolkit.java +++ b/src/java.desktop/share/classes/sun/awt/SunToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -205,7 +205,7 @@ public abstract KeyboardFocusManagerPeer getKeyboardFocusManagerPeer() * access to Xlib, OpenGL, etc. However, these methods are implemented * in SunToolkit so that they can be called from shared code (e.g. * from the OGL pipeline) or from the X11 pipeline regardless of whether - * XToolkit or MToolkit is currently in use. There are native macros + * XToolkit is currently in use. There are native macros * (such as AWT_LOCK) defined in awt.h, so if the implementation of these * methods is changed, make sure it is compatible with the native macros. * @@ -1805,8 +1805,8 @@ public static RenderingHints getDesktopFontHints() { if (useSystemAAFontSettings()) { Toolkit tk = Toolkit.getDefaultToolkit(); if (tk instanceof SunToolkit) { - Object map = ((SunToolkit)tk).getDesktopAAHints(); - return (RenderingHints)map; + RenderingHints map = ((SunToolkit)tk).getDesktopAAHints(); + return map; } else { /* Headless Toolkit */ return null; } @@ -1883,6 +1883,20 @@ public boolean isNativeGTKAvailable() { return false; } + /** + * Checks if the system is running Linux with the Wayland server. + * + * @return true if running on Wayland, false otherwise + */ + public boolean isRunningOnWayland() { + return false; + } + + public void dismissPopupOnFocusLostIfNeeded(Window invoker) {} + + public void dismissPopupOnFocusLostIfNeededCleanUp(Window invoker) {} + + private static final Object DEACTIVATION_TIMES_MAP_KEY = new Object(); public synchronized void setWindowDeactivationTime(Window w, long time) { diff --git a/src/java.desktop/share/classes/sun/awt/datatransfer/DataTransferer.java b/src/java.desktop/share/classes/sun/awt/datatransfer/DataTransferer.java index 84677785cc680..0898a3ddcecee 100644 --- a/src/java.desktop/share/classes/sun/awt/datatransfer/DataTransferer.java +++ b/src/java.desktop/share/classes/sun/awt/datatransfer/DataTransferer.java @@ -29,77 +29,78 @@ import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; - import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.FlavorMap; import java.awt.datatransfer.FlavorTable; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; - +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FilePermission; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Reader; import java.io.SequenceInputStream; import java.io.StringReader; - +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; import java.net.URI; import java.net.URISyntaxException; - import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.IllegalCharsetNameException; -import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Modifier; - import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; - -import java.util.*; - -import sun.datatransfer.DataFlavorUtil; - -import sun.awt.AppContext; -import sun.awt.ComponentFactory; -import sun.awt.SunToolkit; - -import java.awt.image.BufferedImage; -import java.awt.image.ImageObserver; -import java.awt.image.RenderedImage; -import java.awt.image.WritableRaster; -import java.awt.image.ColorModel; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.Stack; +import java.util.TreeMap; +import java.util.stream.Stream; import javax.imageio.ImageIO; -import javax.imageio.ImageReader; import javax.imageio.ImageReadParam; -import javax.imageio.ImageWriter; +import javax.imageio.ImageReader; import javax.imageio.ImageTypeSpecifier; - +import javax.imageio.ImageWriter; import javax.imageio.spi.ImageWriterSpi; - import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; +import sun.awt.AppContext; +import sun.awt.ComponentFactory; +import sun.awt.SunToolkit; import sun.awt.image.ImageRepresentation; import sun.awt.image.ToolkitImage; +import sun.datatransfer.DataFlavorUtil; -import java.io.FilePermission; -import java.util.stream.Stream; - +import static java.nio.charset.StandardCharsets.UTF_8; /** * Provides a set of functions to be shared among the DataFlavor class and @@ -550,7 +551,7 @@ protected String getBestCharsetForTextFormat(Long lFormat, try { byte[] charsetNameBytes = (byte[])localeTransferable .getTransferData(javaTextEncodingFlavor); - charset = new String(charsetNameBytes, StandardCharsets.UTF_8); + charset = new String(charsetNameBytes, UTF_8); } catch (UnsupportedFlavorException cannotHappen) { } } else { diff --git a/src/java.desktop/share/classes/sun/font/FileFontStrike.java b/src/java.desktop/share/classes/sun/font/FileFontStrike.java index ea2a1608f2d17..bf98b8ca57863 100644 --- a/src/java.desktop/share/classes/sun/font/FileFontStrike.java +++ b/src/java.desktop/share/classes/sun/font/FileFontStrike.java @@ -37,6 +37,7 @@ import java.awt.geom.Rectangle2D; import java.util.concurrent.ConcurrentHashMap; import static sun.awt.SunHints.*; +import sun.java2d.pipe.OutlineTextRenderer; public class FileFontStrike extends PhysicalStrike { @@ -107,6 +108,7 @@ public class FileFontStrike extends PhysicalStrike { boolean useNatives; NativeStrike[] nativeStrikes; + static final int MAX_IMAGE_SIZE = OutlineTextRenderer.THRESHHOLD; /* Used only for communication to native layer */ private int intPtSize; @@ -697,6 +699,20 @@ float getCodePointAdvance(int cp) { void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) { + if (intPtSize > MAX_IMAGE_SIZE) { + Rectangle.Float obds = getGlyphOutlineBounds(glyphCode); + if (obds.isEmpty()) { + Rectangle bds = getGlyphOutline(glyphCode, pt.x, pt.y).getBounds(); + result.setBounds(bds); + } else { + result.x = (int)Math.floor(pt.x + obds.getX() + 0.5f); + result.y = (int)Math.floor(pt.y + obds.getY() + 0.5f); + result.width = (int)Math.floor(obds.getWidth() + 0.5f); + result.height = (int)Math.floor(obds.getHeight() + 0.5f); + } + return; + } + long ptr = getGlyphImagePtr(glyphCode); float topLeftX, topLeftY; diff --git a/src/java.desktop/share/classes/sun/font/Type1Font.java b/src/java.desktop/share/classes/sun/font/Type1Font.java index f640c6f09a910..1c9cf9f8e888a 100644 --- a/src/java.desktop/share/classes/sun/font/Type1Font.java +++ b/src/java.desktop/share/classes/sun/font/Type1Font.java @@ -25,23 +25,23 @@ package sun.font; -import java.lang.ref.WeakReference; import java.awt.FontFormatException; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; -import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.nio.BufferUnderflowException; import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; +import java.util.HashMap; +import java.util.HashSet; + import sun.java2d.Disposer; import sun.java2d.DisposerRecord; -import java.util.HashSet; -import java.util.HashMap; -import java.awt.Font; + +import static java.nio.charset.StandardCharsets.US_ASCII; /* * Adobe Technical Note 5040 details the format of PFB files. @@ -609,11 +609,7 @@ private String getSimpleToken(ByteBuffer bb) { byte[] nameBytes = new byte[pos2-pos1-1]; bb.position(pos1); bb.get(nameBytes); - try { - return new String(nameBytes, "US-ASCII"); - } catch (UnsupportedEncodingException e) { - return new String(nameBytes); - } + return new String(nameBytes, US_ASCII); } private String getString(ByteBuffer bb) { @@ -623,11 +619,7 @@ private String getString(ByteBuffer bb) { byte[] nameBytes = new byte[pos2-pos1-1]; bb.position(pos1); bb.get(nameBytes); - try { - return new String(nameBytes, "US-ASCII"); - } catch (UnsupportedEncodingException e) { - return new String(nameBytes); - } + return new String(nameBytes, US_ASCII); } diff --git a/src/java.desktop/share/classes/sun/java2d/Disposer.java b/src/java.desktop/share/classes/sun/java2d/Disposer.java index 1581364331fd9..6f359f7f87aef 100644 --- a/src/java.desktop/share/classes/sun/java2d/Disposer.java +++ b/src/java.desktop/share/classes/sun/java2d/Disposer.java @@ -142,8 +142,8 @@ synchronized void add(Object target, DisposerRecord rec) { public void run() { while (true) { try { - Object obj = queue.remove(); - ((Reference)obj).clear(); + Reference obj = queue.remove(); + obj.clear(); DisposerRecord rec = records.remove(obj); rec.dispose(); obj = null; @@ -200,7 +200,7 @@ public static void pollRemove() { if (pollingQueue) { return; } - Object obj; + Reference obj; pollingQueue = true; int freed = 0; int deferred = 0; @@ -208,7 +208,7 @@ public static void pollRemove() { while ( freed < 10000 && deferred < 100 && (obj = queue.poll()) != null ) { freed++; - ((Reference)obj).clear(); + obj.clear(); DisposerRecord rec = records.remove(obj); if (rec instanceof PollDisposable) { rec.dispose(); diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 15db79d69d2f9..6aefbe401ce25 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2151,27 +2151,33 @@ private void doCopyArea(int x, int y, int w, int h, int dx, int dy) { } Blit ob = lastCAblit; - if (dy == 0 && dx > 0 && dx < w) { - while (w > 0) { - int partW = Math.min(w, dx); - w -= partW; - int sx = x + w; - ob.Blit(theData, theData, comp, clip, - sx, y, sx+dx, y+dy, partW, h); + try { + if (dy == 0 && dx > 0 && dx < w) { + while (w > 0) { + int partW = Math.min(w, dx); + w -= partW; + int sx = Math.addExact(x, w); + ob.Blit(theData, theData, comp, clip, + sx, y, sx+dx, y+dy, partW, h); + } + return; } - return; - } - if (dy > 0 && dy < h && dx > -w && dx < w) { - while (h > 0) { - int partH = Math.min(h, dy); - h -= partH; - int sy = y + h; - ob.Blit(theData, theData, comp, clip, - x, sy, x+dx, sy+dy, w, partH); + if (dy > 0 && dy < h && dx > -w && dx < w) { + while (h > 0) { + int partH = Math.min(h, dy); + h -= partH; + int sy = Math.addExact(y, h); + ob.Blit(theData, theData, comp, clip, + x, sy, Math.addExact(x, dx), sy+dy, w, partH); + } + return; } + ob.Blit(theData, theData, comp, clip, x, y, + Math.addExact(x, dx), Math.addExact(y, dy), w, h); + } catch (ArithmeticException ex) { + // We are hitting integer overflow in Math.addExact() return; } - ob.Blit(theData, theData, comp, clip, x, y, x+dx, y+dy, w, h); } /* diff --git a/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java b/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java index 7275a01ce0697..00563a84ecf17 100644 --- a/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java +++ b/src/java.desktop/share/classes/sun/java2d/pipe/DrawImage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -369,6 +369,13 @@ protected void renderImageXform(SunGraphics2D sg, Image img, final AffineTransform itx; try { itx = tx.createInverse(); + double[] mat = new double[6]; + itx.getMatrix(mat); + for (double d : mat) { + if (!Double.isFinite(d)) { + return; + } + } } catch (final NoninvertibleTransformException ignored) { // Non-invertible transform means no output return; diff --git a/src/java.desktop/share/classes/sun/print/PSPrinterJob.java b/src/java.desktop/share/classes/sun/print/PSPrinterJob.java index 4645aabd86152..71c642a4b71c4 100644 --- a/src/java.desktop/share/classes/sun/print/PSPrinterJob.java +++ b/src/java.desktop/share/classes/sun/print/PSPrinterJob.java @@ -393,11 +393,12 @@ private static Properties initProps() { } // Load property file - InputStream in = - new BufferedInputStream(new FileInputStream(f.getPath())); Properties props = new Properties(); - props.load(in); - in.close(); + try (FileInputStream is = new FileInputStream(f.getPath()); + BufferedInputStream bis = new BufferedInputStream(is)) + { + props.load(bis); + } return props; } catch (Exception e){ return (Properties)null; diff --git a/src/java.desktop/share/classes/sun/print/PageableDoc.java b/src/java.desktop/share/classes/sun/print/PageableDoc.java index 859f40e853e48..ef829d5bfbb68 100644 --- a/src/java.desktop/share/classes/sun/print/PageableDoc.java +++ b/src/java.desktop/share/classes/sun/print/PageableDoc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,12 +25,10 @@ package sun.print; +import java.awt.print.Pageable; import java.io.IOException; import java.io.InputStream; import java.io.Reader; -import java.io.UnsupportedEncodingException; - -import java.awt.print.Pageable; import javax.print.Doc; import javax.print.DocFlavor; @@ -57,9 +55,7 @@ public Object getPrintData() throws IOException { return pageable; } - public Reader getReaderForText() - throws UnsupportedEncodingException, IOException { - + public Reader getReaderForText() throws IOException { return null; } diff --git a/src/java.desktop/share/legal/giflib.md b/src/java.desktop/share/legal/giflib.md index 0be4fb8226e2d..8b8fde8706d92 100644 --- a/src/java.desktop/share/legal/giflib.md +++ b/src/java.desktop/share/legal/giflib.md @@ -1,4 +1,4 @@ -## GIFLIB v5.2.1 +## GIFLIB v5.2.2 ### GIFLIB License ``` @@ -24,7 +24,27 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -https://sourceforge.net/p/giflib/code/ci/master/tree/openbsd-reallocarray.c +tree/README -Copyright (c) 2008 Otto Moerbeek +== Authors == + +Gershon Elber +original giflib code + +Toshio Kuratomi +uncompressed gif writing code +former maintainer + +Eric Raymond +current as well as long time former maintainer of giflib code + +There have been many other contributors; see the attributions in the +version-control history to learn more. + + +tree/openbsd-reallocarray.c + +Copyright (C) 2008 Otto Moerbeek SPDX-License-Identifier: MIT + +``` diff --git a/src/java.desktop/share/legal/libpng.md b/src/java.desktop/share/legal/libpng.md index f420ccd94ed2e..cbffed8133209 100644 --- a/src/java.desktop/share/legal/libpng.md +++ b/src/java.desktop/share/legal/libpng.md @@ -1,4 +1,4 @@ -## libpng v1.6.40 +## libpng v1.6.43 ### libpng License
    @@ -9,11 +9,11 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
     PNG Reference Library License version 2
     ---------------------------------------
     
    -Copyright (c) 1995-2023 The PNG Reference Library Authors.
    -Copyright (c) 2018-2023 Cosmin Truta
    -Copyright (c) 1998-2018 Glenn Randers-Pehrson
    -Copyright (c) 1996-1997 Andreas Dilger
    -Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    +Copyright (C) 1995-2024 The PNG Reference Library Authors.
    +Copyright (C) 2018-2024 Cosmin Truta
    +Copyright (C) 1998-2018 Glenn Randers-Pehrson
    +Copyright (C) 1996-1997 Andreas Dilger
    +Copyright (C) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
     
     The software is supplied "as is", without warranty of any kind,
     express or implied, including, without limitation, the warranties
    @@ -157,7 +157,9 @@ PNG REFERENCE LIBRARY AUTHORS
     This is the list of PNG Reference Library ("libpng") Contributing
     Authors, for copyright and licensing purposes.
     
    + * Adam Richter
      * Andreas Dilger
    + * Chris Blume
      * Cosmin Truta
      * Dave Martindale
      * Eric S. Raymond
    @@ -186,21 +188,28 @@ Authors, for copyright and licensing purposes.
      * Vadim Barkov
      * Willem van Schaik
      * Zhijie Liang
    + * Apple Inc.
    +    - Zixu Wang (王子旭)
      * Arm Holdings
    -   - Richard Townsend
    +    - Richard Townsend
      * Google Inc.
    -   - Dan Field
    -   - Leon Scroggins III
    -   - Matt Sarett
    -   - Mike Klein
    -   - Sami Boukortt
    -   - Wan-Teh Chang
    +    - Dan Field
    +    - Leon Scroggins III
    +    - Matt Sarett
    +    - Mike Klein
    +    - Sami Boukortt
    +    - Wan-Teh Chang
    + * Loongson Technology Corporation Ltd.
    +    - GuXiWei (顾希伟)
    +    - JinBo (金波)
    +    - ZhangLixia (张利霞)
     
     The build projects, the build scripts, the test scripts, and other
    -files in the "ci", "projects", "scripts" and "tests" directories, have
    +files in the "projects", "scripts" and "tests" directories, have
     other copyright owners, but are released under the libpng license.
     
    -Some files in the "contrib" directory, and some tools-generated files
    -that are distributed with libpng, have other copyright owners, and are
    -released under other open source licenses.
    +Some files in the "ci" and "contrib" directories, as well as some
    +of the tools-generated files that are distributed with libpng, have
    +other copyright owners, and are released under other open source
    +licenses.
     ```
    diff --git a/src/java.desktop/share/native/common/awt/debug/debug_mem.c b/src/java.desktop/share/native/common/awt/debug/debug_mem.c
    index cd6dbe233d37c..3cbe05327c22a 100644
    --- a/src/java.desktop/share/native/common/awt/debug/debug_mem.c
    +++ b/src/java.desktop/share/native/common/awt/debug/debug_mem.c
    @@ -279,7 +279,7 @@ static void DMem_DumpHeader(MemoryBlockHeader * header) {
             "-------";
     
         DMem_VerifyHeader(header);
    -    sprintf(report, reportFormat, header->filename, header->linenumber, header->size, header->order);
    +    snprintf(report, sizeof(report), reportFormat, header->filename, header->linenumber, header->size, header->order);
         DTRACE_PRINTLN(report);
     }
     
    diff --git a/src/java.desktop/share/native/common/awt/debug/debug_trace.c b/src/java.desktop/share/native/common/awt/debug/debug_trace.c
    index 117ebab6e5ec6..31424fb6c3ca8 100644
    --- a/src/java.desktop/share/native/common/awt/debug/debug_trace.c
    +++ b/src/java.desktop/share/native/common/awt/debug/debug_trace.c
    @@ -216,7 +216,7 @@ void DTrace_VPrintImpl(const char * fmt, va_list arglist) {
         DASSERT(fmt != NULL);
     
         /* format the trace message */
    -    vsprintf(DTraceBuffer, fmt, arglist);
    +    vsnprintf(DTraceBuffer, sizeof(DTraceBuffer), fmt, arglist);
         /* not a real great overflow check (memory would already be hammered) but better than nothing */
         DASSERT(strlen(DTraceBuffer) < MAX_TRACE_BUFFER);
         /* output the trace message */
    diff --git a/src/java.desktop/share/native/common/java2d/opengl/OGLBufImgOps.c b/src/java.desktop/share/native/common/java2d/opengl/OGLBufImgOps.c
    index b7e59341cf23e..b1b216287a50f 100644
    --- a/src/java.desktop/share/native/common/java2d/opengl/OGLBufImgOps.c
    +++ b/src/java.desktop/share/native/common/java2d/opengl/OGLBufImgOps.c
    @@ -50,7 +50,7 @@
      * Note that this shader source code includes some "holes" marked by "%s".
      * This allows us to build different shader programs (e.g. one for
      * 3x3, one for 5x5, and so on) simply by filling in these "holes" with
    - * a call to sprintf().  See the OGLBufImgOps_CreateConvolveProgram() method
    + * a call to snprintf().  See the OGLBufImgOps_CreateConvolveProgram() method
      * for more details.
      *
      * REMIND: Currently this shader (and the supporting code in the
    @@ -141,16 +141,16 @@ OGLBufImgOps_CreateConvolveProgram(jint flags)
     
         if (IS_SET(CONVOLVE_EDGE_ZERO_FILL)) {
             // EDGE_ZERO_FILL: fill in zero at the edges
    -        sprintf(edge, "sum = vec4(0.0);");
    +        snprintf(edge, sizeof(edge), "sum = vec4(0.0);");
         } else {
             // EDGE_NO_OP: use the source pixel color at the edges
    -        sprintf(edge,
    +        snprintf(edge, sizeof(edge),
                     "sum = texture%s(baseImage, gl_TexCoord[0].st);",
                     target);
         }
     
         // compose the final source code string from the various pieces
    -    sprintf(finalSource, convolveShaderSource,
    +    snprintf(finalSource, sizeof(finalSource), convolveShaderSource,
                 kernelMax, target, edge, target);
     
         convolveProgram = OGLContext_CreateFragmentProgram(finalSource);
    @@ -296,7 +296,7 @@ OGLBufImgOps_DisableConvolveOp(OGLContext *oglc)
      * Note that this shader source code includes some "holes" marked by "%s".
      * This allows us to build different shader programs (e.g. one for
      * GL_TEXTURE_2D targets, one for GL_TEXTURE_RECTANGLE_ARB targets, and so on)
    - * simply by filling in these "holes" with a call to sprintf().  See the
    + * simply by filling in these "holes" with a call to snprintf().  See the
      * OGLBufImgOps_CreateRescaleProgram() method for more details.
      */
     static const char *rescaleShaderSource =
    @@ -360,7 +360,7 @@ OGLBufImgOps_CreateRescaleProgram(jint flags)
         }
     
         // compose the final source code string from the various pieces
    -    sprintf(finalSource, rescaleShaderSource,
    +    snprintf(finalSource, sizeof(finalSource), rescaleShaderSource,
                 target, target, preRescale, postRescale);
     
         rescaleProgram = OGLContext_CreateFragmentProgram(finalSource);
    @@ -502,7 +502,7 @@ OGLBufImgOps_DisableRescaleOp(OGLContext *oglc)
      * Note that this shader source code includes some "holes" marked by "%s".
      * This allows us to build different shader programs (e.g. one for
      * GL_TEXTURE_2D targets, one for GL_TEXTURE_RECTANGLE_ARB targets, and so on)
    - * simply by filling in these "holes" with a call to sprintf().  See the
    + * simply by filling in these "holes" with a call to snprintf().  See the
      * OGLBufImgOps_CreateLookupProgram() method for more details.
      */
     static const char *lookupShaderSource =
    @@ -592,7 +592,7 @@ OGLBufImgOps_CreateLookupProgram(jint flags)
         }
     
         // compose the final source code string from the various pieces
    -    sprintf(finalSource, lookupShaderSource,
    +    snprintf(finalSource, sizeof(finalSource), lookupShaderSource,
                 target, target, preLookup, alpha, postLookup);
     
         lookupProgram = OGLContext_CreateFragmentProgram(finalSource);
    diff --git a/src/java.desktop/share/native/common/java2d/opengl/OGLPaints.c b/src/java.desktop/share/native/common/java2d/opengl/OGLPaints.c
    index ae416c232d2ab..89f02cf67fb50 100644
    --- a/src/java.desktop/share/native/common/java2d/opengl/OGLPaints.c
    +++ b/src/java.desktop/share/native/common/java2d/opengl/OGLPaints.c
    @@ -578,15 +578,15 @@ OGLPaints_CreateMultiGradProgram(jint flags,
         }
     
         if (cycleMethod == CYCLE_NONE) {
    -        sprintf(cycleCode, noCycleCode, texCoordCalcCode);
    +        snprintf(cycleCode, sizeof(cycleCode), noCycleCode, texCoordCalcCode);
         } else if (cycleMethod == CYCLE_REFLECT) {
    -        sprintf(cycleCode, reflectCode, texCoordCalcCode);
    +        snprintf(cycleCode, sizeof(cycleCode), reflectCode, texCoordCalcCode);
         } else { // (cycleMethod == CYCLE_REPEAT)
    -        sprintf(cycleCode, repeatCode, texCoordCalcCode);
    +        snprintf(cycleCode, sizeof(cycleCode), repeatCode, texCoordCalcCode);
         }
     
         // compose the final source code string from the various pieces
    -    sprintf(finalSource, multiGradientShaderSource,
    +    snprintf(finalSource, sizeof(finalSource), multiGradientShaderSource,
                 MAX_COLORS, maxFractions,
                 maskVars, paintVars, distCode,
                 cycleCode, colorSpaceCode, maskCode);
    diff --git a/src/java.desktop/share/native/libawt/java2d/SurfaceData.h b/src/java.desktop/share/native/libawt/java2d/SurfaceData.h
    index 485046297035b..ca2e985fa7a1c 100644
    --- a/src/java.desktop/share/native/libawt/java2d/SurfaceData.h
    +++ b/src/java.desktop/share/native/libawt/java2d/SurfaceData.h
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -32,6 +32,7 @@
     #define _Included_SurfaceData
     
     #include 
    +#include 
     
     #ifdef __cplusplus
     extern "C" {
    @@ -53,6 +54,14 @@ typedef struct {
     
     #define SD_RASINFO_PRIVATE_SIZE         64
     
    +#define UNSAFE_TO_ADD(a, b) \
    +    (((a >= 0) && (b >= 0) && (a > (INT_MAX - b))) || \
    +     ((a < 0) && (b < 0) && (a < (INT_MIN - b)))) \
    +
    +#define UNSAFE_TO_SUB(a, b) \
    +    (((b >= 0) && (a < 0) && (a < (INT_MIN + b))) || \
    +     ((b < 0) && (a >= 0) && (a > (INT_MAX + b)))) \
    +
     /*
      * The SurfaceDataRasInfo structure is used to pass in and return various
      * pieces of information about the destination drawable.  In particular:
    diff --git a/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c b/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c
    index 21b716e3bcdd6..e8c8765dd2c1c 100644
    --- a/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c
    +++ b/src/java.desktop/share/native/libawt/java2d/loops/MaskBlit.c
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -68,14 +68,28 @@ Java_sun_java2d_loops_MaskBlit_MaskBlit
             return;
         }
     
    +    if (width <= 0 || height <= 0) {
    +        return;
    +    }
    +
         srcInfo.bounds.x1 = srcx;
         srcInfo.bounds.y1 = srcy;
    +    if (UNSAFE_TO_ADD(srcx, width) ||
    +        UNSAFE_TO_ADD(srcy, height) ||
    +        UNSAFE_TO_ADD(dstx, width) ||
    +        UNSAFE_TO_ADD(dsty, height)) {
    +        return;
    +    }
         srcInfo.bounds.x2 = srcx + width;
         srcInfo.bounds.y2 = srcy + height;
         dstInfo.bounds.x1 = dstx;
         dstInfo.bounds.y1 = dsty;
         dstInfo.bounds.x2 = dstx + width;
         dstInfo.bounds.y2 = dsty + height;
    +    if (UNSAFE_TO_SUB(srcx, dstx) ||
    +        UNSAFE_TO_SUB(srcy, dsty)) {
    +        return;
    +    }
         srcx -= dstx;
         srcy -= dsty;
         SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds);
    diff --git a/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c b/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c
    index 354934069c04f..fe0bc406860ce 100644
    --- a/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c
    +++ b/src/java.desktop/share/native/libawt/java2d/loops/MaskFill.c
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -467,7 +467,7 @@ storePgram(EdgeInfo *pLeftEdge, EdgeInfo *pRightEdge,
     #define INSERT_ACCUM(pACCUM, IMIN, IMAX, X0, Y0, X1, Y1, CX1, CX2, MULT) \
         do { \
             jdouble xmid = ((X0) + (X1)) * 0.5; \
    -        if (xmid <= (CX2)) { \
    +        if (xmid < (CX2)) { \
                 jdouble sliceh = ((Y1) - (Y0)); \
                 jdouble slicearea; \
                 jint i; \
    @@ -556,7 +556,7 @@ fillAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo,
         jint cy2 = pRasInfo->bounds.y2;
         jint width = cx2 - cx1;
         EdgeInfo edges[4];
    -    jfloat localaccum[MASK_BUF_LEN + 1];
    +    jfloat localaccum[MASK_BUF_LEN + 2];
         jfloat *pAccum;
     
         if (!storePgram(edges + 0, edges + 2,
    @@ -568,12 +568,12 @@ fillAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo,
         }
     
         pAccum = ((width > MASK_BUF_LEN)
    -              ? malloc((width + 1) * sizeof(jfloat))
    +              ? malloc((width + 2) * sizeof(jfloat))
                   : localaccum);
         if (pAccum == NULL) {
             return;
         }
    -    memset(pAccum, 0, (width+1) * sizeof(jfloat));
    +    memset(pAccum, 0, (width + 2) * sizeof(jfloat));
     
         while (cy1 < cy2) {
             jint lmin, lmax, rmin, rmax;
    @@ -794,7 +794,7 @@ drawAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo,
         jint cy2 = pRasInfo->bounds.y2;
         jint width = cx2 - cx1;
         EdgeInfo edges[8];
    -    jfloat localaccum[MASK_BUF_LEN + 1];
    +    jfloat localaccum[MASK_BUF_LEN + 2];
         jfloat *pAccum;
     
         if (!storePgram(edges + 0, edges + 6,
    @@ -815,12 +815,12 @@ drawAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo,
                    JNI_TRUE);
     
         pAccum = ((width > MASK_BUF_LEN)
    -              ? malloc((width + 1) * sizeof(jfloat))
    +              ? malloc((width + 2) * sizeof(jfloat))
                   : localaccum);
         if (pAccum == NULL) {
             return;
         }
    -    memset(pAccum, 0, (width+1) * sizeof(jfloat));
    +    memset(pAccum, 0, (width + 2) * sizeof(jfloat));
     
         while (cy1 < cy2) {
             jint lmin, lmax, rmin, rmax;
    diff --git a/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c b/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c
    index 4d7442d7aef66..02c99ea9adad3 100644
    --- a/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c
    +++ b/src/java.desktop/share/native/libawt/java2d/loops/TransformHelper.c
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -120,7 +120,7 @@ TransformInterpFunc *pBicubicFunc = BicubicInterp;
     /* We reject coordinates not less than 1<<30 so that the distance between */
     /* any 2 of them is less than 1<<31 which would overflow into the sign */
     /* bit of a signed long value used to represent fixed point coordinates. */
    -#define TX_FIXED_UNSAFE(v)  (fabs(v) >= (1<<30))
    +#define TX_FIXED_UNSAFE(v)  (isinf(v) || isnan(v) || fabs(v) >= (1<<30))
     static jboolean
     checkOverflow(jint dxoff, jint dyoff,
                   SurfaceDataBounds *pBounds,
    diff --git a/src/java.desktop/share/native/libfontmanager/freetypeScaler.c b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c
    index 4cdd0855fe812..7d5b1ed94fe04 100644
    --- a/src/java.desktop/share/native/libfontmanager/freetypeScaler.c
    +++ b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c
    @@ -486,6 +486,8 @@ static double euclidianDistance(double a, double b) {
         return sqrt(a*a+b*b);
     }
     
    +#define TOO_LARGE(a, b) (abs((int)(a / b)) > 32766)
    +
     JNIEXPORT jlong JNICALL
     Java_sun_font_FreetypeFontScaler_createScalerContextNative(
             JNIEnv *env, jobject scaler, jlong pScaler, jdoubleArray matrix,
    @@ -497,6 +499,7 @@ Java_sun_font_FreetypeFontScaler_createScalerContextNative(
                  (FTScalerInfo*) jlong_to_ptr(pScaler);
     
         if (context == NULL) {
    +        free(context);
             invalidateJavaScaler(env, scaler, NULL);
             return (jlong) 0;
         }
    @@ -506,7 +509,18 @@ Java_sun_font_FreetypeFontScaler_createScalerContextNative(
             //text can not be smaller than 1 point
             ptsz = 1.0;
         }
    +    if (ptsz > 16384) {
    +        ptsz = 16384;    // far enough from 32767
    +        fm = TEXT_FM_ON; // avoids calculations which might overflow
    +    }
         context->ptsz = (int)(ptsz * 64);
    +    if (TOO_LARGE(dmat[0], ptsz) || TOO_LARGE(dmat[1], ptsz) ||
    +        TOO_LARGE(dmat[2], ptsz) || TOO_LARGE(dmat[3], ptsz))
    +    {
    +        free(context);
    +        return (jlong)0;
    +    }
    +
         context->transform.xx =  FloatToFTFixed((float)(dmat[0]/ptsz));
         context->transform.yx = -FloatToFTFixed((float)(dmat[1]/ptsz));
         context->transform.xy = -FloatToFTFixed((float)(dmat[2]/ptsz));
    diff --git a/src/java.desktop/share/native/libfontmanager/sunFont.c b/src/java.desktop/share/native/libfontmanager/sunFont.c
    index 661dccae0c5c6..4704f18ea4a02 100644
    --- a/src/java.desktop/share/native/libfontmanager/sunFont.c
    +++ b/src/java.desktop/share/native/libfontmanager/sunFont.c
    @@ -67,7 +67,7 @@ int isNullScalerContext(void *context) {
      */
     JNIEXPORT jlong JNICALL Java_sun_font_NullFontScaler_getGlyphImage
       (JNIEnv *env, jobject scaler, jlong pContext, jint glyphCode) {
    -    void *nullscaler = calloc(sizeof(GlyphInfo), 1);
    +    void *nullscaler = calloc(1, sizeof(GlyphInfo));
         return ptr_to_jlong(nullscaler);
     }
     
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-meta.hh b/src/java.desktop/share/native/libharfbuzz/hb-meta.hh
    index c3af0e75e5f9b..d8d12f0aeb3db 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-meta.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-meta.hh
    @@ -199,7 +199,7 @@ template <> struct hb_int_max         : hb_integral_constant struct hb_int_max       : hb_integral_constant     {};
     #define hb_int_max(T) hb_int_max::value
     
    -#if defined(__GNUC__) && __GNUC__ < 5
    +#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
     #define hb_is_trivially_copyable(T) __has_trivial_copy(T)
     #define hb_is_trivially_copy_assignable(T) __has_trivial_assign(T)
     #define hb_is_trivially_constructible(T) __has_trivial_constructor(T)
    diff --git a/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c b/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c
    index 8a74a6d711e12..5cf8d13a91d2c 100644
    --- a/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c
    +++ b/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -666,8 +666,6 @@ static void imageio_reset(JNIEnv *env,
     static void imageio_dispose(j_common_ptr info) {
     
         if (info != NULL) {
    -        free(info->err);
    -        info->err = NULL;
             if (info->is_decompressor) {
                 j_decompress_ptr dinfo = (j_decompress_ptr) info;
                 free(dinfo->src);
    @@ -678,6 +676,8 @@ static void imageio_dispose(j_common_ptr info) {
                 cinfo->dest = NULL;
             }
             jpeg_destroy(info);
    +        free(info->err);
    +        info->err = NULL;
             free(info);
         }
     }
    diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/dgif_lib.c b/src/java.desktop/share/native/libsplashscreen/giflib/dgif_lib.c
    index 6ddfb46060de4..0b2860b4b509d 100644
    --- a/src/java.desktop/share/native/libsplashscreen/giflib/dgif_lib.c
    +++ b/src/java.desktop/share/native/libsplashscreen/giflib/dgif_lib.c
    @@ -34,11 +34,11 @@ SPDX-License-Identifier: MIT
     
     *****************************************************************************/
     
    -#include 
    +#include 
     #include 
     #include 
    -#include 
     #include 
    +#include 
     #include 
     
     #ifdef _WIN32
    @@ -55,18 +55,19 @@ SPDX-License-Identifier: MIT
     
     /* avoid extra function call in case we use fread (TVT) */
     static int InternalRead(GifFileType *gif, GifByteType *buf, int len) {
    -    //fprintf(stderr, "### Read: %d\n", len);
    -    return
    -    (((GifFilePrivateType*)gif->Private)->Read ?
    -     ((GifFilePrivateType*)gif->Private)->Read(gif,buf,len) :
    -     fread(buf,1,len,((GifFilePrivateType*)gif->Private)->File));
    +    // fprintf(stderr, "### Read: %d\n", len);
    +    return (((GifFilePrivateType *)gif->Private)->Read
    +                ? ((GifFilePrivateType *)gif->Private)->Read(gif, buf, len)
    +                : fread(buf, 1, len,
    +                        ((GifFilePrivateType *)gif->Private)->File));
     }
     
     static int DGifGetWord(GifFileType *GifFile, GifWord *Word);
     static int DGifSetupDecompress(GifFileType *GifFile);
     static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line,
                                   int LineLen);
    -static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode);
    +static int DGifGetPrefixChar(const GifPrefixType *Prefix, int Code,
    +                             int ClearCode);
     static int DGifDecompressInput(GifFileType *GifFile, int *Code);
     static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf,
                                  GifByteType *NextByte);
    @@ -76,15 +77,14 @@ static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf,
      Returns dynamically allocated GifFileType pointer which serves as the GIF
      info record.
     ******************************************************************************/
    -GifFileType *
    -DGifOpenFileName(const char *FileName, int *Error)
    -{
    +GifFileType *DGifOpenFileName(const char *FileName, int *Error) {
         int FileHandle;
         GifFileType *GifFile;
     
         if ((FileHandle = open(FileName, O_RDONLY)) == -1) {
    -        if (Error != NULL)
    +        if (Error != NULL) {
                 *Error = D_GIF_ERR_OPEN_FAILED;
    +        }
             return NULL;
         }
     
    @@ -97,9 +97,7 @@ DGifOpenFileName(const char *FileName, int *Error)
      Returns dynamically allocated GifFileType pointer which serves as the GIF
      info record.
     ******************************************************************************/
    -GifFileType *
    -DGifOpenFileHandle(int FileHandle, int *Error)
    -{
    +GifFileType *DGifOpenFileHandle(int FileHandle, int *Error) {
         char Buf[GIF_STAMP_LEN + 1];
         GifFileType *GifFile;
         GifFilePrivateType *Private;
    @@ -107,13 +105,14 @@ DGifOpenFileHandle(int FileHandle, int *Error)
     
         GifFile = (GifFileType *)malloc(sizeof(GifFileType));
         if (GifFile == NULL) {
    -        if (Error != NULL)
    +        if (Error != NULL) {
                 *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    +        }
             (void)close(FileHandle);
             return NULL;
         }
     
    -    /*@i1@*/memset(GifFile, '\0', sizeof(GifFileType));
    +    /*@i1@*/ memset(GifFile, '\0', sizeof(GifFileType));
     
         /* Belt and suspenders, in case the null pointer isn't zero */
         GifFile->SavedImages = NULL;
    @@ -121,35 +120,38 @@ DGifOpenFileHandle(int FileHandle, int *Error)
     
         Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType));
         if (Private == NULL) {
    -        if (Error != NULL)
    +        if (Error != NULL) {
                 *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    +        }
             (void)close(FileHandle);
             free((char *)GifFile);
             return NULL;
         }
     
    -    /*@i1@*/memset(Private, '\0', sizeof(GifFilePrivateType));
    +    /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType));
     
     #ifdef _WIN32
    -    _setmode(FileHandle, O_BINARY);    /* Make sure it is in binary mode. */
    -#endif /* _WIN32 */
    +    _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */
    +#endif                                  /* _WIN32 */
     
    -    f = fdopen(FileHandle, "rb");    /* Make it into a stream: */
    +    f = fdopen(FileHandle, "rb"); /* Make it into a stream: */
     
         /*@-mustfreeonly@*/
         GifFile->Private = (void *)Private;
         Private->FileHandle = FileHandle;
         Private->File = f;
         Private->FileState = FILE_STATE_READ;
    -    Private->Read = NULL;        /* don't use alternate input method (TVT) */
    -    GifFile->UserData = NULL;    /* TVT */
    +    Private->Read = NULL;     /* don't use alternate input method (TVT) */
    +    GifFile->UserData = NULL; /* TVT */
         /*@=mustfreeonly@*/
     
         /* Let's see if this is a GIF file: */
         /* coverity[check_return] */
    -    if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) {
    -        if (Error != NULL)
    +    if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) !=
    +        GIF_STAMP_LEN) {
    +        if (Error != NULL) {
                 *Error = D_GIF_ERR_READ_FAILED;
    +        }
             (void)fclose(f);
             free((char *)Private);
             free((char *)GifFile);
    @@ -159,8 +161,9 @@ DGifOpenFileHandle(int FileHandle, int *Error)
         /* Check for GIF prefix at start of file */
         Buf[GIF_STAMP_LEN] = 0;
         if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) {
    -        if (Error != NULL)
    +        if (Error != NULL) {
                 *Error = D_GIF_ERR_NOT_GIF_FILE;
    +        }
             (void)fclose(f);
             free((char *)Private);
             free((char *)GifFile);
    @@ -177,7 +180,7 @@ DGifOpenFileHandle(int FileHandle, int *Error)
         GifFile->Error = 0;
     
         /* What version of GIF? */
    -    Private->gif89 = (Buf[GIF_VERSION_POS] == '9');
    +    Private->gif89 = (Buf[GIF_VERSION_POS + 1] == '9');
     
         return GifFile;
     }
    @@ -185,17 +188,16 @@ DGifOpenFileHandle(int FileHandle, int *Error)
     /******************************************************************************
      GifFileType constructor with user supplied input function (TVT)
     ******************************************************************************/
    -GifFileType *
    -DGifOpen(void *userData, InputFunc readFunc, int *Error)
    -{
    +GifFileType *DGifOpen(void *userData, InputFunc readFunc, int *Error) {
         char Buf[GIF_STAMP_LEN + 1];
         GifFileType *GifFile;
         GifFilePrivateType *Private;
     
         GifFile = (GifFileType *)malloc(sizeof(GifFileType));
         if (GifFile == NULL) {
    -        if (Error != NULL)
    +        if (Error != NULL) {
                 *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    +        }
             return NULL;
         }
     
    @@ -207,26 +209,29 @@ DGifOpen(void *userData, InputFunc readFunc, int *Error)
     
         Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType));
         if (!Private) {
    -        if (Error != NULL)
    +        if (Error != NULL) {
                 *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
    +        }
             free((char *)GifFile);
             return NULL;
         }
    -    /*@i1@*/memset(Private, '\0', sizeof(GifFilePrivateType));
    +    /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType));
     
         GifFile->Private = (void *)Private;
         Private->FileHandle = 0;
         Private->File = NULL;
         Private->FileState = FILE_STATE_READ;
     
    -    Private->Read = readFunc;    /* TVT */
    -    GifFile->UserData = userData;    /* TVT */
    +    Private->Read = readFunc;     /* TVT */
    +    GifFile->UserData = userData; /* TVT */
     
         /* Lets see if this is a GIF file: */
         /* coverity[check_return] */
    -    if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) {
    -        if (Error != NULL)
    +    if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) !=
    +        GIF_STAMP_LEN) {
    +        if (Error != NULL) {
                 *Error = D_GIF_ERR_READ_FAILED;
    +        }
             free((char *)Private);
             free((char *)GifFile);
             return NULL;
    @@ -235,8 +240,9 @@ DGifOpen(void *userData, InputFunc readFunc, int *Error)
         /* Check for GIF prefix at start of file */
         Buf[GIF_STAMP_LEN] = '\0';
         if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) {
    -        if (Error != NULL)
    +        if (Error != NULL) {
                 *Error = D_GIF_ERR_NOT_GIF_FILE;
    +        }
             free((char *)Private);
             free((char *)GifFile);
             return NULL;
    @@ -245,15 +251,16 @@ DGifOpen(void *userData, InputFunc readFunc, int *Error)
         if (DGifGetScreenDesc(GifFile) == GIF_ERROR) {
             free((char *)Private);
             free((char *)GifFile);
    -        if (Error != NULL)
    +        if (Error != NULL) {
                 *Error = D_GIF_ERR_NO_SCRN_DSCR;
    +        }
             return NULL;
         }
     
         GifFile->Error = 0;
     
         /* What version of GIF? */
    -    Private->gif89 = (Buf[GIF_VERSION_POS] == '9');
    +    Private->gif89 = (Buf[GIF_VERSION_POS + 1] == '9');
     
         return GifFile;
     }
    @@ -262,9 +269,7 @@ DGifOpen(void *userData, InputFunc readFunc, int *Error)
      This routine should be called before any other DGif calls. Note that
      this routine is called automatically from DGif file open routines.
     ******************************************************************************/
    -int
    -DGifGetScreenDesc(GifFileType *GifFile)
    -{
    +int DGifGetScreenDesc(GifFileType *GifFile) {
         int BitsPerPixel;
         bool SortFlag;
         GifByteType Buf[3];
    @@ -278,8 +283,9 @@ DGifGetScreenDesc(GifFileType *GifFile)
     
         /* Put the screen descriptor into the file: */
         if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR ||
    -        DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR)
    +        DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) {
             return GIF_ERROR;
    +    }
     
         if (InternalRead(GifFile, Buf, 3) != 3) {
             GifFile->Error = D_GIF_ERR_READ_FAILED;
    @@ -292,7 +298,7 @@ DGifGetScreenDesc(GifFileType *GifFile)
         BitsPerPixel = (Buf[0] & 0x07) + 1;
         GifFile->SBackGroundColor = Buf[1];
         GifFile->AspectByte = Buf[2];
    -    if (Buf[0] & 0x80) {    /* Do we have global color map? */
    +    if (Buf[0] & 0x80) { /* Do we have global color map? */
             int i;
     
             GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL);
    @@ -327,23 +333,20 @@ DGifGetScreenDesc(GifFileType *GifFile)
         return GIF_OK;
     }
     
    -const char *
    -DGifGetGifVersion(GifFileType *GifFile)
    -{
    -    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
    +const char *DGifGetGifVersion(GifFileType *GifFile) {
    +    GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
    -    if (Private->gif89)
    +    if (Private->gif89) {
             return GIF89_STAMP;
    -    else
    +    } else {
             return GIF87_STAMP;
    +    }
     }
     
     /******************************************************************************
      This routine should be called before any attempt to read an image.
     ******************************************************************************/
    -int
    -DGifGetRecordType(GifFileType *GifFile, GifRecordType* Type)
    -{
    +int DGifGetRecordType(GifFileType *GifFile, GifRecordType *Type) {
         GifByteType Buf;
         GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
    @@ -359,29 +362,27 @@ DGifGetRecordType(GifFileType *GifFile, GifRecordType* Type)
             return GIF_ERROR;
         }
     
    -    //fprintf(stderr, "### DGifGetRecordType: %02x\n", Buf);
    +    // fprintf(stderr, "### DGifGetRecordType: %02x\n", Buf);
         switch (Buf) {
    -      case DESCRIPTOR_INTRODUCER:
    -          *Type = IMAGE_DESC_RECORD_TYPE;
    -          break;
    -      case EXTENSION_INTRODUCER:
    -          *Type = EXTENSION_RECORD_TYPE;
    -          break;
    -      case TERMINATOR_INTRODUCER:
    -          *Type = TERMINATE_RECORD_TYPE;
    -          break;
    -      default:
    -          *Type = UNDEFINED_RECORD_TYPE;
    -          GifFile->Error = D_GIF_ERR_WRONG_RECORD;
    -          return GIF_ERROR;
    +    case DESCRIPTOR_INTRODUCER:
    +        *Type = IMAGE_DESC_RECORD_TYPE;
    +        break;
    +    case EXTENSION_INTRODUCER:
    +        *Type = EXTENSION_RECORD_TYPE;
    +        break;
    +    case TERMINATOR_INTRODUCER:
    +        *Type = TERMINATE_RECORD_TYPE;
    +        break;
    +    default:
    +        *Type = UNDEFINED_RECORD_TYPE;
    +        GifFile->Error = D_GIF_ERR_WRONG_RECORD;
    +        return GIF_ERROR;
         }
     
         return GIF_OK;
     }
     
    -int
    -DGifGetImageHeader(GifFileType *GifFile)
    -{
    +int DGifGetImageHeader(GifFileType *GifFile) {
         unsigned int BitsPerPixel;
         GifByteType Buf[3];
         GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
    @@ -395,8 +396,9 @@ DGifGetImageHeader(GifFileType *GifFile)
         if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR ||
             DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR ||
             DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR ||
    -        DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR)
    +        DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) {
             return GIF_ERROR;
    +    }
         if (InternalRead(GifFile, Buf, 1) != 1) {
             GifFile->Error = D_GIF_ERR_READ_FAILED;
             GifFreeMapObject(GifFile->Image.ColorMap);
    @@ -415,7 +417,8 @@ DGifGetImageHeader(GifFileType *GifFile)
         if (Buf[0] & 0x80) {
             unsigned int i;
     
    -        GifFile->Image.ColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL);
    +        GifFile->Image.ColorMap =
    +            GifMakeMapObject(1 << BitsPerPixel, NULL);
             if (GifFile->Image.ColorMap == NULL) {
                 GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
                 return GIF_ERROR;
    @@ -436,8 +439,8 @@ DGifGetImageHeader(GifFileType *GifFile)
             }
         }
     
    -    Private->PixelCount = (long)GifFile->Image.Width *
    -       (long)GifFile->Image.Height;
    +    Private->PixelCount =
    +        (long)GifFile->Image.Width * (long)GifFile->Image.Height;
     
         /* Reset decompress algorithm parameters. */
         return DGifSetupDecompress(GifFile);
    @@ -447,9 +450,7 @@ DGifGetImageHeader(GifFileType *GifFile)
      This routine should be called before any attempt to read an image.
      Note it is assumed the Image desc. header has been read.
     ******************************************************************************/
    -int
    -DGifGetImageDesc(GifFileType *GifFile)
    -{
    +int DGifGetImageDesc(GifFileType *GifFile) {
         GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
         SavedImage *sp;
     
    @@ -464,9 +465,9 @@ DGifGetImageDesc(GifFileType *GifFile)
         }
     
         if (GifFile->SavedImages) {
    -        SavedImage* new_saved_images =
    -            (SavedImage *)reallocarray(GifFile->SavedImages,
    -                            (GifFile->ImageCount + 1), sizeof(SavedImage));
    +        SavedImage *new_saved_images = (SavedImage *)reallocarray(
    +            GifFile->SavedImages, (GifFile->ImageCount + 1),
    +            sizeof(SavedImage));
             if (new_saved_images == NULL) {
                 GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
                 return GIF_ERROR;
    @@ -474,7 +475,7 @@ DGifGetImageDesc(GifFileType *GifFile)
             GifFile->SavedImages = new_saved_images;
         } else {
             if ((GifFile->SavedImages =
    -             (SavedImage *) malloc(sizeof(SavedImage))) == NULL) {
    +                 (SavedImage *)malloc(sizeof(SavedImage))) == NULL) {
                 GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
                 return GIF_ERROR;
             }
    @@ -483,9 +484,9 @@ DGifGetImageDesc(GifFileType *GifFile)
         sp = &GifFile->SavedImages[GifFile->ImageCount];
         memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc));
         if (GifFile->Image.ColorMap != NULL) {
    -        sp->ImageDesc.ColorMap = GifMakeMapObject(
    -                                 GifFile->Image.ColorMap->ColorCount,
    -                                 GifFile->Image.ColorMap->Colors);
    +        sp->ImageDesc.ColorMap =
    +            GifMakeMapObject(GifFile->Image.ColorMap->ColorCount,
    +                             GifFile->Image.ColorMap->Colors);
             if (sp->ImageDesc.ColorMap == NULL) {
                 GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
                 return GIF_ERROR;
    @@ -493,7 +494,7 @@ DGifGetImageDesc(GifFileType *GifFile)
         }
         sp->RasterBits = (unsigned char *)NULL;
         sp->ExtensionBlockCount = 0;
    -    sp->ExtensionBlocks = (ExtensionBlock *) NULL;
    +    sp->ExtensionBlocks = (ExtensionBlock *)NULL;
     
         GifFile->ImageCount++;
     
    @@ -503,11 +504,9 @@ DGifGetImageDesc(GifFileType *GifFile)
     /******************************************************************************
      Get one full scanned line (Line) of length LineLen from GIF file.
     ******************************************************************************/
    -int
    -DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
    -{
    +int DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) {
         GifByteType *Dummy;
    -    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
    +    GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
         if (!IS_READABLE(Private)) {
             /* This file was NOT open for reading: */
    @@ -515,8 +514,9 @@ DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
             return GIF_ERROR;
         }
     
    -    if (!LineLen)
    +    if (!LineLen) {
             LineLen = GifFile->Image.Width;
    +    }
     
         if ((Private->PixelCount -= LineLen) > 0xffff0000UL) {
             GifFile->Error = D_GIF_ERR_DATA_TOO_BIG;
    @@ -525,56 +525,59 @@ DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
     
         if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) {
             if (Private->PixelCount == 0) {
    -            /* We probably won't be called any more, so let's clean up
    -             * everything before we return: need to flush out all the
    -             * rest of image until an empty block (size 0)
    +            /* We probably won't be called any more, so let's clean
    +             * up everything before we return: need to flush out all
    +             * the rest of image until an empty block (size 0)
                  * detected. We use GetCodeNext.
                  */
    -            do
    -                if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR)
    +            do {
    +                if (DGifGetCodeNext(GifFile, &Dummy) ==
    +                    GIF_ERROR) {
                         return GIF_ERROR;
    -            while (Dummy != NULL) ;
    +                }
    +            } while (Dummy != NULL);
             }
             return GIF_OK;
    -    } else
    +    } else {
             return GIF_ERROR;
    +    }
     }
     
     /******************************************************************************
      Put one pixel (Pixel) into GIF file.
     ******************************************************************************/
    -int
    -DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel)
    -{
    +int DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel) {
         GifByteType *Dummy;
    -    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
    +    GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
         if (!IS_READABLE(Private)) {
             /* This file was NOT open for reading: */
             GifFile->Error = D_GIF_ERR_NOT_READABLE;
             return GIF_ERROR;
         }
    -    if (--Private->PixelCount > 0xffff0000UL)
    -    {
    +    if (--Private->PixelCount > 0xffff0000UL) {
             GifFile->Error = D_GIF_ERR_DATA_TOO_BIG;
             return GIF_ERROR;
         }
     
         if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) {
             if (Private->PixelCount == 0) {
    -            /* We probably won't be called any more, so let's clean up
    -             * everything before we return: need to flush out all the
    -             * rest of image until an empty block (size 0)
    +            /* We probably won't be called any more, so let's clean
    +             * up everything before we return: need to flush out all
    +             * the rest of image until an empty block (size 0)
                  * detected. We use GetCodeNext.
                  */
    -            do
    -                if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR)
    +            do {
    +                if (DGifGetCodeNext(GifFile, &Dummy) ==
    +                    GIF_ERROR) {
                         return GIF_ERROR;
    -            while (Dummy != NULL) ;
    +                }
    +            } while (Dummy != NULL);
             }
             return GIF_OK;
    -    } else
    +    } else {
             return GIF_ERROR;
    +    }
     }
     
     /******************************************************************************
    @@ -584,13 +587,12 @@ DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel)
      The Extension should NOT be freed by the user (not dynamically allocated).
      Note it is assumed the Extension description header has been read.
     ******************************************************************************/
    -int
    -DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension)
    -{
    +int DGifGetExtension(GifFileType *GifFile, int *ExtCode,
    +                     GifByteType **Extension) {
         GifByteType Buf;
         GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
    -    //fprintf(stderr, "### -> DGifGetExtension:\n");
    +    // fprintf(stderr, "### -> DGifGetExtension:\n");
         if (!IS_READABLE(Private)) {
             /* This file was NOT open for reading: */
             GifFile->Error = D_GIF_ERR_NOT_READABLE;
    @@ -603,7 +605,8 @@ DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension)
             return GIF_ERROR;
         }
         *ExtCode = Buf;
    -    //fprintf(stderr, "### <- DGifGetExtension: %02x, about to call next\n", Buf);
    +    // fprintf(stderr, "### <- DGifGetExtension: %02x, about to call
    +    // next\n", Buf);
     
         return DGifGetExtensionNext(GifFile, Extension);
     }
    @@ -613,30 +616,30 @@ DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension)
      routine should be called until NULL Extension is returned.
      The Extension should NOT be freed by the user (not dynamically allocated).
     ******************************************************************************/
    -int
    -DGifGetExtensionNext(GifFileType *GifFile, GifByteType ** Extension)
    -{
    +int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **Extension) {
         GifByteType Buf;
         GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
    -    //fprintf(stderr, "### -> DGifGetExtensionNext\n");
    +    // fprintf(stderr, "### -> DGifGetExtensionNext\n");
         if (InternalRead(GifFile, &Buf, 1) != 1) {
             GifFile->Error = D_GIF_ERR_READ_FAILED;
             return GIF_ERROR;
         }
    -    //fprintf(stderr, "### DGifGetExtensionNext sees %d\n", Buf);
    +    // fprintf(stderr, "### DGifGetExtensionNext sees %d\n", Buf);
     
         if (Buf > 0) {
    -        *Extension = Private->Buf;    /* Use private unused buffer. */
    -        (*Extension)[0] = Buf;  /* Pascal strings notation (pos. 0 is len.). */
    -        /* coverity[tainted_data,check_return] */
    +        *Extension = Private->Buf; /* Use private unused buffer. */
    +        (*Extension)[0] =
    +            Buf; /* Pascal strings notation (pos. 0 is len.). */
    +                 /* coverity[tainted_data,check_return] */
             if (InternalRead(GifFile, &((*Extension)[1]), Buf) != Buf) {
                 GifFile->Error = D_GIF_ERR_READ_FAILED;
                 return GIF_ERROR;
             }
    -    } else
    +    } else {
             *Extension = NULL;
    -    //fprintf(stderr, "### <- DGifGetExtensionNext: %p\n", Extension);
    +    }
    +    // fprintf(stderr, "### <- DGifGetExtensionNext: %p\n", Extension);
     
         return GIF_OK;
     }
    @@ -647,19 +650,20 @@ DGifGetExtensionNext(GifFileType *GifFile, GifByteType ** Extension)
     
     int DGifExtensionToGCB(const size_t GifExtensionLength,
                            const GifByteType *GifExtension,
    -                       GraphicsControlBlock *GCB)
    -{
    +                       GraphicsControlBlock *GCB) {
         if (GifExtensionLength != 4) {
             return GIF_ERROR;
         }
     
         GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07;
         GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0;
    -    GCB->DelayTime = UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]);
    -    if (GifExtension[0] & 0x01)
    +    GCB->DelayTime =
    +        UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]);
    +    if (GifExtension[0] & 0x01) {
             GCB->TransparentColor = (int)GifExtension[3];
    -    else
    +    } else {
             GCB->TransparentColor = NO_TRANSPARENT_COLOR;
    +    }
     
         return GIF_OK;
     }
    @@ -668,23 +672,27 @@ int DGifExtensionToGCB(const size_t GifExtensionLength,
      Extract the Graphics Control Block for a saved image, if it exists.
     ******************************************************************************/
     
    -int DGifSavedExtensionToGCB(GifFileType *GifFile,
    -                int ImageIndex, GraphicsControlBlock *GCB)
    -{
    +int DGifSavedExtensionToGCB(GifFileType *GifFile, int ImageIndex,
    +                            GraphicsControlBlock *GCB) {
         int i;
     
    -    if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1)
    +    if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) {
             return GIF_ERROR;
    +    }
     
         GCB->DisposalMode = DISPOSAL_UNSPECIFIED;
         GCB->UserInputFlag = false;
         GCB->DelayTime = 0;
         GCB->TransparentColor = NO_TRANSPARENT_COLOR;
     
    -    for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) {
    -        ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
    -        if (ep->Function == GRAPHICS_EXT_FUNC_CODE)
    -            return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, GCB);
    +    for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount;
    +         i++) {
    +        ExtensionBlock *ep =
    +            &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
    +        if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
    +            return DGifExtensionToGCB(ep->ByteCount, ep->Bytes,
    +                                      GCB);
    +        }
         }
     
         return GIF_ERROR;
    @@ -693,13 +701,12 @@ int DGifSavedExtensionToGCB(GifFileType *GifFile,
     /******************************************************************************
      This routine should be called last, to close the GIF file.
     ******************************************************************************/
    -int
    -DGifCloseFile(GifFileType *GifFile, int *ErrorCode)
    -{
    +int DGifCloseFile(GifFileType *GifFile, int *ErrorCode) {
         GifFilePrivateType *Private;
     
    -    if (GifFile == NULL || GifFile->Private == NULL)
    +    if (GifFile == NULL || GifFile->Private == NULL) {
             return GIF_ERROR;
    +    }
     
         if (GifFile->Image.ColorMap) {
             GifFreeMapObject(GifFile->Image.ColorMap);
    @@ -716,22 +723,25 @@ DGifCloseFile(GifFileType *GifFile, int *ErrorCode)
             GifFile->SavedImages = NULL;
         }
     
    -    GifFreeExtensions(&GifFile->ExtensionBlockCount, &GifFile->ExtensionBlocks);
    +    GifFreeExtensions(&GifFile->ExtensionBlockCount,
    +                      &GifFile->ExtensionBlocks);
     
    -    Private = (GifFilePrivateType *) GifFile->Private;
    +    Private = (GifFilePrivateType *)GifFile->Private;
     
         if (!IS_READABLE(Private)) {
             /* This file was NOT open for reading: */
    -        if (ErrorCode != NULL)
    +        if (ErrorCode != NULL) {
                 *ErrorCode = D_GIF_ERR_NOT_READABLE;
    +        }
             free((char *)GifFile->Private);
             free(GifFile);
             return GIF_ERROR;
         }
     
         if (Private->File && (fclose(Private->File) != 0)) {
    -        if (ErrorCode != NULL)
    +        if (ErrorCode != NULL) {
                 *ErrorCode = D_GIF_ERR_CLOSE_FAILED;
    +        }
             free((char *)GifFile->Private);
             free(GifFile);
             return GIF_ERROR;
    @@ -739,17 +749,16 @@ DGifCloseFile(GifFileType *GifFile, int *ErrorCode)
     
         free((char *)GifFile->Private);
         free(GifFile);
    -    if (ErrorCode != NULL)
    +    if (ErrorCode != NULL) {
             *ErrorCode = D_GIF_SUCCEEDED;
    +    }
         return GIF_OK;
     }
     
     /******************************************************************************
      Get 2 bytes (word) from the given file:
     ******************************************************************************/
    -static int
    -DGifGetWord(GifFileType *GifFile, GifWord *Word)
    -{
    +static int DGifGetWord(GifFileType *GifFile, GifWord *Word) {
         unsigned char c[2];
     
         /* coverity[check_return] */
    @@ -769,9 +778,7 @@ DGifGetWord(GifFileType *GifFile, GifWord *Word)
      to DGifGetCodeNext, until NULL block is returned.
      The block should NOT be freed by the user (not dynamically allocated).
     ******************************************************************************/
    -int
    -DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock)
    -{
    +int DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock) {
         GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
         if (!IS_READABLE(Private)) {
    @@ -790,9 +797,7 @@ DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock)
      called until NULL block is returned.
      The block should NOT be freed by the user (not dynamically allocated).
     ******************************************************************************/
    -int
    -DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock)
    -{
    +int DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock) {
         GifByteType Buf;
         GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
    @@ -805,17 +810,19 @@ DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock)
     
         /* coverity[lower_bounds] */
         if (Buf > 0) {
    -        *CodeBlock = Private->Buf;    /* Use private unused buffer. */
    -        (*CodeBlock)[0] = Buf;  /* Pascal strings notation (pos. 0 is len.). */
    -        /* coverity[tainted_data] */
    +        *CodeBlock = Private->Buf; /* Use private unused buffer. */
    +        (*CodeBlock)[0] =
    +            Buf; /* Pascal strings notation (pos. 0 is len.). */
    +                 /* coverity[tainted_data] */
             if (InternalRead(GifFile, &((*CodeBlock)[1]), Buf) != Buf) {
                 GifFile->Error = D_GIF_ERR_READ_FAILED;
                 return GIF_ERROR;
             }
         } else {
             *CodeBlock = NULL;
    -        Private->Buf[0] = 0;    /* Make sure the buffer is empty! */
    -        Private->PixelCount = 0;    /* And local info. indicate image read. */
    +        Private->Buf[0] = 0; /* Make sure the buffer is empty! */
    +        Private->PixelCount =
    +            0; /* And local info. indicate image read. */
         }
     
         return GIF_OK;
    @@ -824,41 +831,43 @@ DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock)
     /******************************************************************************
      Setup the LZ decompression for this image:
     ******************************************************************************/
    -static int
    -DGifSetupDecompress(GifFileType *GifFile)
    -{
    +static int DGifSetupDecompress(GifFileType *GifFile) {
         int i, BitsPerPixel;
         GifByteType CodeSize;
         GifPrefixType *Prefix;
         GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
         /* coverity[check_return] */
    -    if (InternalRead(GifFile, &CodeSize, 1) < 1) {    /* Read Code size from file. */
    -        return GIF_ERROR;    /* Failed to read Code size. */
    +    if (InternalRead(GifFile, &CodeSize, 1) <
    +        1) { /* Read Code size from file. */
    +        GifFile->Error = D_GIF_ERR_READ_FAILED;
    +        return GIF_ERROR; /* Failed to read Code size. */
         }
         BitsPerPixel = CodeSize;
     
         /* this can only happen on a severely malformed GIF */
         if (BitsPerPixel > 8) {
    -        GifFile->Error = D_GIF_ERR_READ_FAILED;    /* somewhat bogus error code */
    -        return GIF_ERROR;    /* Failed to read Code size. */
    +        GifFile->Error =
    +            D_GIF_ERR_READ_FAILED; /* somewhat bogus error code */
    +        return GIF_ERROR;          /* Failed to read Code size. */
         }
     
    -    Private->Buf[0] = 0;    /* Input Buffer empty. */
    +    Private->Buf[0] = 0; /* Input Buffer empty. */
         Private->BitsPerPixel = BitsPerPixel;
         Private->ClearCode = (1 << BitsPerPixel);
         Private->EOFCode = Private->ClearCode + 1;
         Private->RunningCode = Private->EOFCode + 1;
    -    Private->RunningBits = BitsPerPixel + 1;    /* Number of bits per code. */
    -    Private->MaxCode1 = 1 << Private->RunningBits;    /* Max. code + 1. */
    -    Private->StackPtr = 0;    /* No pixels on the pixel stack. */
    +    Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */
    +    Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */
    +    Private->StackPtr = 0; /* No pixels on the pixel stack. */
         Private->LastCode = NO_SUCH_CODE;
    -    Private->CrntShiftState = 0;    /* No information in CrntShiftDWord. */
    +    Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */
         Private->CrntShiftDWord = 0;
     
         Prefix = Private->Prefix;
    -    for (i = 0; i <= LZ_MAX_CODE; i++)
    +    for (i = 0; i <= LZ_MAX_CODE; i++) {
             Prefix[i] = NO_SUCH_CODE;
    +    }
     
         return GIF_OK;
     }
    @@ -869,14 +878,13 @@ DGifSetupDecompress(GifFileType *GifFile)
      This routine can be called few times (one per scan line, for example), in
      order the complete the whole image.
     ******************************************************************************/
    -static int
    -DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
    -{
    +static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line,
    +                              int LineLen) {
         int i = 0;
         int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr;
         GifByteType *Stack, *Suffix;
         GifPrefixType *Prefix;
    -    GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
    +    GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
         StackPtr = Private->StackPtr;
         Prefix = Private->Prefix;
    @@ -891,72 +899,88 @@ DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
         }
     
         if (StackPtr != 0) {
    -        /* Let pop the stack off before continueing to read the GIF file: */
    -        while (StackPtr != 0 && i < LineLen)
    +        /* Let pop the stack off before continueing to read the GIF
    +         * file: */
    +        while (StackPtr != 0 && i < LineLen) {
                 Line[i++] = Stack[--StackPtr];
    +        }
         }
     
    -    while (i < LineLen) {    /* Decode LineLen items. */
    -        if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR)
    +    while (i < LineLen) { /* Decode LineLen items. */
    +        if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) {
                 return GIF_ERROR;
    +        }
     
             if (CrntCode == EOFCode) {
    -            /* Note however that usually we will not be here as we will stop
    -             * decoding as soon as we got all the pixel, or EOF code will
    -             * not be read at all, and DGifGetLine/Pixel clean everything.  */
    +            /* Note however that usually we will not be here as we
    +             * will stop decoding as soon as we got all the pixel,
    +             * or EOF code will not be read at all, and
    +             * DGifGetLine/Pixel clean everything.  */
                 GifFile->Error = D_GIF_ERR_EOF_TOO_SOON;
                 return GIF_ERROR;
             } else if (CrntCode == ClearCode) {
                 /* We need to start over again: */
    -            for (j = 0; j <= LZ_MAX_CODE; j++)
    +            for (j = 0; j <= LZ_MAX_CODE; j++) {
                     Prefix[j] = NO_SUCH_CODE;
    +            }
                 Private->RunningCode = Private->EOFCode + 1;
                 Private->RunningBits = Private->BitsPerPixel + 1;
                 Private->MaxCode1 = 1 << Private->RunningBits;
                 LastCode = Private->LastCode = NO_SUCH_CODE;
             } else {
    -            /* Its regular code - if in pixel range simply add it to output
    -             * stream, otherwise trace to codes linked list until the prefix
    -             * is in pixel range: */
    +            /* Its regular code - if in pixel range simply add it to
    +             * output stream, otherwise trace to codes linked list
    +             * until the prefix is in pixel range: */
                 if (CrntCode < ClearCode) {
    -                /* This is simple - its pixel scalar, so add it to output: */
    +                /* This is simple - its pixel scalar, so add it
    +                 * to output: */
                     Line[i++] = CrntCode;
                 } else {
    -                /* Its a code to needed to be traced: trace the linked list
    -                 * until the prefix is a pixel, while pushing the suffix
    -                 * pixels on our stack. If we done, pop the stack in reverse
    -                 * (thats what stack is good for!) order to output.  */
    +                /* Its a code to needed to be traced: trace the
    +                 * linked list until the prefix is a pixel,
    +                 * while pushing the suffix pixels on our stack.
    +                 * If we done, pop the stack in reverse (thats
    +                 * what stack is good for!) order to output.  */
                     if (Prefix[CrntCode] == NO_SUCH_CODE) {
                         CrntPrefix = LastCode;
     
    -                    /* Only allowed if CrntCode is exactly the running code:
    -                     * In that case CrntCode = XXXCode, CrntCode or the
    -                     * prefix code is last code and the suffix char is
    -                     * exactly the prefix of last code! */
    -                    if (CrntCode == Private->RunningCode - 2) {
    -                        Suffix[Private->RunningCode - 2] =
    -                           Stack[StackPtr++] = DGifGetPrefixChar(Prefix,
    -                                                                 LastCode,
    -                                                                 ClearCode);
    +                    /* Only allowed if CrntCode is exactly
    +                     * the running code: In that case
    +                     * CrntCode = XXXCode, CrntCode or the
    +                     * prefix code is last code and the
    +                     * suffix char is exactly the prefix of
    +                     * last code! */
    +                    if (CrntCode ==
    +                        Private->RunningCode - 2) {
    +                        Suffix[Private->RunningCode -
    +                               2] = Stack[StackPtr++] =
    +                            DGifGetPrefixChar(
    +                                Prefix, LastCode,
    +                                ClearCode);
                         } else {
    -                        Suffix[Private->RunningCode - 2] =
    -                           Stack[StackPtr++] = DGifGetPrefixChar(Prefix,
    -                                                                 CrntCode,
    -                                                                 ClearCode);
    +                        Suffix[Private->RunningCode -
    +                               2] = Stack[StackPtr++] =
    +                            DGifGetPrefixChar(
    +                                Prefix, CrntCode,
    +                                ClearCode);
                         }
    -                } else
    +                } else {
                         CrntPrefix = CrntCode;
    +                }
     
    -                /* Now (if image is O.K.) we should not get a NO_SUCH_CODE
    -                 * during the trace. As we might loop forever, in case of
    -                 * defective image, we use StackPtr as loop counter and stop
    -                 * before overflowing Stack[]. */
    +                /* Now (if image is O.K.) we should not get a
    +                 * NO_SUCH_CODE during the trace. As we might
    +                 * loop forever, in case of defective image, we
    +                 * use StackPtr as loop counter and stop before
    +                 * overflowing Stack[]. */
                     while (StackPtr < LZ_MAX_CODE &&
    -                       CrntPrefix > ClearCode && CrntPrefix <= LZ_MAX_CODE) {
    +                       CrntPrefix > ClearCode &&
    +                       CrntPrefix <= LZ_MAX_CODE) {
                         Stack[StackPtr++] = Suffix[CrntPrefix];
                         CrntPrefix = Prefix[CrntPrefix];
                     }
    -                if (StackPtr >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) {
    +                if (StackPtr >= LZ_MAX_CODE ||
    +                    CrntPrefix > LZ_MAX_CODE) {
                         GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
                         return GIF_ERROR;
                     }
    @@ -964,22 +988,29 @@ DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
                     Stack[StackPtr++] = CrntPrefix;
     
                     /* Now lets pop all the stack into output: */
    -                while (StackPtr != 0 && i < LineLen)
    +                while (StackPtr != 0 && i < LineLen) {
                         Line[i++] = Stack[--StackPtr];
    +                }
                 }
    -            if (LastCode != NO_SUCH_CODE && Private->RunningCode - 2 < (LZ_MAX_CODE+1) && Prefix[Private->RunningCode - 2] == NO_SUCH_CODE) {
    +            if (LastCode != NO_SUCH_CODE &&
    +                Private->RunningCode - 2 < (LZ_MAX_CODE + 1) &&
    +                Prefix[Private->RunningCode - 2] == NO_SUCH_CODE) {
                     Prefix[Private->RunningCode - 2] = LastCode;
     
                     if (CrntCode == Private->RunningCode - 2) {
    -                    /* Only allowed if CrntCode is exactly the running code:
    -                     * In that case CrntCode = XXXCode, CrntCode or the
    -                     * prefix code is last code and the suffix char is
    -                     * exactly the prefix of last code! */
    +                    /* Only allowed if CrntCode is exactly
    +                     * the running code: In that case
    +                     * CrntCode = XXXCode, CrntCode or the
    +                     * prefix code is last code and the
    +                     * suffix char is exactly the prefix of
    +                     * last code! */
                         Suffix[Private->RunningCode - 2] =
    -                       DGifGetPrefixChar(Prefix, LastCode, ClearCode);
    +                        DGifGetPrefixChar(Prefix, LastCode,
    +                                          ClearCode);
                     } else {
                         Suffix[Private->RunningCode - 2] =
    -                       DGifGetPrefixChar(Prefix, CrntCode, ClearCode);
    +                        DGifGetPrefixChar(Prefix, CrntCode,
    +                                          ClearCode);
                     }
                 }
                 LastCode = CrntCode;
    @@ -998,9 +1029,8 @@ DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
      If image is defective, we might loop here forever, so we limit the loops to
      the maximum possible if image O.k. - LZ_MAX_CODE times.
     ******************************************************************************/
    -static int
    -DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode)
    -{
    +static int DGifGetPrefixChar(const GifPrefixType *Prefix, int Code,
    +                             int ClearCode) {
         int i = 0;
     
         while (Code > ClearCode && i++ <= LZ_MAX_CODE) {
    @@ -1016,9 +1046,7 @@ DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode)
      Interface for accessing the LZ codes directly. Set Code to the real code
      (12bits), or to -1 if EOF code is returned.
     ******************************************************************************/
    -int
    -DGifGetLZCodes(GifFileType *GifFile, int *Code)
    -{
    +int DGifGetLZCodes(GifFileType *GifFile, int *Code) {
         GifByteType *CodeBlock;
         GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
    @@ -1028,15 +1056,18 @@ DGifGetLZCodes(GifFileType *GifFile, int *Code)
             return GIF_ERROR;
         }
     
    -    if (DGifDecompressInput(GifFile, Code) == GIF_ERROR)
    +    if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) {
             return GIF_ERROR;
    +    }
     
         if (*Code == Private->EOFCode) {
    -        /* Skip rest of codes (hopefully only NULL terminating block): */
    +        /* Skip rest of codes (hopefully only NULL terminating block):
    +         */
             do {
    -            if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR)
    +            if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) {
                     return GIF_ERROR;
    -        } while (CodeBlock != NULL) ;
    +            }
    +        } while (CodeBlock != NULL);
     
             *Code = -1;
         } else if (*Code == Private->ClearCode) {
    @@ -1055,15 +1086,10 @@ DGifGetLZCodes(GifFileType *GifFile, int *Code)
      8 bits (bytes) packets, into the real codes.
      Returns GIF_OK if read successfully.
     ******************************************************************************/
    -static int
    -DGifDecompressInput(GifFileType *GifFile, int *Code)
    -{
    +static int DGifDecompressInput(GifFileType *GifFile, int *Code) {
         static const unsigned short CodeMasks[] = {
    -        0x0000, 0x0001, 0x0003, 0x0007,
    -        0x000f, 0x001f, 0x003f, 0x007f,
    -        0x00ff, 0x01ff, 0x03ff, 0x07ff,
    -        0x0fff
    -    };
    +        0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f,
    +        0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff};
     
         GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
     
    @@ -1077,11 +1103,12 @@ DGifDecompressInput(GifFileType *GifFile, int *Code)
     
         while (Private->CrntShiftState < Private->RunningBits) {
             /* Needs to get more bytes from input stream for next code: */
    -        if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == GIF_ERROR) {
    +        if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) ==
    +            GIF_ERROR) {
                 return GIF_ERROR;
             }
    -        Private->CrntShiftDWord |=
    -            ((unsigned long)NextByte) << Private->CrntShiftState;
    +        Private->CrntShiftDWord |= ((unsigned long)NextByte)
    +                                   << Private->CrntShiftState;
             Private->CrntShiftState += 8;
         }
         *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits];
    @@ -1109,9 +1136,8 @@ DGifDecompressInput(GifFileType *GifFile, int *Code)
      The routine returns the next byte from its internal buffer (or read next
      block in if buffer empty) and returns GIF_OK if succesful.
     ******************************************************************************/
    -static int
    -DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte)
    -{
    +static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf,
    +                             GifByteType *NextByte) {
         if (Buf[0] == 0) {
             /* Needs to read the next buffer - this one is empty: */
             /* coverity[check_return] */
    @@ -1120,8 +1146,8 @@ DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte)
                 return GIF_ERROR;
             }
             /* There shouldn't be any empty data blocks here as the LZW spec
    -         * says the LZW termination code should come first.  Therefore we
    -         * shouldn't be inside this routine at that point.
    +         * says the LZW termination code should come first.  Therefore
    +         * we shouldn't be inside this routine at that point.
              */
             if (Buf[0] == 0) {
                 GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
    @@ -1132,7 +1158,7 @@ DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte)
                 return GIF_ERROR;
             }
             *NextByte = Buf[1];
    -        Buf[1] = 2;    /* We use now the second place as last char read! */
    +        Buf[1] = 2; /* We use now the second place as last char read! */
             Buf[0]--;
         } else {
             *NextByte = Buf[Buf[1]++];
    @@ -1142,14 +1168,32 @@ DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte)
         return GIF_OK;
     }
     
    +/******************************************************************************
    + This routine is called in case of error during parsing image. We need to
    + decrease image counter and reallocate memory for saved images. Not decreasing
    + ImageCount may lead to null pointer dereference, because the last element in
    + SavedImages may point to the spoilt image and null pointer buffers.
    +*******************************************************************************/
    +void DGifDecreaseImageCounter(GifFileType *GifFile) {
    +    GifFile->ImageCount--;
    +    if (GifFile->SavedImages[GifFile->ImageCount].RasterBits != NULL) {
    +        free(GifFile->SavedImages[GifFile->ImageCount].RasterBits);
    +    }
    +
    +    // Realloc array according to the new image counter.
    +    SavedImage *correct_saved_images = (SavedImage *)reallocarray(
    +        GifFile->SavedImages, GifFile->ImageCount, sizeof(SavedImage));
    +    if (correct_saved_images != NULL) {
    +        GifFile->SavedImages = correct_saved_images;
    +    }
    +}
    +
     /******************************************************************************
      This routine reads an entire GIF into core, hanging all its state info off
      the GifFileType pointer.  Call DGifOpenFileName() or DGifOpenFileHandle()
      first to initialize I/O.  Its inverse is EGifSpew().
     *******************************************************************************/
    -int
    -DGifSlurp(GifFileType *GifFile)
    -{
    +int DGifSlurp(GifFileType *GifFile) {
         size_t ImageSize;
         GifRecordType RecordType;
         SavedImage *sp;
    @@ -1160,103 +1204,130 @@ DGifSlurp(GifFileType *GifFile)
         GifFile->ExtensionBlockCount = 0;
     
         do {
    -        if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
    +        if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
                 return (GIF_ERROR);
    +        }
     
             switch (RecordType) {
    -          case IMAGE_DESC_RECORD_TYPE:
    -              if (DGifGetImageDesc(GifFile) == GIF_ERROR)
    -                  return (GIF_ERROR);
    -
    -              sp = &GifFile->SavedImages[GifFile->ImageCount - 1];
    -              /* Allocate memory for the image */
    -              if (sp->ImageDesc.Width <= 0 || sp->ImageDesc.Height <= 0 ||
    -                      sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) {
    -                  return GIF_ERROR;
    -              }
    -              ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;
    -
    -              if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) {
    -                  return GIF_ERROR;
    -              }
    -              sp->RasterBits = (unsigned char *)reallocarray(NULL, ImageSize,
    -                      sizeof(GifPixelType));
    -
    -              if (sp->RasterBits == NULL) {
    -                  return GIF_ERROR;
    -              }
    -
    -              if (sp->ImageDesc.Interlace) {
    -                  int i, j;
    -                   /*
    -                    * The way an interlaced image should be read -
    -                    * offsets and jumps...
    -                    */
    -                  int InterlacedOffset[] = { 0, 4, 2, 1 };
    -                  int InterlacedJumps[] = { 8, 8, 4, 2 };
    -                  /* Need to perform 4 passes on the image */
    -                  for (i = 0; i < 4; i++)
    -                      for (j = InterlacedOffset[i];
    -                       j < sp->ImageDesc.Height;
    -                       j += InterlacedJumps[i]) {
    -                      if (DGifGetLine(GifFile,
    -                              sp->RasterBits+j*sp->ImageDesc.Width,
    -                              sp->ImageDesc.Width) == GIF_ERROR)
    -                          return GIF_ERROR;
    -                      }
    -              }
    -              else {
    -                  if (DGifGetLine(GifFile,sp->RasterBits,ImageSize)==GIF_ERROR)
    -                      return (GIF_ERROR);
    -              }
    -
    -              if (GifFile->ExtensionBlocks) {
    -                  sp->ExtensionBlocks = GifFile->ExtensionBlocks;
    -                  sp->ExtensionBlockCount = GifFile->ExtensionBlockCount;
    -
    -                  GifFile->ExtensionBlocks = NULL;
    -                  GifFile->ExtensionBlockCount = 0;
    -              }
    -              break;
    -
    -          case EXTENSION_RECORD_TYPE:
    -              if (DGifGetExtension(GifFile,&ExtFunction,&ExtData) == GIF_ERROR)
    -                  return (GIF_ERROR);
    -              /* Create an extension block with our data */
    -              if (ExtData != NULL) {
    -                  if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount,
    -                               &GifFile->ExtensionBlocks,
    -                               ExtFunction, ExtData[0], &ExtData[1])
    -                      == GIF_ERROR)
    -                      return (GIF_ERROR);
    -              }
    -              for (;;) {
    -                  if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR)
    -                      return (GIF_ERROR);
    -                  if (ExtData == NULL)
    -                      break;
    -                  /* Continue the extension block */
    -                  if (ExtData != NULL)
    -                      if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount,
    -                                   &GifFile->ExtensionBlocks,
    -                                   CONTINUE_EXT_FUNC_CODE,
    -                                   ExtData[0], &ExtData[1]) == GIF_ERROR)
    -                              return (GIF_ERROR);
    -              }
    -              break;
    -
    -          case TERMINATE_RECORD_TYPE:
    -              break;
    -
    -          default:    /* Should be trapped by DGifGetRecordType */
    -              break;
    +        case IMAGE_DESC_RECORD_TYPE:
    +            if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
    +                return (GIF_ERROR);
    +            }
    +
    +            sp = &GifFile->SavedImages[GifFile->ImageCount - 1];
    +            /* Allocate memory for the image */
    +            if (sp->ImageDesc.Width <= 0 ||
    +                sp->ImageDesc.Height <= 0 ||
    +                sp->ImageDesc.Width >
    +                    (INT_MAX / sp->ImageDesc.Height)) {
    +                DGifDecreaseImageCounter(GifFile);
    +                return GIF_ERROR;
    +            }
    +            ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;
    +
    +            if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) {
    +                DGifDecreaseImageCounter(GifFile);
    +                return GIF_ERROR;
    +            }
    +            sp->RasterBits = (unsigned char *)reallocarray(
    +                NULL, ImageSize, sizeof(GifPixelType));
    +
    +            if (sp->RasterBits == NULL) {
    +                DGifDecreaseImageCounter(GifFile);
    +                return GIF_ERROR;
    +            }
    +
    +            if (sp->ImageDesc.Interlace) {
    +                int i, j;
    +                /*
    +                 * The way an interlaced image should be read -
    +                 * offsets and jumps...
    +                 */
    +                static const int InterlacedOffset[] = {0, 4, 2,
    +                                                       1};
    +                static const int InterlacedJumps[] = {8, 8, 4,
    +                                                      2};
    +                /* Need to perform 4 passes on the image */
    +                for (i = 0; i < 4; i++) {
    +                    for (j = InterlacedOffset[i];
    +                         j < sp->ImageDesc.Height;
    +                         j += InterlacedJumps[i]) {
    +                        if (DGifGetLine(
    +                                GifFile,
    +                                sp->RasterBits +
    +                                    j * sp->ImageDesc
    +                                            .Width,
    +                                sp->ImageDesc.Width) ==
    +                            GIF_ERROR) {
    +                            DGifDecreaseImageCounter(
    +                                GifFile);
    +                            return GIF_ERROR;
    +                        }
    +                    }
    +                }
    +            } else {
    +                if (DGifGetLine(GifFile, sp->RasterBits,
    +                                ImageSize) == GIF_ERROR) {
    +                    DGifDecreaseImageCounter(GifFile);
    +                    return GIF_ERROR;
    +                }
    +            }
    +
    +            if (GifFile->ExtensionBlocks) {
    +                sp->ExtensionBlocks = GifFile->ExtensionBlocks;
    +                sp->ExtensionBlockCount =
    +                    GifFile->ExtensionBlockCount;
    +
    +                GifFile->ExtensionBlocks = NULL;
    +                GifFile->ExtensionBlockCount = 0;
    +            }
    +            break;
    +
    +        case EXTENSION_RECORD_TYPE:
    +            if (DGifGetExtension(GifFile, &ExtFunction, &ExtData) ==
    +                GIF_ERROR) {
    +                return (GIF_ERROR);
    +            }
    +            /* Create an extension block with our data */
    +            if (ExtData != NULL) {
    +                if (GifAddExtensionBlock(
    +                        &GifFile->ExtensionBlockCount,
    +                        &GifFile->ExtensionBlocks, ExtFunction,
    +                        ExtData[0], &ExtData[1]) == GIF_ERROR) {
    +                    return (GIF_ERROR);
    +                }
    +            }
    +            for (;;) {
    +                if (DGifGetExtensionNext(GifFile, &ExtData) ==
    +                    GIF_ERROR) {
    +                    return (GIF_ERROR);
    +                }
    +                if (ExtData == NULL) {
    +                    break;
    +                }
    +                /* Continue the extension block */
    +                if (GifAddExtensionBlock(
    +                        &GifFile->ExtensionBlockCount,
    +                        &GifFile->ExtensionBlocks,
    +                        CONTINUE_EXT_FUNC_CODE, ExtData[0],
    +                        &ExtData[1]) == GIF_ERROR) {
    +                    return (GIF_ERROR);
    +                }
    +            }
    +            break;
    +
    +        case TERMINATE_RECORD_TYPE:
    +            break;
    +
    +        default: /* Should be trapped by DGifGetRecordType */
    +            break;
             }
         } while (RecordType != TERMINATE_RECORD_TYPE);
     
         /* Sanity check for corrupted file */
         if (GifFile->ImageCount == 0) {
             GifFile->Error = D_GIF_ERR_NO_IMAG_DSCR;
    -        return(GIF_ERROR);
    +        return (GIF_ERROR);
         }
     
         return (GIF_OK);
    diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gif_err.c b/src/java.desktop/share/native/libsplashscreen/giflib/gif_err.c
    index db08838efff84..3b6785f7c6323 100644
    --- a/src/java.desktop/share/native/libsplashscreen/giflib/gif_err.c
    +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gif_err.c
    @@ -38,82 +38,80 @@ SPDX-License-Identifier: MIT
     /*****************************************************************************
      Return a string description of  the last GIF error
     *****************************************************************************/
    -const char *
    -GifErrorString(int ErrorCode)
    -{
    +const char *GifErrorString(int ErrorCode) {
         const char *Err;
     
         switch (ErrorCode) {
    -      case E_GIF_ERR_OPEN_FAILED:
    +    case E_GIF_ERR_OPEN_FAILED:
             Err = "Failed to open given file";
             break;
    -      case E_GIF_ERR_WRITE_FAILED:
    +    case E_GIF_ERR_WRITE_FAILED:
             Err = "Failed to write to given file";
             break;
    -      case E_GIF_ERR_HAS_SCRN_DSCR:
    +    case E_GIF_ERR_HAS_SCRN_DSCR:
             Err = "Screen descriptor has already been set";
             break;
    -      case E_GIF_ERR_HAS_IMAG_DSCR:
    +    case E_GIF_ERR_HAS_IMAG_DSCR:
             Err = "Image descriptor is still active";
             break;
    -      case E_GIF_ERR_NO_COLOR_MAP:
    +    case E_GIF_ERR_NO_COLOR_MAP:
             Err = "Neither global nor local color map";
             break;
    -      case E_GIF_ERR_DATA_TOO_BIG:
    +    case E_GIF_ERR_DATA_TOO_BIG:
             Err = "Number of pixels bigger than width * height";
             break;
    -      case E_GIF_ERR_NOT_ENOUGH_MEM:
    +    case E_GIF_ERR_NOT_ENOUGH_MEM:
             Err = "Failed to allocate required memory";
             break;
    -      case E_GIF_ERR_DISK_IS_FULL:
    +    case E_GIF_ERR_DISK_IS_FULL:
             Err = "Write failed (disk full?)";
             break;
    -      case E_GIF_ERR_CLOSE_FAILED:
    +    case E_GIF_ERR_CLOSE_FAILED:
             Err = "Failed to close given file";
             break;
    -      case E_GIF_ERR_NOT_WRITEABLE:
    +    case E_GIF_ERR_NOT_WRITEABLE:
             Err = "Given file was not opened for write";
             break;
    -      case D_GIF_ERR_OPEN_FAILED:
    +    case D_GIF_ERR_OPEN_FAILED:
             Err = "Failed to open given file";
             break;
    -      case D_GIF_ERR_READ_FAILED:
    +    case D_GIF_ERR_READ_FAILED:
             Err = "Failed to read from given file";
             break;
    -      case D_GIF_ERR_NOT_GIF_FILE:
    +    case D_GIF_ERR_NOT_GIF_FILE:
             Err = "Data is not in GIF format";
             break;
    -      case D_GIF_ERR_NO_SCRN_DSCR:
    +    case D_GIF_ERR_NO_SCRN_DSCR:
             Err = "No screen descriptor detected";
             break;
    -      case D_GIF_ERR_NO_IMAG_DSCR:
    +    case D_GIF_ERR_NO_IMAG_DSCR:
             Err = "No Image Descriptor detected";
             break;
    -      case D_GIF_ERR_NO_COLOR_MAP:
    +    case D_GIF_ERR_NO_COLOR_MAP:
             Err = "Neither global nor local color map";
             break;
    -      case D_GIF_ERR_WRONG_RECORD:
    +    case D_GIF_ERR_WRONG_RECORD:
             Err = "Wrong record type detected";
             break;
    -      case D_GIF_ERR_DATA_TOO_BIG:
    +    case D_GIF_ERR_DATA_TOO_BIG:
             Err = "Number of pixels bigger than width * height";
             break;
    -      case D_GIF_ERR_NOT_ENOUGH_MEM:
    +    case D_GIF_ERR_NOT_ENOUGH_MEM:
             Err = "Failed to allocate required memory";
             break;
    -      case D_GIF_ERR_CLOSE_FAILED:
    +    case D_GIF_ERR_CLOSE_FAILED:
             Err = "Failed to close given file";
             break;
    -      case D_GIF_ERR_NOT_READABLE:
    +    case D_GIF_ERR_NOT_READABLE:
             Err = "Given file was not opened for read";
             break;
    -      case D_GIF_ERR_IMAGE_DEFECT:
    +    case D_GIF_ERR_IMAGE_DEFECT:
             Err = "Image is defective, decoding aborted";
             break;
    -      case D_GIF_ERR_EOF_TOO_SOON:
    +    case D_GIF_ERR_EOF_TOO_SOON:
             Err = "Image EOF detected before image complete";
             break;
    -      default:
    +    default:
             Err = NULL;
             break;
         }
    diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gif_hash.h b/src/java.desktop/share/native/libsplashscreen/giflib/gif_hash.h
    index 6cabd0866ed1e..bd00af64161db 100644
    --- a/src/java.desktop/share/native/libsplashscreen/giflib/gif_hash.h
    +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gif_hash.h
    @@ -33,27 +33,25 @@ SPDX-License-Identifier: MIT
     #ifndef _GIF_HASH_H_
     #define _GIF_HASH_H_
     
    -/** Begin JDK modifications to support building on Windows **/
     #ifndef _WIN32
     #include 
    -#endif
    -/** End JDK modifications to support building on Windows **/
    +#endif /* _WIN32 */
     #include 
     
    -#define HT_SIZE         8192    /* 12bits = 4096 or twice as big! */
    -#define HT_KEY_MASK     0x1FFF  /* 13bits keys */
    -#define HT_KEY_NUM_BITS 13      /* 13bits keys */
    -#define HT_MAX_KEY      8191    /* 13bits - 1, maximal code possible */
    -#define HT_MAX_CODE     4095    /* Biggest code possible in 12 bits. */
    +#define HT_SIZE 8192       /* 12bits = 4096 or twice as big! */
    +#define HT_KEY_MASK 0x1FFF /* 13bits keys */
    +#define HT_KEY_NUM_BITS 13 /* 13bits keys */
    +#define HT_MAX_KEY 8191    /* 13bits - 1, maximal code possible */
    +#define HT_MAX_CODE 4095   /* Biggest code possible in 12 bits. */
     
     /* The 32 bits of the long are divided into two parts for the key & code:   */
     /* 1. The code is 12 bits as our compression algorithm is limited to 12bits */
    -/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits.           */
    +/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits.        */
     /* The key is the upper 20 bits.  The code is the lower 12. */
    -#define HT_GET_KEY(l)    (l >> 12)
    -#define HT_GET_CODE(l)   (l & 0x0FFF)
    -#define HT_PUT_KEY(l)    (l << 12)
    -#define HT_PUT_CODE(l)   (l & 0x0FFF)
    +#define HT_GET_KEY(l) (l >> 12)
    +#define HT_GET_CODE(l) (l & 0x0FFF)
    +#define HT_PUT_KEY(l) (l << 12)
    +#define HT_PUT_CODE(l) (l & 0x0FFF)
     
     typedef struct GifHashTableType {
         uint32_t HTable[HT_SIZE];
    diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h b/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h
    index f739b36adfd0f..74a2e969c0dea 100644
    --- a/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h
    +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h
    @@ -39,27 +39,19 @@ extern "C" {
     
     #define GIFLIB_MAJOR 5
     #define GIFLIB_MINOR 2
    -#define GIFLIB_RELEASE 1
    +#define GIFLIB_RELEASE 2
     
    -#define GIF_ERROR   0
    -#define GIF_OK      1
    +#define GIF_ERROR 0
    +#define GIF_OK 1
     
    +#include 
     #include 
    -/** Begin JDK modifications to support building using old compilers**/
    -//#include 
    -#ifdef bool
    -#undef bool
    -#endif
    -typedef int bool;
    -#define false 0
    -#define true 1
    -/** End JDK modifications to support building using old compilers**/
    -
    -#define GIF_STAMP "GIFVER"          /* First chars in file - GIF stamp.  */
    +
    +#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp.  */
     #define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1
    -#define GIF_VERSION_POS 3           /* Version first character in stamp. */
    -#define GIF87_STAMP "GIF87a"        /* First chars in file - GIF stamp.  */
    -#define GIF89_STAMP "GIF89a"        /* First chars in file - GIF stamp.  */
    +#define GIF_VERSION_POS 3    /* Version first character in stamp. */
    +#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp.  */
    +#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp.  */
     
     typedef unsigned char GifPixelType;
     typedef unsigned char *GifRowType;
    @@ -75,24 +67,24 @@ typedef struct ColorMapObject {
         int ColorCount;
         int BitsPerPixel;
         bool SortFlag;
    -    GifColorType *Colors;    /* on malloc(3) heap */
    +    GifColorType *Colors; /* on malloc(3) heap */
     } ColorMapObject;
     
     typedef struct GifImageDesc {
    -    GifWord Left, Top, Width, Height;   /* Current image dimensions. */
    -    bool Interlace;                     /* Sequential/Interlaced lines. */
    -    ColorMapObject *ColorMap;           /* The local color map */
    +    GifWord Left, Top, Width, Height; /* Current image dimensions. */
    +    bool Interlace;                   /* Sequential/Interlaced lines. */
    +    ColorMapObject *ColorMap;         /* The local color map */
     } GifImageDesc;
     
     typedef struct ExtensionBlock {
         int ByteCount;
    -    GifByteType *Bytes; /* on malloc(3) heap */
    -    int Function;       /* The block function code */
    -#define CONTINUE_EXT_FUNC_CODE    0x00    /* continuation subblock */
    -#define COMMENT_EXT_FUNC_CODE     0xfe    /* comment */
    -#define GRAPHICS_EXT_FUNC_CODE    0xf9    /* graphics control (GIF89) */
    -#define PLAINTEXT_EXT_FUNC_CODE   0x01    /* plaintext */
    -#define APPLICATION_EXT_FUNC_CODE 0xff    /* application block (GIF89) */
    +    GifByteType *Bytes;            /* on malloc(3) heap */
    +    int Function;                  /* The block function code */
    +#define CONTINUE_EXT_FUNC_CODE 0x00    /* continuation subblock */
    +#define COMMENT_EXT_FUNC_CODE 0xfe     /* comment */
    +#define GRAPHICS_EXT_FUNC_CODE 0xf9    /* graphics control (GIF89) */
    +#define PLAINTEXT_EXT_FUNC_CODE 0x01   /* plaintext */
    +#define APPLICATION_EXT_FUNC_CODE 0xff /* application block (GIF89) */
     } ExtensionBlock;
     
     typedef struct SavedImage {
    @@ -103,22 +95,22 @@ typedef struct SavedImage {
     } SavedImage;
     
     typedef struct GifFileType {
    -    GifWord SWidth, SHeight;         /* Size of virtual canvas */
    -    GifWord SColorResolution;        /* How many colors can we generate? */
    -    GifWord SBackGroundColor;        /* Background color for virtual canvas */
    -    GifByteType AspectByte;          /* Used to compute pixel aspect ratio */
    -    ColorMapObject *SColorMap;       /* Global colormap, NULL if nonexistent. */
    -    int ImageCount;                  /* Number of current image (both APIs) */
    -    GifImageDesc Image;              /* Current image (low-level API) */
    -    SavedImage *SavedImages;         /* Image sequence (high-level API) */
    -    int ExtensionBlockCount;         /* Count extensions past last image */
    +    GifWord SWidth, SHeight;   /* Size of virtual canvas */
    +    GifWord SColorResolution;  /* How many colors can we generate? */
    +    GifWord SBackGroundColor;  /* Background color for virtual canvas */
    +    GifByteType AspectByte;    /* Used to compute pixel aspect ratio */
    +    ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */
    +    int ImageCount;            /* Number of current image (both APIs) */
    +    GifImageDesc Image;        /* Current image (low-level API) */
    +    SavedImage *SavedImages;   /* Image sequence (high-level API) */
    +    int ExtensionBlockCount;   /* Count extensions past last image */
         ExtensionBlock *ExtensionBlocks; /* Extensions past last image */
         int Error;                       /* Last error condition reported */
         void *UserData;                  /* hook to attach user data (TVT) */
         void *Private;                   /* Don't mess with this! */
     } GifFileType;
     
    -#define GIF_ASPECT_RATIO(n)    ((n)+15.0/64.0)
    +#define GIF_ASPECT_RATIO(n) ((n) + 15.0 / 64.0)
     
     typedef enum {
         UNDEFINED_RECORD_TYPE,
    @@ -129,12 +121,12 @@ typedef enum {
     } GifRecordType;
     
     /* func type to read gif data from arbitrary sources (TVT) */
    -typedef int (*InputFunc) (GifFileType *, GifByteType *, int);
    +typedef int (*InputFunc)(GifFileType *, GifByteType *, int);
     
     /* func type to write gif data to arbitrary targets.
      * Returns count of bytes written. (MRB)
      */
    -typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int);
    +typedef int (*OutputFunc)(GifFileType *, const GifByteType *, int);
     
     /******************************************************************************
      GIF89 structures
    @@ -142,14 +134,14 @@ typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int);
     
     typedef struct GraphicsControlBlock {
         int DisposalMode;
    -#define DISPOSAL_UNSPECIFIED      0       /* No disposal specified. */
    -#define DISPOSE_DO_NOT            1       /* Leave image in place */
    -#define DISPOSE_BACKGROUND        2       /* Set area too background color */
    -#define DISPOSE_PREVIOUS          3       /* Restore to previous content */
    -    bool UserInputFlag;      /* User confirmation required before disposal */
    -    int DelayTime;           /* pre-display delay in 0.01sec units */
    -    int TransparentColor;    /* Palette index for transparency, -1 if none */
    -#define NO_TRANSPARENT_COLOR    -1
    +#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */
    +#define DISPOSE_DO_NOT 1       /* Leave image in place */
    +#define DISPOSE_BACKGROUND 2   /* Set area too background color */
    +#define DISPOSE_PREVIOUS 3     /* Restore to previous content */
    +    bool UserInputFlag;    /* User confirmation required before disposal */
    +    int DelayTime;         /* pre-display delay in 0.01sec units */
    +    int TransparentColor;  /* Palette index for transparency, -1 if none */
    +#define NO_TRANSPARENT_COLOR -1
     } GraphicsControlBlock;
     
     /******************************************************************************
    @@ -161,49 +153,44 @@ GifFileType *EGifOpenFileName(const char *GifFileName,
                                   const bool GifTestExistence, int *Error);
     GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error);
     GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error);
    -int EGifSpew(GifFileType * GifFile);
    +int EGifSpew(GifFileType *GifFile);
     const char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */
     int EGifCloseFile(GifFileType *GifFile, int *ErrorCode);
     
    -#define E_GIF_SUCCEEDED          0
    -#define E_GIF_ERR_OPEN_FAILED    1    /* And EGif possible errors. */
    -#define E_GIF_ERR_WRITE_FAILED   2
    -#define E_GIF_ERR_HAS_SCRN_DSCR  3
    -#define E_GIF_ERR_HAS_IMAG_DSCR  4
    -#define E_GIF_ERR_NO_COLOR_MAP   5
    -#define E_GIF_ERR_DATA_TOO_BIG   6
    +#define E_GIF_SUCCEEDED 0
    +#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */
    +#define E_GIF_ERR_WRITE_FAILED 2
    +#define E_GIF_ERR_HAS_SCRN_DSCR 3
    +#define E_GIF_ERR_HAS_IMAG_DSCR 4
    +#define E_GIF_ERR_NO_COLOR_MAP 5
    +#define E_GIF_ERR_DATA_TOO_BIG 6
     #define E_GIF_ERR_NOT_ENOUGH_MEM 7
    -#define E_GIF_ERR_DISK_IS_FULL   8
    -#define E_GIF_ERR_CLOSE_FAILED   9
    -#define E_GIF_ERR_NOT_WRITEABLE  10
    +#define E_GIF_ERR_DISK_IS_FULL 8
    +#define E_GIF_ERR_CLOSE_FAILED 9
    +#define E_GIF_ERR_NOT_WRITEABLE 10
     
     /* These are legacy.  You probably do not want to call them directly */
    -int EGifPutScreenDesc(GifFileType *GifFile,
    -                      const int GifWidth, const int GifHeight,
    -                      const int GifColorRes,
    +int EGifPutScreenDesc(GifFileType *GifFile, const int GifWidth,
    +                      const int GifHeight, const int GifColorRes,
                           const int GifBackGround,
                           const ColorMapObject *GifColorMap);
    -int EGifPutImageDesc(GifFileType *GifFile,
    -                     const int GifLeft, const int GifTop,
    +int EGifPutImageDesc(GifFileType *GifFile, const int GifLeft, const int GifTop,
                          const int GifWidth, const int GifHeight,
                          const bool GifInterlace,
                          const ColorMapObject *GifColorMap);
     void EGifSetGifVersion(GifFileType *GifFile, const bool gif89);
    -int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine,
    -                int GifLineLen);
    +int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen);
     int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel);
     int EGifPutComment(GifFileType *GifFile, const char *GifComment);
     int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode);
    -int EGifPutExtensionBlock(GifFileType *GifFile,
    -                         const int GifExtLen, const void *GifExtension);
    +int EGifPutExtensionBlock(GifFileType *GifFile, const int GifExtLen,
    +                          const void *GifExtension);
     int EGifPutExtensionTrailer(GifFileType *GifFile);
     int EGifPutExtension(GifFileType *GifFile, const int GifExtCode,
    -                     const int GifExtLen,
    -                     const void *GifExtension);
    +                     const int GifExtLen, const void *GifExtension);
     int EGifPutCode(GifFileType *GifFile, int GifCodeSize,
                     const GifByteType *GifCodeBlock);
    -int EGifPutCodeNext(GifFileType *GifFile,
    -                    const GifByteType *GifCodeBlock);
    +int EGifPutCodeNext(GifFileType *GifFile, const GifByteType *GifCodeBlock);
     
     /******************************************************************************
      GIF decoding routines
    @@ -212,24 +199,25 @@ int EGifPutCodeNext(GifFileType *GifFile,
     /* Main entry points */
     GifFileType *DGifOpenFileName(const char *GifFileName, int *Error);
     GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error);
    -int DGifSlurp(GifFileType * GifFile);
    -GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error);    /* new one (TVT) */
    -    int DGifCloseFile(GifFileType * GifFile, int *ErrorCode);
    -
    -#define D_GIF_SUCCEEDED          0
    -#define D_GIF_ERR_OPEN_FAILED    101    /* And DGif possible errors. */
    -#define D_GIF_ERR_READ_FAILED    102
    -#define D_GIF_ERR_NOT_GIF_FILE   103
    -#define D_GIF_ERR_NO_SCRN_DSCR   104
    -#define D_GIF_ERR_NO_IMAG_DSCR   105
    -#define D_GIF_ERR_NO_COLOR_MAP   106
    -#define D_GIF_ERR_WRONG_RECORD   107
    -#define D_GIF_ERR_DATA_TOO_BIG   108
    +int DGifSlurp(GifFileType *GifFile);
    +GifFileType *DGifOpen(void *userPtr, InputFunc readFunc,
    +                      int *Error); /* new one (TVT) */
    +int DGifCloseFile(GifFileType *GifFile, int *ErrorCode);
    +
    +#define D_GIF_SUCCEEDED 0
    +#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */
    +#define D_GIF_ERR_READ_FAILED 102
    +#define D_GIF_ERR_NOT_GIF_FILE 103
    +#define D_GIF_ERR_NO_SCRN_DSCR 104
    +#define D_GIF_ERR_NO_IMAG_DSCR 105
    +#define D_GIF_ERR_NO_COLOR_MAP 106
    +#define D_GIF_ERR_WRONG_RECORD 107
    +#define D_GIF_ERR_DATA_TOO_BIG 108
     #define D_GIF_ERR_NOT_ENOUGH_MEM 109
    -#define D_GIF_ERR_CLOSE_FAILED   110
    -#define D_GIF_ERR_NOT_READABLE   111
    -#define D_GIF_ERR_IMAGE_DEFECT   112
    -#define D_GIF_ERR_EOF_TOO_SOON   113
    +#define D_GIF_ERR_CLOSE_FAILED 110
    +#define D_GIF_ERR_NOT_READABLE 111
    +#define D_GIF_ERR_IMAGE_DEFECT 112
    +#define D_GIF_ERR_EOF_TOO_SOON 113
     
     /* These are legacy.  You probably do not want to call them directly */
     int DGifGetScreenDesc(GifFileType *GifFile);
    @@ -247,11 +235,10 @@ int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock);
     int DGifGetLZCodes(GifFileType *GifFile, int *GifCode);
     const char *DGifGetGifVersion(GifFileType *GifFile);
     
    -
     /******************************************************************************
      Error handling and reporting.
     ******************************************************************************/
    -extern const char *GifErrorString(int ErrorCode);     /* new in 2012 - ESR */
    +extern const char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */
     
     /*****************************************************************************
      Everything below this point is new after version 1.2, supporting `slurp
    @@ -263,26 +250,26 @@ extern const char *GifErrorString(int ErrorCode);     /* new in 2012 - ESR */
     ******************************************************************************/
     
     extern ColorMapObject *GifMakeMapObject(int ColorCount,
    -                                     const GifColorType *ColorMap);
    +                                        const GifColorType *ColorMap);
     extern void GifFreeMapObject(ColorMapObject *Object);
     extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1,
    -                                     const ColorMapObject *ColorIn2,
    -                                     GifPixelType ColorTransIn2[]);
    +                                        const ColorMapObject *ColorIn2,
    +                                        GifPixelType ColorTransIn2[]);
     extern int GifBitSize(int n);
     
     /******************************************************************************
      Support for the in-core structures allocation (slurp mode).
     ******************************************************************************/
     
    -extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]);
    +extern void GifApplyTranslation(SavedImage *Image,
    +                                const GifPixelType Translation[]);
     extern int GifAddExtensionBlock(int *ExtensionBlock_Count,
    -                                ExtensionBlock **ExtensionBlocks,
    -                                int Function,
    +                                ExtensionBlock **ExtensionBlocks, int Function,
                                     unsigned int Len, unsigned char ExtData[]);
     extern void GifFreeExtensions(int *ExtensionBlock_Count,
                                   ExtensionBlock **ExtensionBlocks);
     extern SavedImage *GifMakeSavedImage(GifFileType *GifFile,
    -                                  const SavedImage *CopyFrom);
    +                                     const SavedImage *CopyFrom);
     extern void GifFreeSavedImages(GifFileType *GifFile);
     
     /******************************************************************************
    @@ -295,37 +282,31 @@ int DGifExtensionToGCB(const size_t GifExtensionLength,
     size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
                               GifByteType *GifExtension);
     
    -int DGifSavedExtensionToGCB(GifFileType *GifFile,
    -                            int ImageIndex,
    +int DGifSavedExtensionToGCB(GifFileType *GifFile, int ImageIndex,
                                 GraphicsControlBlock *GCB);
     int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
    -                            GifFileType *GifFile,
    -                            int ImageIndex);
    +                            GifFileType *GifFile, int ImageIndex);
     
     /******************************************************************************
      The library's internal utility font
     ******************************************************************************/
     
    -#define GIF_FONT_WIDTH  8
    +#define GIF_FONT_WIDTH 8
     #define GIF_FONT_HEIGHT 8
     extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH];
     
    -extern void GifDrawText8x8(SavedImage *Image,
    -                     const int x, const int y,
    -                     const char *legend, const int color);
    +extern void GifDrawText8x8(SavedImage *Image, const int x, const int y,
    +                           const char *legend, const int color);
     
    -extern void GifDrawBox(SavedImage *Image,
    -                    const int x, const int y,
    -                    const int w, const int d, const int color);
    +extern void GifDrawBox(SavedImage *Image, const int x, const int y, const int w,
    +                       const int d, const int color);
     
    -extern void GifDrawRectangle(SavedImage *Image,
    -                   const int x, const int y,
    -                   const int w, const int d, const int color);
    +extern void GifDrawRectangle(SavedImage *Image, const int x, const int y,
    +                             const int w, const int d, const int color);
     
    -extern void GifDrawBoxedText8x8(SavedImage *Image,
    -                          const int x, const int y,
    -                          const char *legend,
    -                          const int border, const int bg, const int fg);
    +extern void GifDrawBoxedText8x8(SavedImage *Image, const int x, const int y,
    +                                const char *legend, const int border,
    +                                const int bg, const int fg);
     
     #ifdef __cplusplus
     }
    diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib_private.h b/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib_private.h
    index 4f832676ffcd5..f905e0d7b4809 100644
    --- a/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib_private.h
    +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib_private.h
    @@ -33,52 +33,54 @@ SPDX-License-Identifier: MIT
     #ifndef _GIF_LIB_PRIVATE_H
     #define _GIF_LIB_PRIVATE_H
     
    -#include "gif_lib.h"
     #include "gif_hash.h"
    +#include "gif_lib.h"
     
     #ifndef SIZE_MAX
    -    #define SIZE_MAX     UINTPTR_MAX
    +#define SIZE_MAX UINTPTR_MAX
     #endif
     
    -#define EXTENSION_INTRODUCER      0x21
    -#define DESCRIPTOR_INTRODUCER     0x2c
    -#define TERMINATOR_INTRODUCER     0x3b
    +#define EXTENSION_INTRODUCER 0x21
    +#define DESCRIPTOR_INTRODUCER 0x2c
    +#define TERMINATOR_INTRODUCER 0x3b
     
    -#define LZ_MAX_CODE         4095    /* Biggest code possible in 12 bits. */
    -#define LZ_BITS             12
    +#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
    +#define LZ_BITS 12
     
    -#define FLUSH_OUTPUT        4096    /* Impossible code, to signal flush. */
    -#define FIRST_CODE          4097    /* Impossible code, to signal first. */
    -#define NO_SUCH_CODE        4098    /* Impossible code, to signal empty. */
    +#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */
    +#define FIRST_CODE 4097   /* Impossible code, to signal first. */
    +#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */
     
    -#define FILE_STATE_WRITE    0x01
    -#define FILE_STATE_SCREEN   0x02
    -#define FILE_STATE_IMAGE    0x04
    -#define FILE_STATE_READ     0x08
    +#define FILE_STATE_WRITE 0x01
    +#define FILE_STATE_SCREEN 0x02
    +#define FILE_STATE_IMAGE 0x04
    +#define FILE_STATE_READ 0x08
     
    -#define IS_READABLE(Private)    (Private->FileState & FILE_STATE_READ)
    -#define IS_WRITEABLE(Private)   (Private->FileState & FILE_STATE_WRITE)
    +#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ)
    +#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE)
     
     typedef struct GifFilePrivateType {
    -    GifWord FileState, FileHandle,  /* Where all this data goes to! */
    -      BitsPerPixel,     /* Bits per pixel (Codes uses at least this + 1). */
    -      ClearCode,   /* The CLEAR LZ code. */
    -      EOFCode,     /* The EOF LZ code. */
    -      RunningCode, /* The next code algorithm can generate. */
    -      RunningBits, /* The number of bits required to represent RunningCode. */
    -      MaxCode1,    /* 1 bigger than max. possible code, in RunningBits bits. */
    -      LastCode,    /* The code before the current code. */
    -      CrntCode,    /* Current algorithm code. */
    -      StackPtr,    /* For character stack (see below). */
    -      CrntShiftState;    /* Number of bits in CrntShiftDWord. */
    -    unsigned long CrntShiftDWord;   /* For bytes decomposition into codes. */
    -    unsigned long PixelCount;   /* Number of pixels in image. */
    -    FILE *File;    /* File as stream. */
    -    InputFunc Read;     /* function to read gif input (TVT) */
    -    OutputFunc Write;   /* function to write gif output (MRB) */
    -    GifByteType Buf[256];   /* Compressed input is buffered here. */
    +    GifWord FileState, FileHandle, /* Where all this data goes to! */
    +        BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */
    +        ClearCode,    /* The CLEAR LZ code. */
    +        EOFCode,      /* The EOF LZ code. */
    +        RunningCode,  /* The next code algorithm can generate. */
    +        RunningBits,  /* The number of bits required to represent
    +                         RunningCode. */
    +        MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits.
    +                   */
    +        LastCode, /* The code before the current code. */
    +        CrntCode, /* Current algorithm code. */
    +        StackPtr, /* For character stack (see below). */
    +        CrntShiftState;           /* Number of bits in CrntShiftDWord. */
    +    unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */
    +    unsigned long PixelCount;     /* Number of pixels in image. */
    +    FILE *File;                   /* File as stream. */
    +    InputFunc Read;               /* function to read gif input (TVT) */
    +    OutputFunc Write;             /* function to write gif output (MRB) */
    +    GifByteType Buf[256];         /* Compressed input is buffered here. */
         GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */
    -    GifByteType Suffix[LZ_MAX_CODE + 1];    /* So we can trace the codes. */
    +    GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */
         GifPrefixType Prefix[LZ_MAX_CODE + 1];
         GifHashTableType *HashTable;
         bool gif89;
    diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c b/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c
    index 75b74b4fba0a2..5aef304455813 100644
    --- a/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c
    +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c
    @@ -30,59 +30,59 @@ SPDX-License-Identifier: MIT
     
     ****************************************************************************/
     
    -#include 
     #include 
    +#include 
     #include 
     
     #include "gif_lib.h"
     #include "gif_lib_private.h"
     
    -#define MAX(x, y)    (((x) > (y)) ? (x) : (y))
    +#define MAX(x, y) (((x) > (y)) ? (x) : (y))
     
     /******************************************************************************
      Miscellaneous utility functions
     ******************************************************************************/
     
     /* return smallest bitfield size n will fit in */
    -int
    -GifBitSize(int n)
    -{
    +int GifBitSize(int n) {
         register int i;
     
    -    for (i = 1; i <= 8; i++)
    -        if ((1 << i) >= n)
    +    for (i = 1; i <= 8; i++) {
    +        if ((1 << i) >= n) {
                 break;
    +        }
    +    }
         return (i);
     }
     
     /******************************************************************************
    -  Color map object functions
    + Color map object functions
     ******************************************************************************/
     
     /*
      * Allocate a color map of given size; initialize with contents of
      * ColorMap if that pointer is non-NULL.
      */
    -ColorMapObject *
    -GifMakeMapObject(int ColorCount, const GifColorType *ColorMap)
    -{
    +ColorMapObject *GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) {
         ColorMapObject *Object;
     
         /*** FIXME: Our ColorCount has to be a power of two.  Is it necessary to
    -     * make the user know that or should we automatically round up instead? */
    +     * make the user know that or should we automatically round up instead?
    +     */
         if (ColorCount != (1 << GifBitSize(ColorCount))) {
    -        return ((ColorMapObject *) NULL);
    +        return ((ColorMapObject *)NULL);
         }
     
         Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
    -    if (Object == (ColorMapObject *) NULL) {
    -        return ((ColorMapObject *) NULL);
    +    if (Object == (ColorMapObject *)NULL) {
    +        return ((ColorMapObject *)NULL);
         }
     
    -    Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
    -    if (Object->Colors == (GifColorType *) NULL) {
    +    Object->Colors =
    +        (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
    +    if (Object->Colors == (GifColorType *)NULL) {
             free(Object);
    -        return ((ColorMapObject *) NULL);
    +        return ((ColorMapObject *)NULL);
         }
     
         Object->ColorCount = ColorCount;
    @@ -90,19 +90,17 @@ GifMakeMapObject(int ColorCount, const GifColorType *ColorMap)
         Object->SortFlag = false;
     
         if (ColorMap != NULL) {
    -        memcpy((char *)Object->Colors,
    -               (char *)ColorMap, ColorCount * sizeof(GifColorType));
    +        memcpy((char *)Object->Colors, (char *)ColorMap,
    +               ColorCount * sizeof(GifColorType));
         }
     
         return (Object);
     }
     
     /*******************************************************************************
    -Free a color map object
    + Free a color map object
     *******************************************************************************/
    -void
    -GifFreeMapObject(ColorMapObject *Object)
    -{
    +void GifFreeMapObject(ColorMapObject *Object) {
         if (Object != NULL) {
             (void)free(Object->Colors);
             (void)free(Object);
    @@ -110,17 +108,14 @@ GifFreeMapObject(ColorMapObject *Object)
     }
     
     #ifdef DEBUG
    -void
    -DumpColorMap(ColorMapObject *Object,
    -             FILE * fp)
    -{
    +void DumpColorMap(ColorMapObject *Object, FILE *fp) {
         if (Object != NULL) {
             int i, j, Len = Object->ColorCount;
     
             for (i = 0; i < Len; i += 4) {
                 for (j = 0; j < 4 && j < Len; j++) {
    -                (void)fprintf(fp, "%3d: %02x %02x %02x   ", i + j,
    -                              Object->Colors[i + j].Red,
    +                (void)fprintf(fp, "%3d: %02x %02x %02x   ",
    +                              i + j, Object->Colors[i + j].Red,
                                   Object->Colors[i + j].Green,
                                   Object->Colors[i + j].Blue);
                 }
    @@ -137,11 +132,9 @@ DumpColorMap(ColorMapObject *Object,
      copied iff they didn't exist before.  ColorTransIn2 maps the old
      ColorIn2 into the ColorUnion color map table./
     *******************************************************************************/
    -ColorMapObject *
    -GifUnionColorMap(const ColorMapObject *ColorIn1,
    -              const ColorMapObject *ColorIn2,
    -              GifPixelType ColorTransIn2[])
    -{
    +ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1,
    +                                 const ColorMapObject *ColorIn2,
    +                                 GifPixelType ColorTransIn2[]) {
         int i, j, CrntSlot, RoundUpTo, NewGifBitSize;
         ColorMapObject *ColorUnion;
     
    @@ -152,17 +145,19 @@ GifUnionColorMap(const ColorMapObject *ColorIn1,
          */
     
         /* Allocate table which will hold the result for sure. */
    -    ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount,
    -                               ColorIn2->ColorCount) * 2, NULL);
    +    ColorUnion = GifMakeMapObject(
    +        MAX(ColorIn1->ColorCount, ColorIn2->ColorCount) * 2, NULL);
     
    -    if (ColorUnion == NULL)
    +    if (ColorUnion == NULL) {
             return (NULL);
    +    }
     
         /*
          * Copy ColorIn1 to ColorUnion.
          */
    -    for (i = 0; i < ColorIn1->ColorCount; i++)
    +    for (i = 0; i < ColorIn1->ColorCount; i++) {
             ColorUnion->Colors[i] = ColorIn1->Colors[i];
    +    }
         CrntSlot = ColorIn1->ColorCount;
     
         /*
    @@ -172,22 +167,25 @@ GifUnionColorMap(const ColorMapObject *ColorIn1,
          * of table 1.  This is very useful if your display is limited to
          * 16 colors.
          */
    -    while (ColorIn1->Colors[CrntSlot - 1].Red == 0
    -           && ColorIn1->Colors[CrntSlot - 1].Green == 0
    -           && ColorIn1->Colors[CrntSlot - 1].Blue == 0)
    +    while (ColorIn1->Colors[CrntSlot - 1].Red == 0 &&
    +           ColorIn1->Colors[CrntSlot - 1].Green == 0 &&
    +           ColorIn1->Colors[CrntSlot - 1].Blue == 0) {
             CrntSlot--;
    +    }
     
         /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
         for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
             /* Let's see if this color already exists: */
    -        for (j = 0; j < ColorIn1->ColorCount; j++)
    -            if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i],
    -                        sizeof(GifColorType)) == 0)
    +        for (j = 0; j < ColorIn1->ColorCount; j++) {
    +            if (memcmp(&ColorIn1->Colors[j], &ColorIn2->Colors[i],
    +                       sizeof(GifColorType)) == 0) {
                     break;
    +            }
    +        }
     
    -        if (j < ColorIn1->ColorCount)
    -            ColorTransIn2[i] = j;    /* color exists in Color1 */
    -        else {
    +        if (j < ColorIn1->ColorCount) {
    +            ColorTransIn2[i] = j; /* color exists in Color1 */
    +        } else {
                 /* Color is new - copy it to a new slot: */
                 ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
                 ColorTransIn2[i] = CrntSlot++;
    @@ -196,7 +194,7 @@ GifUnionColorMap(const ColorMapObject *ColorIn1,
     
         if (CrntSlot > 256) {
             GifFreeMapObject(ColorUnion);
    -        return ((ColorMapObject *) NULL);
    +        return ((ColorMapObject *)NULL);
         }
     
         NewGifBitSize = GifBitSize(CrntSlot);
    @@ -210,16 +208,17 @@ GifUnionColorMap(const ColorMapObject *ColorIn1,
              * We know these slots exist because of the way ColorUnion's
              * start dimension was computed.
              */
    -        for (j = CrntSlot; j < RoundUpTo; j++)
    +        for (j = CrntSlot; j < RoundUpTo; j++) {
                 Map[j].Red = Map[j].Green = Map[j].Blue = 0;
    +        }
     
             /* perhaps we can shrink the map? */
             if (RoundUpTo < ColorUnion->ColorCount) {
    -            GifColorType *new_map = (GifColorType *)reallocarray(Map,
    -                                 RoundUpTo, sizeof(GifColorType));
    -            if( new_map == NULL ) {
    +            GifColorType *new_map = (GifColorType *)reallocarray(
    +                Map, RoundUpTo, sizeof(GifColorType));
    +            if (new_map == NULL) {
                     GifFreeMapObject(ColorUnion);
    -                return ((ColorMapObject *) NULL);
    +                return ((ColorMapObject *)NULL);
                 }
                 ColorUnion->Colors = new_map;
             }
    @@ -234,49 +233,49 @@ GifUnionColorMap(const ColorMapObject *ColorIn1,
     /*******************************************************************************
      Apply a given color translation to the raster bits of an image
     *******************************************************************************/
    -void
    -GifApplyTranslation(SavedImage *Image, GifPixelType Translation[])
    -{
    +void GifApplyTranslation(SavedImage *Image, const GifPixelType Translation[]) {
         register int i;
    -    register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
    +    register int RasterSize =
    +        Image->ImageDesc.Height * Image->ImageDesc.Width;
     
    -    for (i = 0; i < RasterSize; i++)
    +    for (i = 0; i < RasterSize; i++) {
             Image->RasterBits[i] = Translation[Image->RasterBits[i]];
    +    }
     }
     
     /******************************************************************************
      Extension record functions
     ******************************************************************************/
    -int
    -GifAddExtensionBlock(int *ExtensionBlockCount,
    -                     ExtensionBlock **ExtensionBlocks,
    -                     int Function,
    -                     unsigned int Len,
    -                     unsigned char ExtData[])
    -{
    +int GifAddExtensionBlock(int *ExtensionBlockCount,
    +                         ExtensionBlock **ExtensionBlocks, int Function,
    +                         unsigned int Len, unsigned char ExtData[]) {
         ExtensionBlock *ep;
     
    -    if (*ExtensionBlocks == NULL)
    -        *ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
    -    else {
    -        ExtensionBlock* ep_new = (ExtensionBlock *)reallocarray
    -                                      (*ExtensionBlocks, (*ExtensionBlockCount + 1),
    -                                      sizeof(ExtensionBlock));
    -        if( ep_new == NULL )
    +    if (*ExtensionBlocks == NULL) {
    +        *ExtensionBlocks =
    +            (ExtensionBlock *)malloc(sizeof(ExtensionBlock));
    +    } else {
    +        ExtensionBlock *ep_new = (ExtensionBlock *)reallocarray(
    +            *ExtensionBlocks, (*ExtensionBlockCount + 1),
    +            sizeof(ExtensionBlock));
    +        if (ep_new == NULL) {
                 return (GIF_ERROR);
    +        }
             *ExtensionBlocks = ep_new;
         }
     
    -    if (*ExtensionBlocks == NULL)
    +    if (*ExtensionBlocks == NULL) {
             return (GIF_ERROR);
    +    }
     
         ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
     
         ep->Function = Function;
    -    ep->ByteCount=Len;
    +    ep->ByteCount = Len;
         ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
    -    if (ep->Bytes == NULL)
    +    if (ep->Bytes == NULL) {
             return (GIF_ERROR);
    +    }
     
         if (ExtData != NULL) {
             memcpy(ep->Bytes, ExtData, Len);
    @@ -285,38 +284,36 @@ GifAddExtensionBlock(int *ExtensionBlockCount,
         return (GIF_OK);
     }
     
    -void
    -GifFreeExtensions(int *ExtensionBlockCount,
    -                  ExtensionBlock **ExtensionBlocks)
    -{
    +void GifFreeExtensions(int *ExtensionBlockCount,
    +                       ExtensionBlock **ExtensionBlocks) {
         ExtensionBlock *ep;
     
    -    if (*ExtensionBlocks == NULL)
    +    if (*ExtensionBlocks == NULL) {
             return;
    +    }
     
         for (ep = *ExtensionBlocks;
    -         ep < (*ExtensionBlocks + *ExtensionBlockCount);
    -         ep++)
    +         ep < (*ExtensionBlocks + *ExtensionBlockCount); ep++) {
             (void)free((char *)ep->Bytes);
    +    }
         (void)free((char *)*ExtensionBlocks);
         *ExtensionBlocks = NULL;
         *ExtensionBlockCount = 0;
     }
     
     /******************************************************************************
    - Image block allocation functions
    +   Image block allocation functions
     ******************************************************************************/
     
     /* Private Function:
      * Frees the last image in the GifFile->SavedImages array
      */
    -void
    -FreeLastSavedImage(GifFileType *GifFile)
    -{
    +void FreeLastSavedImage(GifFileType *GifFile) {
         SavedImage *sp;
     
    -    if ((GifFile == NULL) || (GifFile->SavedImages == NULL))
    +    if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
             return;
    +    }
     
         /* Remove one SavedImage from the GifFile */
         GifFile->ImageCount--;
    @@ -329,54 +326,58 @@ FreeLastSavedImage(GifFileType *GifFile)
         }
     
         /* Deallocate the image data */
    -    if (sp->RasterBits != NULL)
    +    if (sp->RasterBits != NULL) {
             free((char *)sp->RasterBits);
    +    }
     
         /* Deallocate any extensions */
         GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
     
         /*** FIXME: We could realloc the GifFile->SavedImages structure but is
          * there a point to it? Saves some memory but we'd have to do it every
    -     * time.  If this is used in GifFreeSavedImages then it would be inefficient
    -     * (The whole array is going to be deallocated.)  If we just use it when
    -     * we want to free the last Image it's convenient to do it here.
    +     * time.  If this is used in GifFreeSavedImages then it would be
    +     * inefficient (The whole array is going to be deallocated.)  If we just
    +     * use it when we want to free the last Image it's convenient to do it
    +     * here.
          */
     }
     
     /*
      * Append an image block to the SavedImages array
      */
    -SavedImage *
    -GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
    -{
    -    if (GifFile->SavedImages == NULL)
    +SavedImage *GifMakeSavedImage(GifFileType *GifFile,
    +                              const SavedImage *CopyFrom) {
    +    // cppcheck-suppress ctunullpointer
    +    if (GifFile->SavedImages == NULL) {
             GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
    -    else {
    -        SavedImage* newSavedImages = (SavedImage *)reallocarray(GifFile->SavedImages,
    -                               (GifFile->ImageCount + 1), sizeof(SavedImage));
    -        if( newSavedImages == NULL)
    +    } else {
    +        SavedImage *newSavedImages = (SavedImage *)reallocarray(
    +            GifFile->SavedImages, (GifFile->ImageCount + 1),
    +            sizeof(SavedImage));
    +        if (newSavedImages == NULL) {
                 return ((SavedImage *)NULL);
    +        }
             GifFile->SavedImages = newSavedImages;
         }
    -    if (GifFile->SavedImages == NULL)
    +    if (GifFile->SavedImages == NULL) {
             return ((SavedImage *)NULL);
    -    else {
    +    } else {
             SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++];
     
             if (CopyFrom != NULL) {
                 memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
     
                 /*
    -             * Make our own allocated copies of the heap fields in the
    -             * copied record.  This guards against potential aliasing
    -             * problems.
    +             * Make our own allocated copies of the heap fields in
    +             * the copied record.  This guards against potential
    +             * aliasing problems.
                  */
     
                 /* first, the local color map */
                 if (CopyFrom->ImageDesc.ColorMap != NULL) {
                     sp->ImageDesc.ColorMap = GifMakeMapObject(
    -                                         CopyFrom->ImageDesc.ColorMap->ColorCount,
    -                                         CopyFrom->ImageDesc.ColorMap->Colors);
    +                    CopyFrom->ImageDesc.ColorMap->ColorCount,
    +                    CopyFrom->ImageDesc.ColorMap->Colors);
                     if (sp->ImageDesc.ColorMap == NULL) {
                         FreeLastSavedImage(GifFile);
                         return (SavedImage *)(NULL);
    @@ -384,32 +385,36 @@ GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
                 }
     
                 /* next, the raster */
    -            sp->RasterBits = (unsigned char *)reallocarray(NULL,
    -                                                  (CopyFrom->ImageDesc.Height *
    -                                                  CopyFrom->ImageDesc.Width),
    -                                                  sizeof(GifPixelType));
    +            sp->RasterBits = (unsigned char *)reallocarray(
    +                NULL,
    +                (CopyFrom->ImageDesc.Height *
    +                 CopyFrom->ImageDesc.Width),
    +                sizeof(GifPixelType));
                 if (sp->RasterBits == NULL) {
                     FreeLastSavedImage(GifFile);
                     return (SavedImage *)(NULL);
                 }
                 memcpy(sp->RasterBits, CopyFrom->RasterBits,
    -                   sizeof(GifPixelType) * CopyFrom->ImageDesc.Height *
    -                   CopyFrom->ImageDesc.Width);
    +                   sizeof(GifPixelType) *
    +                       CopyFrom->ImageDesc.Height *
    +                       CopyFrom->ImageDesc.Width);
     
                 /* finally, the extension blocks */
                 if (CopyFrom->ExtensionBlocks != NULL) {
    -                sp->ExtensionBlocks = (ExtensionBlock *)reallocarray(NULL,
    -                                      CopyFrom->ExtensionBlockCount,
    -                                      sizeof(ExtensionBlock));
    +                sp->ExtensionBlocks =
    +                    (ExtensionBlock *)reallocarray(
    +                        NULL, CopyFrom->ExtensionBlockCount,
    +                        sizeof(ExtensionBlock));
                     if (sp->ExtensionBlocks == NULL) {
                         FreeLastSavedImage(GifFile);
                         return (SavedImage *)(NULL);
                     }
    -                memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
    -                       sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
    +                memcpy(sp->ExtensionBlocks,
    +                       CopyFrom->ExtensionBlocks,
    +                       sizeof(ExtensionBlock) *
    +                           CopyFrom->ExtensionBlockCount);
                 }
    -        }
    -        else {
    +        } else {
                 memset((char *)sp, '\0', sizeof(SavedImage));
             }
     
    @@ -417,9 +422,7 @@ GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
         }
     }
     
    -void
    -GifFreeSavedImages(GifFileType *GifFile)
    -{
    +void GifFreeSavedImages(GifFileType *GifFile) {
         SavedImage *sp;
     
         if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
    @@ -432,10 +435,12 @@ GifFreeSavedImages(GifFileType *GifFile)
                 sp->ImageDesc.ColorMap = NULL;
             }
     
    -        if (sp->RasterBits != NULL)
    +        if (sp->RasterBits != NULL) {
                 free((char *)sp->RasterBits);
    +        }
     
    -        GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
    +        GifFreeExtensions(&sp->ExtensionBlockCount,
    +                          &sp->ExtensionBlocks);
         }
         free((char *)GifFile->SavedImages);
         GifFile->SavedImages = NULL;
    diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/openbsd-reallocarray.c b/src/java.desktop/share/native/libsplashscreen/giflib/openbsd-reallocarray.c
    index 452df69d7cd4f..7420af674c530 100644
    --- a/src/java.desktop/share/native/libsplashscreen/giflib/openbsd-reallocarray.c
    +++ b/src/java.desktop/share/native/libsplashscreen/giflib/openbsd-reallocarray.c
    @@ -28,24 +28,22 @@
      * SPDX-License-Identifier: MIT
      */
     
    -#include 
     #include 
     #include 
     #include 
    +#include 
     
     #ifndef SIZE_MAX
    -    #define SIZE_MAX     UINTPTR_MAX
    +#define SIZE_MAX UINTPTR_MAX
     #endif
     
     /*
      * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
      * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
      */
    -#define MUL_NO_OVERFLOW    ((size_t)1 << (sizeof(size_t) * 4))
    +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
     
    -void *
    -openbsd_reallocarray(void *optr, size_t nmemb, size_t size)
    -{
    +void *openbsd_reallocarray(void *optr, size_t nmemb, size_t size) {
         if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
             nmemb > 0 && SIZE_MAX / nmemb < size) {
             errno = ENOMEM;
    @@ -93,7 +91,8 @@ openbsd_reallocarray(void *optr, size_t nmemb, size_t size)
          * fuzzing on one platform may not detect zero-size allocation
          * problems on other platforms.
          */
    -    if (size == 0 || nmemb == 0)
    +    if (size == 0 || nmemb == 0) {
             return NULL;
    +    }
         return realloc(optr, size * nmemb);
     }
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
    index 2d8c585c0e794..441b57ecf1ab2 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
    @@ -6129,6 +6129,73 @@ Version 1.6.40 [June 21, 2023]
       Updated the configurations and the scripts for continuous integration.
       Cleaned up the code, the build scripts, and the documentation.
     
    +Version 1.6.41 [January 24, 2024]
    +  Added SIMD-optimized code for the LoongArch LSX hardware.
    +    (Contributed by GuXiWei, JinBo and ZhangLixia)
    +  Fixed the run-time discovery of MIPS MSA hardware.
    +    (Contributed by Sui Jingfeng)
    +  Fixed an off-by-one error in the function png_do_check_palette_indexes(),
    +    which failed to recognize errors that might have existed in the first
    +    column of a broken palette-encoded image. This was a benign regression
    +    accidentally introduced in libpng-1.6.33. No pixel was harmed.
    +    (Contributed by Adam Richter; reviewed by John Bowler)
    +  Fixed, improved and modernized the contrib/pngminus programs, i.e.,
    +    png2pnm.c and pnm2png.c
    +  Removed old and peculiar portability hacks that were meant to silence
    +    warnings issued by gcc version 7.1 alone.
    +    (Contributed by John Bowler)
    +  Fixed and modernized the CMake file, and raised the minimum required
    +    CMake version from 3.1 to 3.6.
    +    (Contributed by Clinton Ingram, Timothy Lyanguzov, Tyler Kropp, et al.)
    +  Allowed the configure script to disable the building of auxiliary tools
    +    and tests, thus catching up with the CMake file.
    +    (Contributed by Carlo Bramini)
    +  Fixed a build issue on Mac.
    +    (Contributed by Zixu Wang)
    +  Moved the Autoconf macro files to scripts/autoconf.
    +  Moved the CMake files (except for the main CMakeLists.txt) to
    +    scripts/cmake and moved the list of their contributing authors to
    +    scripts/cmake/AUTHORS.md
    +  Updated the CI configurations and scripts.
    +  Relicensed the CI scripts to the MIT License.
    +  Improved the test coverage.
    +    (Contributed by John Bowler)
    +
    +Version 1.6.42 [January 29, 2024]
    +  Fixed the implementation of the macro function png_check_sig().
    +    This was an API regression, introduced in libpng-1.6.41.
    +    (Reported by Matthieu Darbois)
    +  Fixed and updated the libpng manual.
    +
    +Version 1.6.43 [February 23, 2024]
    +  Fixed the row width check in png_check_IHDR().
    +    This corrected a bug that was specific to the 16-bit platforms,
    +    and removed a spurious compiler warning from the 64-bit builds.
    +    (Reported by Jacek Caban; fixed by John Bowler)
    +  Added eXIf chunk support to the push-mode reader in pngpread.c.
    +    (Contributed by Chris Blume)
    +  Added contrib/pngexif for the benefit of the users who would like
    +    to inspect the content of eXIf chunks.
    +  Added contrib/conftest/basic.dfa, a basic build-time configuration.
    +    (Contributed by John Bowler)
    +  Fixed a preprocessor condition in pngread.c that broke build-time
    +    configurations like contrib/conftest/pngcp.dfa.
    +    (Contributed by John Bowler)
    +  Added CMake build support for LoongArch LSX.
    +    (Contributed by GuXiWei)
    +  Fixed a CMake build error that occurred under a peculiar state of the
    +    dependency tree. This was a regression introduced in libpng-1.6.41.
    +    (Contributed by Dan Rosser)
    +  Marked the installed libpng headers as system headers in CMake.
    +    (Contributed by Benjamin Buch)
    +  Updated the build support for RISCOS.
    +    (Contributed by Cameron Cawley)
    +  Updated the makefiles to allow cross-platform builds to initialize
    +    conventional make variables like AR and ARFLAGS.
    +  Added various improvements to the CI scripts in areas like version
    +    consistency verification and text linting.
    +  Added version consistency verification to pngtest.c also.
    +
     Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
     Subscription is required; visit
     https://lists.sourceforge.net/lists/listinfo/png-mng-implement
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE b/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE
    index 086d1c2fda63b..25f298f0fcfd8 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE
    @@ -4,8 +4,8 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
     PNG Reference Library License version 2
     ---------------------------------------
     
    - * Copyright (c) 1995-2023 The PNG Reference Library Authors.
    - * Copyright (c) 2018-2023 Cosmin Truta.
    + * Copyright (c) 1995-2024 The PNG Reference Library Authors.
    + * Copyright (c) 2018-2024 Cosmin Truta.
      * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
      * Copyright (c) 1996-1997 Andreas Dilger.
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/README b/src/java.desktop/share/native/libsplashscreen/libpng/README
    index dedd2c1639e24..a6ca3ae9f9406 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/README
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/README
    @@ -1,4 +1,4 @@
    -README for libpng version 1.6.40
    +README for libpng version 1.6.43
     ================================
     
     See the note about version numbers near the top of `png.h`.
    @@ -142,10 +142,11 @@ Files included in this distribution
         pngwrite.c    =>  High-level write functions
         pngwtran.c    =>  Write data transformations
         pngwutil.c    =>  Write utility functions
    -    arm/          =>  Optimized code for the ARM platform
    -    intel/        =>  Optimized code for the INTEL-SSE2 platform
    -    mips/         =>  Optimized code for the MIPS platform
    -    powerpc/      =>  Optimized code for the PowerPC platform
    +    arm/          =>  Optimized code for ARM Neon
    +    intel/        =>  Optimized code for INTEL SSE2
    +    loongarch/    =>  Optimized code for LoongArch LSX
    +    mips/         =>  Optimized code for MIPS MSA and MIPS MMI
    +    powerpc/      =>  Optimized code for PowerPC VSX
         ci/           =>  Scripts for continuous integration
         contrib/      =>  External contributions
             arm-neon/     =>  Optimized code for the ARM-NEON platform
    @@ -158,6 +159,7 @@ Files included in this distribution
             libtests/     =>  Test programs
             oss-fuzz/     =>  Files used by the OSS-Fuzz project for fuzz-testing
                               libpng
    +        pngexif/      =>  Program to inspect the EXIF information in PNG files
             pngminim/     =>  Minimal decoder, encoder, and progressive decoder
                               programs demonstrating the use of pngusr.dfa
             pngminus/     =>  Simple pnm2png and png2pnm programs
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/UPDATING.txt b/src/java.desktop/share/native/libsplashscreen/libpng/UPDATING.txt
    index 93c8f5bb7039c..88200db5d732f 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/UPDATING.txt
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/UPDATING.txt
    @@ -37,18 +37,21 @@ and instead just tweak the existing one.
     
     First cd into the libpng folder and run the following script.
     
    +    shopt -s nullglob
         for f in *.c *.h;
    -    do
    -      # replace tabs with spaces
    -      expand ${f} > ${f}.tmp;
    -      mv ${f}.tmp $f;
    -
    -      # fix line endings to LF
    -      sed -i -e 's/\r$//g' ${f};
    -
    -      # remove trailing spaces
    -      sed -i -e 's/[ ]* $//g' ${f};
    -    done
    +         do
    +            # replace tabs with spaces
    +            expand ${f} > ${f}.tmp
    +            mv ${f}.tmp $f
    +
    +            # fix line endings to LF
    +            sed -e 's/\r$//g' ${f} > ${f}.tmp
    +            mv ${f}.tmp $f
    +
    +            # remove trailing spaces
    +            sed -e 's/[ ]* $//g' ${f} > ${f}.tmp
    +            mv ${f}.tmp $f
    +         done
     
     6) As with all native code, run it through the official build systems, in case
     the updated code trigger any fatal warnings with the official compilers.
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.c b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
    index 91a92e5f71871..232dff876c793 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/png.c
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
    @@ -29,7 +29,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * Copyright (c) 2018-2023 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -42,27 +42,7 @@
     #include "pngpriv.h"
     
     /* Generate a compiler error if there is an old png.h in the search path. */
    -typedef png_libpng_version_1_6_40 Your_png_h_is_not_version_1_6_40;
    -
    -#ifdef __GNUC__
    -/* The version tests may need to be added to, but the problem warning has
    - * consistently been fixed in GCC versions which obtain wide-spread release.
    - * The problem is that many versions of GCC rearrange comparison expressions in
    - * the optimizer in such a way that the results of the comparison will change
    - * if signed integer overflow occurs.  Such comparisons are not permitted in
    - * ANSI C90, however GCC isn't clever enough to work out that that do not occur
    - * below in png_ascii_from_fp and png_muldiv, so it produces a warning with
    - * -Wextra.  Unfortunately this is highly dependent on the optimizer and the
    - * machine architecture so the warning comes and goes unpredictably and is
    - * impossible to "fix", even were that a good idea.
    - */
    -#if __GNUC__ == 7 && __GNUC_MINOR__ == 1
    -#define GCC_STRICT_OVERFLOW 1
    -#endif /* GNU 7.1.x */
    -#endif /* GNU */
    -#ifndef GCC_STRICT_OVERFLOW
    -#define GCC_STRICT_OVERFLOW 0
    -#endif
    +typedef png_libpng_version_1_6_43 Your_png_h_is_not_version_1_6_43;
     
     /* Tells libpng that we have already handled the first "num_bytes" bytes
      * of the PNG file signature.  If the PNG data is embedded into another
    @@ -101,21 +81,21 @@ png_set_sig_bytes(png_structrp png_ptr, int num_bytes)
     int PNGAPI
     png_sig_cmp(png_const_bytep sig, size_t start, size_t num_to_check)
     {
    -   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
    +   static const png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
     
        if (num_to_check > 8)
           num_to_check = 8;
     
        else if (num_to_check < 1)
    -      return (-1);
    +      return -1;
     
        if (start > 7)
    -      return (-1);
    +      return -1;
     
        if (start + num_to_check > 8)
           num_to_check = 8 - start;
     
    -   return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check)));
    +   return memcmp(&sig[start], &png_signature[start], num_to_check);
     }
     
     #endif /* READ */
    @@ -475,7 +455,6 @@ png_info_init_3,(png_infopp ptr_ptr, size_t png_info_struct_size),
        memset(info_ptr, 0, (sizeof *info_ptr));
     }
     
    -/* The following API is not called internally */
     void PNGAPI
     png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr,
         int freer, png_uint_32 mask)
    @@ -714,9 +693,9 @@ png_voidp PNGAPI
     png_get_io_ptr(png_const_structrp png_ptr)
     {
        if (png_ptr == NULL)
    -      return (NULL);
    +      return NULL;
     
    -   return (png_ptr->io_ptr);
    +   return png_ptr->io_ptr;
     }
     
     #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
    @@ -780,7 +759,7 @@ png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime)
     
        {
           size_t pos = 0;
    -      char number_buf[5]; /* enough for a four-digit year */
    +      char number_buf[5] = {0, 0, 0, 0, 0}; /* enough for a four-digit year */
     
     #     define APPEND_STRING(string) pos = png_safecat(out, 29, pos, (string))
     #     define APPEND_NUMBER(format, value)\
    @@ -843,8 +822,8 @@ png_get_copyright(png_const_structrp png_ptr)
        return PNG_STRING_COPYRIGHT
     #else
        return PNG_STRING_NEWLINE \
    -      "libpng version 1.6.40" PNG_STRING_NEWLINE \
    -      "Copyright (c) 2018-2023 Cosmin Truta" PNG_STRING_NEWLINE \
    +      "libpng version 1.6.43" PNG_STRING_NEWLINE \
    +      "Copyright (c) 2018-2024 Cosmin Truta" PNG_STRING_NEWLINE \
           "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
           PNG_STRING_NEWLINE \
           "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
    @@ -1005,7 +984,7 @@ png_reset_zstream(png_structrp png_ptr)
           return Z_STREAM_ERROR;
     
        /* WARNING: this resets the window bits to the maximum! */
    -   return (inflateReset(&png_ptr->zstream));
    +   return inflateReset(&png_ptr->zstream);
     }
     #endif /* READ */
     
    @@ -1014,7 +993,7 @@ png_uint_32 PNGAPI
     png_access_version_number(void)
     {
        /* Version of *.c files used when building libpng */
    -   return((png_uint_32)PNG_LIBPNG_VER);
    +   return (png_uint_32)PNG_LIBPNG_VER;
     }
     
     #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
    @@ -1870,14 +1849,14 @@ png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
        }
     #  ifdef PNG_WARNINGS_SUPPORTED
        else
    -      {
    -         char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114 */
    +   {
    +      char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114 */
     
    -         pos = png_safecat(message, (sizeof message), pos,
    -             png_format_number(number, number+(sizeof number),
    -             PNG_NUMBER_FORMAT_x, value));
    -         pos = png_safecat(message, (sizeof message), pos, "h: "); /* +2 = 116 */
    -      }
    +      pos = png_safecat(message, (sizeof message), pos,
    +          png_format_number(number, number+(sizeof number),
    +          PNG_NUMBER_FORMAT_x, value));
    +      pos = png_safecat(message, (sizeof message), pos, "h: "); /* +2 = 116 */
    +   }
     #  endif
        /* The 'reason' is an arbitrary message, allow +79 maximum 195 */
        pos = png_safecat(message, (sizeof message), pos, reason);
    @@ -2560,17 +2539,6 @@ png_colorspace_set_rgb_coefficients(png_structrp png_ptr)
     
     #endif /* COLORSPACE */
     
    -#ifdef __GNUC__
    -/* This exists solely to work round a warning from GNU C. */
    -static int /* PRIVATE */
    -png_gt(size_t a, size_t b)
    -{
    -   return a > b;
    -}
    -#else
    -#   define png_gt(a,b) ((a) > (b))
    -#endif
    -
     void /* PRIVATE */
     png_check_IHDR(png_const_structrp png_ptr,
         png_uint_32 width, png_uint_32 height, int bit_depth,
    @@ -2592,8 +2560,16 @@ png_check_IHDR(png_const_structrp png_ptr,
           error = 1;
        }
     
    -   if (png_gt(((width + 7) & (~7U)),
    -       ((PNG_SIZE_MAX
    +   /* The bit mask on the first line below must be at least as big as a
    +    * png_uint_32.  "~7U" is not adequate on 16-bit systems because it will
    +    * be an unsigned 16-bit value.  Casting to (png_alloc_size_t) makes the
    +    * type of the result at least as bit (in bits) as the RHS of the > operator
    +    * which also avoids a common warning on 64-bit systems that the comparison
    +    * of (png_uint_32) against the constant value on the RHS will always be
    +    * false.
    +    */
    +   if (((width + 7) & ~(png_alloc_size_t)7) >
    +       (((PNG_SIZE_MAX
                - 48        /* big_row_buf hack */
                - 1)        /* filter byte */
                / 8)        /* 8-byte RGBA pixels */
    @@ -2919,14 +2895,6 @@ png_pow10(int power)
     /* Function to format a floating point value in ASCII with a given
      * precision.
      */
    -#if GCC_STRICT_OVERFLOW
    -#pragma GCC diagnostic push
    -/* The problem arises below with exp_b10, which can never overflow because it
    - * comes, originally, from frexp and is therefore limited to a range which is
    - * typically +/-710 (log2(DBL_MAX)/log2(DBL_MIN)).
    - */
    -#pragma GCC diagnostic warning "-Wstrict-overflow=2"
    -#endif /* GCC_STRICT_OVERFLOW */
     void /* PRIVATE */
     png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, size_t size,
         double fp, unsigned int precision)
    @@ -3248,10 +3216,6 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, size_t size,
        /* Here on buffer too small. */
        png_error(png_ptr, "ASCII conversion buffer too small");
     }
    -#if GCC_STRICT_OVERFLOW
    -#pragma GCC diagnostic pop
    -#endif /* GCC_STRICT_OVERFLOW */
    -
     #  endif /* FLOATING_POINT */
     
     #  ifdef PNG_FIXED_POINT_SUPPORTED
    @@ -3279,7 +3243,7 @@ png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii,
           if (num <= 0x80000000) /* else overflowed */
           {
              unsigned int ndigits = 0, first = 16 /* flag value */;
    -         char digits[10];
    +         char digits[10] = {0};
     
              while (num)
              {
    @@ -3364,15 +3328,6 @@ png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text)
      * the nearest .00001).  Overflow and divide by zero are signalled in
      * the result, a boolean - true on success, false on overflow.
      */
    -#if GCC_STRICT_OVERFLOW /* from above */
    -/* It is not obvious which comparison below gets optimized in such a way that
    - * signed overflow would change the result; looking through the code does not
    - * reveal any tests which have the form GCC complains about, so presumably the
    - * optimizer is moving an add or subtract into the 'if' somewhere.
    - */
    -#pragma GCC diagnostic push
    -#pragma GCC diagnostic warning "-Wstrict-overflow=2"
    -#endif /* GCC_STRICT_OVERFLOW */
     int
     png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
         png_int_32 divisor)
    @@ -3487,9 +3442,6 @@ png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
     
        return 0;
     }
    -#if GCC_STRICT_OVERFLOW
    -#pragma GCC diagnostic pop
    -#endif /* GCC_STRICT_OVERFLOW */
     #endif /* READ_GAMMA || INCH_CONVERSIONS */
     
     #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.h b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
    index 578841c958049..9f61a773c1ddb 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/png.h
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
    @@ -29,9 +29,9 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * libpng version 1.6.40
    + * libpng version 1.6.43
      *
    - * Copyright (c) 2018-2023 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -43,7 +43,7 @@
      *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
      *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
      *     Glenn Randers-Pehrson
    - *   libpng versions 1.6.36, December 2018, through 1.6.40, June 2023:
    + *   libpng versions 1.6.36, December 2018, through 1.6.43, February 2024:
      *     Cosmin Truta
      *   See also "Contributing Authors", below.
      */
    @@ -55,8 +55,8 @@
      * PNG Reference Library License version 2
      * ---------------------------------------
      *
    - *  * Copyright (c) 1995-2023 The PNG Reference Library Authors.
    - *  * Copyright (c) 2018-2023 Cosmin Truta.
    + *  * Copyright (c) 1995-2024 The PNG Reference Library Authors.
    + *  * Copyright (c) 2018-2024 Cosmin Truta.
      *  * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
      *  * Copyright (c) 1996-1997 Andreas Dilger.
      *  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -267,7 +267,7 @@
      *    ...
      *    1.5.30                  15    10530  15.so.15.30[.0]
      *    ...
    - *    1.6.40                  16    10640  16.so.16.40[.0]
    + *    1.6.43                  16    10643  16.so.16.43[.0]
      *
      *    Henceforth the source version will match the shared-library major and
      *    minor numbers; the shared-library major version number will be used for
    @@ -283,9 +283,6 @@
      *    to the info_ptr or png_ptr members through png.h, and the compiled
      *    application is loaded with a different version of the library.
      *
    - *    DLLNUM will change each time there are forward or backward changes
    - *    in binary compatibility (e.g., when a new feature is added).
    - *
      * See libpng.txt or libpng.3 for more information.  The PNG specification
      * is available as a W3C Recommendation and as an ISO/IEC Standard; see
      * 
    @@ -306,19 +303,21 @@
      */
     
     /* Version information for png.h - this should match the version in png.c */
    -#define PNG_LIBPNG_VER_STRING "1.6.40"
    -#define PNG_HEADER_VERSION_STRING " libpng version 1.6.40 - June 21, 2023\n"
    +#define PNG_LIBPNG_VER_STRING "1.6.43"
    +#define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n"
     
    -#define PNG_LIBPNG_VER_SONUM   16
    -#define PNG_LIBPNG_VER_DLLNUM  16
    +/* The versions of shared library builds should stay in sync, going forward */
    +#define PNG_LIBPNG_VER_SHAREDLIB 16
    +#define PNG_LIBPNG_VER_SONUM     PNG_LIBPNG_VER_SHAREDLIB /* [Deprecated] */
    +#define PNG_LIBPNG_VER_DLLNUM    PNG_LIBPNG_VER_SHAREDLIB /* [Deprecated] */
     
     /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
     #define PNG_LIBPNG_VER_MAJOR   1
     #define PNG_LIBPNG_VER_MINOR   6
    -#define PNG_LIBPNG_VER_RELEASE 40
    +#define PNG_LIBPNG_VER_RELEASE 43
     
     /* This should be zero for a public release, or non-zero for a
    - * development version.  [Deprecated]
    + * development version.
      */
     #define PNG_LIBPNG_VER_BUILD  0
     
    @@ -346,7 +345,7 @@
      * From version 1.0.1 it is:
      * XXYYZZ, where XX=major, YY=minor, ZZ=release
      */
    -#define PNG_LIBPNG_VER 10640 /* 1.6.40 */
    +#define PNG_LIBPNG_VER 10643 /* 1.6.43 */
     
     /* Library configuration: these options cannot be changed after
      * the library has been built.
    @@ -456,7 +455,7 @@ extern "C" {
     /* This triggers a compiler error in png.c, if png.c and png.h
      * do not agree upon the version number.
      */
    -typedef char* png_libpng_version_1_6_40;
    +typedef char* png_libpng_version_1_6_43;
     
     /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
      *
    @@ -877,7 +876,7 @@ PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef);
     #define PNG_TRANSFORM_GRAY_TO_RGB   0x2000      /* read only */
     /* Added to libpng-1.5.4 */
     #define PNG_TRANSFORM_EXPAND_16     0x4000      /* read only */
    -#if INT_MAX >= 0x8000 /* else this might break */
    +#if ~0U > 0xffffU /* or else this might break on a 16-bit machine */
     #define PNG_TRANSFORM_SCALE_16      0x8000      /* read only */
     #endif
     
    @@ -936,15 +935,15 @@ PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes));
     /* Check sig[start] through sig[start + num_to_check - 1] to see if it's a
      * PNG file.  Returns zero if the supplied bytes match the 8-byte PNG
      * signature, and non-zero otherwise.  Having num_to_check == 0 or
    - * start > 7 will always fail (ie return non-zero).
    + * start > 7 will always fail (i.e. return non-zero).
      */
     PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start,
         size_t num_to_check));
     
     /* Simple signature checking function.  This is the same as calling
    - * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n).
    + * png_check_sig(sig, n) := (png_sig_cmp(sig, 0, n) == 0).
      */
    -#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n))
    +#define png_check_sig(sig, n) (png_sig_cmp((sig), 0, (n)) == 0) /* DEPRECATED */
     
     /* Allocate and initialize png_ptr struct for reading, and any other memory. */
     PNG_EXPORTA(4, png_structp, png_create_read_struct,
    @@ -1758,12 +1757,9 @@ PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr));
     PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr,
         png_inforp info_ptr, png_uint_32 free_me, int num));
     
    -/* Reassign responsibility for freeing existing data, whether allocated
    +/* Reassign the responsibility for freeing existing data, whether allocated
      * by libpng or by the application; this works on the png_info structure passed
    - * in, it does not change the state for other png_info structures.
    - *
    - * It is unlikely that this function works correctly as of 1.6.0 and using it
    - * may result either in memory leaks or double free of allocated data.
    + * in, without changing the state for other png_info structures.
      */
     PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr,
         png_inforp info_ptr, int freer, png_uint_32 mask));
    @@ -3235,11 +3231,18 @@ PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory,
     #ifdef PNG_MIPS_MSA_API_SUPPORTED
     #  define PNG_MIPS_MSA   6 /* HARDWARE: MIPS Msa SIMD instructions supported */
     #endif
    -#define PNG_IGNORE_ADLER32 8
    +#ifdef PNG_DISABLE_ADLER32_CHECK_SUPPORTED
    +#  define PNG_IGNORE_ADLER32 8 /* SOFTWARE: disable Adler32 check on IDAT */
    +#endif
     #ifdef PNG_POWERPC_VSX_API_SUPPORTED
    -#  define PNG_POWERPC_VSX   10 /* HARDWARE: PowerPC VSX SIMD instructions supported */
    +#  define PNG_POWERPC_VSX   10 /* HARDWARE: PowerPC VSX SIMD instructions
    +                                * supported */
     #endif
    -#define PNG_OPTION_NEXT  12 /* Next option - numbers must be even */
    +#ifdef PNG_MIPS_MMI_API_SUPPORTED
    +#  define PNG_MIPS_MMI   12 /* HARDWARE: MIPS MMI SIMD instructions supported */
    +#endif
    +
    +#define PNG_OPTION_NEXT  14 /* Next option - numbers must be even */
     
     /* Return values: NOTE: there are four values and 'off' is *not* zero */
     #define PNG_OPTION_UNSET   0 /* Unset - defaults to off */
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
    index 41cbc91d39861..b3b441b1122c2 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
    @@ -29,9 +29,9 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * libpng version 1.6.40
    + * libpng version 1.6.43
      *
    - * Copyright (c) 2018-2022 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c
    index 623735f06f12b..ea8dd1721972c 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c
    @@ -29,7 +29,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * Copyright (c) 2018 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -283,7 +283,7 @@ void
     png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,
         png_alloc_size_t value)
     {
    -   char buffer[PNG_NUMBER_BUFFER_SIZE];
    +   char buffer[PNG_NUMBER_BUFFER_SIZE] = {0};
        png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value));
     }
     
    @@ -293,7 +293,7 @@ png_warning_parameter_signed(png_warning_parameters p, int number, int format,
     {
        png_alloc_size_t u;
        png_charp str;
    -   char buffer[PNG_NUMBER_BUFFER_SIZE];
    +   char buffer[PNG_NUMBER_BUFFER_SIZE] = {0};
     
        /* Avoid overflow by doing the negate in a png_alloc_size_t: */
        u = (png_alloc_size_t)value;
    @@ -886,7 +886,7 @@ png_get_error_ptr(png_const_structrp png_ptr)
        if (png_ptr == NULL)
           return NULL;
     
    -   return ((png_voidp)png_ptr->error_ptr);
    +   return (png_voidp)png_ptr->error_ptr;
     }
     
     
    @@ -961,31 +961,25 @@ png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message)
     #endif
     
     int /* PRIVATE */
    -png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg)
    +png_safe_execute(png_imagep image, int (*function)(png_voidp), png_voidp arg)
     {
    -   volatile png_imagep image = image_in;
    -   volatile int result;
    -   volatile png_voidp saved_error_buf;
    +   png_voidp saved_error_buf = image->opaque->error_buf;
        jmp_buf safe_jmpbuf;
    +   int result;
     
    -   /* Safely execute function(arg) with png_error returning to this function. */
    -   saved_error_buf = image->opaque->error_buf;
    -   result = setjmp(safe_jmpbuf) == 0;
    -
    -   if (result != 0)
    +   /* Safely execute function(arg), with png_error returning back here. */
    +   if (setjmp(safe_jmpbuf) == 0)
        {
    -
           image->opaque->error_buf = safe_jmpbuf;
           result = function(arg);
    +      image->opaque->error_buf = saved_error_buf;
    +      return result;
        }
     
    +   /* On png_error, return via longjmp, pop the jmpbuf, and free the image. */
        image->opaque->error_buf = saved_error_buf;
    -
    -   /* And do the cleanup prior to any failure return. */
    -   if (result == 0)
    -      png_image_free(image);
    -
    -   return result;
    +   png_image_free(image);
    +   return 0;
     }
     #endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */
     #endif /* READ || WRITE */
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c
    index 6e510b2732751..41e0a5abc3a8d 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c
    @@ -29,7 +29,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * Copyright (c) 2018-2023 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -56,22 +56,22 @@ png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr,
            * valid tRNS chunk in this case.
            */
           if (flag == PNG_INFO_tRNS && png_ptr->num_trans == 0)
    -         return(0);
    +         return 0;
     #endif
     
    -      return(info_ptr->valid & flag);
    +      return info_ptr->valid & flag;
        }
     
    -   return(0);
    +   return 0;
     }
     
     size_t PNGAPI
     png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr)
     {
        if (png_ptr != NULL && info_ptr != NULL)
    -      return(info_ptr->rowbytes);
    +      return info_ptr->rowbytes;
     
    -   return(0);
    +   return 0;
     }
     
     #ifdef PNG_INFO_IMAGE_SUPPORTED
    @@ -79,9 +79,9 @@ png_bytepp PNGAPI
     png_get_rows(png_const_structrp png_ptr, png_const_inforp info_ptr)
     {
        if (png_ptr != NULL && info_ptr != NULL)
    -      return(info_ptr->row_pointers);
    +      return info_ptr->row_pointers;
     
    -   return(0);
    +   return 0;
     }
     #endif
     
    @@ -93,7 +93,7 @@ png_get_image_width(png_const_structrp png_ptr, png_const_inforp info_ptr)
        if (png_ptr != NULL && info_ptr != NULL)
           return info_ptr->width;
     
    -   return (0);
    +   return 0;
     }
     
     png_uint_32 PNGAPI
    @@ -102,7 +102,7 @@ png_get_image_height(png_const_structrp png_ptr, png_const_inforp info_ptr)
        if (png_ptr != NULL && info_ptr != NULL)
           return info_ptr->height;
     
    -   return (0);
    +   return 0;
     }
     
     png_byte PNGAPI
    @@ -111,7 +111,7 @@ png_get_bit_depth(png_const_structrp png_ptr, png_const_inforp info_ptr)
        if (png_ptr != NULL && info_ptr != NULL)
           return info_ptr->bit_depth;
     
    -   return (0);
    +   return 0;
     }
     
     png_byte PNGAPI
    @@ -120,7 +120,7 @@ png_get_color_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
        if (png_ptr != NULL && info_ptr != NULL)
           return info_ptr->color_type;
     
    -   return (0);
    +   return 0;
     }
     
     png_byte PNGAPI
    @@ -129,7 +129,7 @@ png_get_filter_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
        if (png_ptr != NULL && info_ptr != NULL)
           return info_ptr->filter_type;
     
    -   return (0);
    +   return 0;
     }
     
     png_byte PNGAPI
    @@ -138,7 +138,7 @@ png_get_interlace_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
        if (png_ptr != NULL && info_ptr != NULL)
           return info_ptr->interlace_type;
     
    -   return (0);
    +   return 0;
     }
     
     png_byte PNGAPI
    @@ -147,7 +147,7 @@ png_get_compression_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
        if (png_ptr != NULL && info_ptr != NULL)
           return info_ptr->compression_type;
     
    -   return (0);
    +   return 0;
     }
     
     png_uint_32 PNGAPI
    @@ -155,21 +155,20 @@ png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
        info_ptr)
     {
     #ifdef PNG_pHYs_SUPPORTED
    +   png_debug(1, "in png_get_x_pixels_per_meter");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_pHYs) != 0)
    -      {
    -         png_debug1(1, "in %s retrieval function",
    -             "png_get_x_pixels_per_meter");
    -
    -         if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)
    -            return (info_ptr->x_pixels_per_unit);
    -      }
    +   {
    +      if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)
    +         return info_ptr->x_pixels_per_unit;
    +   }
     #else
        PNG_UNUSED(png_ptr)
        PNG_UNUSED(info_ptr)
     #endif
     
    -   return (0);
    +   return 0;
     }
     
     png_uint_32 PNGAPI
    @@ -177,42 +176,41 @@ png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
         info_ptr)
     {
     #ifdef PNG_pHYs_SUPPORTED
    +   png_debug(1, "in png_get_y_pixels_per_meter");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_pHYs) != 0)
        {
    -      png_debug1(1, "in %s retrieval function",
    -          "png_get_y_pixels_per_meter");
    -
           if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)
    -         return (info_ptr->y_pixels_per_unit);
    +         return info_ptr->y_pixels_per_unit;
        }
     #else
        PNG_UNUSED(png_ptr)
        PNG_UNUSED(info_ptr)
     #endif
     
    -   return (0);
    +   return 0;
     }
     
     png_uint_32 PNGAPI
     png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr)
     {
     #ifdef PNG_pHYs_SUPPORTED
    +   png_debug(1, "in png_get_pixels_per_meter");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_pHYs) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter");
    -
           if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER &&
               info_ptr->x_pixels_per_unit == info_ptr->y_pixels_per_unit)
    -         return (info_ptr->x_pixels_per_unit);
    +         return info_ptr->x_pixels_per_unit;
        }
     #else
        PNG_UNUSED(png_ptr)
        PNG_UNUSED(info_ptr)
     #endif
     
    -   return (0);
    +   return 0;
     }
     
     #ifdef PNG_FLOATING_POINT_SUPPORTED
    @@ -221,21 +219,21 @@ png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp
        info_ptr)
     {
     #ifdef PNG_READ_pHYs_SUPPORTED
    +   png_debug(1, "in png_get_pixel_aspect_ratio");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_pHYs) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio");
    -
           if (info_ptr->x_pixels_per_unit != 0)
    -         return ((float)((float)info_ptr->y_pixels_per_unit
    -             /(float)info_ptr->x_pixels_per_unit));
    +         return (float)info_ptr->y_pixels_per_unit
    +              / (float)info_ptr->x_pixels_per_unit;
        }
     #else
        PNG_UNUSED(png_ptr)
        PNG_UNUSED(info_ptr)
     #endif
     
    -   return ((float)0.0);
    +   return (float)0.0;
     }
     #endif
     
    @@ -245,6 +243,8 @@ png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr,
         png_const_inforp info_ptr)
     {
     #ifdef PNG_READ_pHYs_SUPPORTED
    +   png_debug(1, "in png_get_pixel_aspect_ratio_fixed");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_pHYs) != 0 &&
            info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0 &&
    @@ -253,8 +253,6 @@ png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr,
        {
           png_fixed_point res;
     
    -      png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio_fixed");
    -
           /* The following casts work because a PNG 4 byte integer only has a valid
            * range of 0..2^31-1; otherwise the cast might overflow.
            */
    @@ -275,80 +273,80 @@ png_int_32 PNGAPI
     png_get_x_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr)
     {
     #ifdef PNG_oFFs_SUPPORTED
    +   png_debug(1, "in png_get_x_offset_microns");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_oFFs) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns");
    -
           if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER)
    -         return (info_ptr->x_offset);
    +         return info_ptr->x_offset;
        }
     #else
        PNG_UNUSED(png_ptr)
        PNG_UNUSED(info_ptr)
     #endif
     
    -   return (0);
    +   return 0;
     }
     
     png_int_32 PNGAPI
     png_get_y_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr)
     {
     #ifdef PNG_oFFs_SUPPORTED
    +   png_debug(1, "in png_get_y_offset_microns");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_oFFs) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns");
    -
           if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER)
    -         return (info_ptr->y_offset);
    +         return info_ptr->y_offset;
        }
     #else
        PNG_UNUSED(png_ptr)
        PNG_UNUSED(info_ptr)
     #endif
     
    -   return (0);
    +   return 0;
     }
     
     png_int_32 PNGAPI
     png_get_x_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)
     {
     #ifdef PNG_oFFs_SUPPORTED
    +   png_debug(1, "in png_get_x_offset_pixels");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_oFFs) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "png_get_x_offset_pixels");
    -
           if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL)
    -         return (info_ptr->x_offset);
    +         return info_ptr->x_offset;
        }
     #else
        PNG_UNUSED(png_ptr)
        PNG_UNUSED(info_ptr)
     #endif
     
    -   return (0);
    +   return 0;
     }
     
     png_int_32 PNGAPI
     png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr)
     {
     #ifdef PNG_oFFs_SUPPORTED
    +   png_debug(1, "in png_get_y_offset_pixels");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_oFFs) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "png_get_y_offset_pixels");
    -
           if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL)
    -         return (info_ptr->y_offset);
    +         return info_ptr->y_offset;
        }
     #else
        PNG_UNUSED(png_ptr)
        PNG_UNUSED(info_ptr)
     #endif
     
    -   return (0);
    +   return 0;
     }
     
     #ifdef PNG_INCH_CONVERSIONS_SUPPORTED
    @@ -462,11 +460,11 @@ png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr,
     {
        png_uint_32 retval = 0;
     
    +   png_debug1(1, "in %s retrieval function", "pHYs");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_pHYs) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "pHYs");
    -
           if (res_x != NULL)
           {
              *res_x = info_ptr->x_pixels_per_unit;
    @@ -492,7 +490,7 @@ png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr,
           }
        }
     
    -   return (retval);
    +   return retval;
     }
     #endif /* pHYs */
     #endif /* INCH_CONVERSIONS */
    @@ -506,9 +504,9 @@ png_byte PNGAPI
     png_get_channels(png_const_structrp png_ptr, png_const_inforp info_ptr)
     {
        if (png_ptr != NULL && info_ptr != NULL)
    -      return(info_ptr->channels);
    +      return info_ptr->channels;
     
    -   return (0);
    +   return 0;
     }
     
     #ifdef PNG_READ_SUPPORTED
    @@ -516,9 +514,9 @@ png_const_bytep PNGAPI
     png_get_signature(png_const_structrp png_ptr, png_const_inforp info_ptr)
     {
        if (png_ptr != NULL && info_ptr != NULL)
    -      return(info_ptr->signature);
    +      return info_ptr->signature;
     
    -   return (NULL);
    +   return NULL;
     }
     #endif
     
    @@ -527,17 +525,17 @@ png_uint_32 PNGAPI
     png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr,
         png_color_16p *background)
     {
    +   png_debug1(1, "in %s retrieval function", "bKGD");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_bKGD) != 0 &&
            background != NULL)
        {
    -      png_debug1(1, "in %s retrieval function", "bKGD");
    -
           *background = &(info_ptr->background);
    -      return (PNG_INFO_bKGD);
    +      return PNG_INFO_bKGD;
        }
     
    -   return (0);
    +   return 0;
     }
     #endif
     
    @@ -552,6 +550,8 @@ png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr,
         double *white_x, double *white_y, double *red_x, double *red_y,
         double *green_x, double *green_y, double *blue_x, double *blue_y)
     {
    +   png_debug1(1, "in %s retrieval function", "cHRM");
    +
        /* Quiet API change: this code used to only return the end points if a cHRM
         * chunk was present, but the end points can also come from iCCP or sRGB
         * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and
    @@ -561,8 +561,6 @@ png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr,
        if (png_ptr != NULL && info_ptr != NULL &&
           (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "cHRM");
    -
           if (white_x != NULL)
              *white_x = png_float(png_ptr,
                  info_ptr->colorspace.end_points_xy.whitex, "cHRM white X");
    @@ -587,10 +585,10 @@ png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr,
           if (blue_y != NULL)
              *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey,
                  "cHRM blue Y");
    -      return (PNG_INFO_cHRM);
    +      return PNG_INFO_cHRM;
        }
     
    -   return (0);
    +   return 0;
     }
     
     png_uint_32 PNGAPI
    @@ -599,11 +597,11 @@ png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr,
         double *green_Y, double *green_Z, double *blue_X, double *blue_Y,
         double *blue_Z)
     {
    +   png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)");
    -
           if (red_X != NULL)
              *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X,
                  "cHRM red X");
    @@ -631,10 +629,10 @@ png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr,
           if (blue_Z != NULL)
              *blue_Z = png_float(png_ptr,
                  info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z");
    -      return (PNG_INFO_cHRM);
    +      return PNG_INFO_cHRM;
        }
     
    -   return (0);
    +   return 0;
     }
     #  endif
     
    @@ -647,11 +645,11 @@ png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
         png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,
         png_fixed_point *int_blue_Z)
     {
    +   png_debug1(1, "in %s retrieval function", "cHRM_XYZ");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
           (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "cHRM_XYZ");
    -
           if (int_red_X != NULL)
              *int_red_X = info_ptr->colorspace.end_points_XYZ.red_X;
           if (int_red_Y != NULL)
    @@ -670,10 +668,10 @@ png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
              *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blue_Y;
           if (int_blue_Z != NULL)
              *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blue_Z;
    -      return (PNG_INFO_cHRM);
    +      return PNG_INFO_cHRM;
        }
     
    -   return (0);
    +   return 0;
     }
     
     png_uint_32 PNGAPI
    @@ -703,10 +701,10 @@ png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
              *blue_x = info_ptr->colorspace.end_points_xy.bluex;
           if (blue_y != NULL)
              *blue_y = info_ptr->colorspace.end_points_xy.bluey;
    -      return (PNG_INFO_cHRM);
    +      return PNG_INFO_cHRM;
        }
     
    -   return (0);
    +   return 0;
     }
     #  endif
     #endif
    @@ -724,10 +722,10 @@ png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
            file_gamma != NULL)
        {
           *file_gamma = info_ptr->colorspace.gamma;
    -      return (PNG_INFO_gAMA);
    +      return PNG_INFO_gAMA;
        }
     
    -   return (0);
    +   return 0;
     }
     #  endif
     
    @@ -744,10 +742,10 @@ png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr,
        {
           *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma,
               "png_get_gAMA");
    -      return (PNG_INFO_gAMA);
    +      return PNG_INFO_gAMA;
        }
     
    -   return (0);
    +   return 0;
     }
     #  endif
     #endif
    @@ -763,10 +761,10 @@ png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr,
           (info_ptr->valid & PNG_INFO_sRGB) != 0 && file_srgb_intent != NULL)
        {
           *file_srgb_intent = info_ptr->colorspace.rendering_intent;
    -      return (PNG_INFO_sRGB);
    +      return PNG_INFO_sRGB;
        }
     
    -   return (0);
    +   return 0;
     }
     #endif
     
    @@ -790,10 +788,10 @@ png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
            */
           if (compression_type != NULL)
              *compression_type = PNG_COMPRESSION_TYPE_BASE;
    -      return (PNG_INFO_iCCP);
    +      return PNG_INFO_iCCP;
        }
     
    -   return (0);
    +   return 0;
     
     }
     #endif
    @@ -803,13 +801,15 @@ int PNGAPI
     png_get_sPLT(png_const_structrp png_ptr, png_inforp info_ptr,
         png_sPLT_tpp spalettes)
     {
    +   png_debug1(1, "in %s retrieval function", "sPLT");
    +
        if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL)
        {
           *spalettes = info_ptr->splt_palettes;
           return info_ptr->splt_palettes_num;
        }
     
    -   return (0);
    +   return 0;
     }
     #endif
     
    @@ -835,10 +835,10 @@ png_get_eXIf_1(png_const_structrp png_ptr, png_const_inforp info_ptr,
        {
           *num_exif = info_ptr->num_exif;
           *exif = info_ptr->exif;
    -      return (PNG_INFO_eXIf);
    +      return PNG_INFO_eXIf;
        }
     
    -   return (0);
    +   return 0;
     }
     #endif
     
    @@ -853,10 +853,10 @@ png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
            (info_ptr->valid & PNG_INFO_hIST) != 0 && hist != NULL)
        {
           *hist = info_ptr->hist;
    -      return (PNG_INFO_hIST);
    +      return PNG_INFO_hIST;
        }
     
    -   return (0);
    +   return 0;
     }
     #endif
     
    @@ -869,7 +869,7 @@ png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr,
        png_debug1(1, "in %s retrieval function", "IHDR");
     
        if (png_ptr == NULL || info_ptr == NULL)
    -      return (0);
    +      return 0;
     
        if (width != NULL)
            *width = info_ptr->width;
    @@ -901,7 +901,7 @@ png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr,
            info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,
            info_ptr->compression_type, info_ptr->filter_type);
     
    -   return (1);
    +   return 1;
     }
     
     #ifdef PNG_oFFs_SUPPORTED
    @@ -918,10 +918,10 @@ png_get_oFFs(png_const_structrp png_ptr, png_const_inforp info_ptr,
           *offset_x = info_ptr->x_offset;
           *offset_y = info_ptr->y_offset;
           *unit_type = (int)info_ptr->offset_unit_type;
    -      return (PNG_INFO_oFFs);
    +      return PNG_INFO_oFFs;
        }
     
    -   return (0);
    +   return 0;
     }
     #endif
     
    @@ -945,10 +945,10 @@ png_get_pCAL(png_const_structrp png_ptr, png_inforp info_ptr,
           *nparams = (int)info_ptr->pcal_nparams;
           *units = info_ptr->pcal_units;
           *params = info_ptr->pcal_params;
    -      return (PNG_INFO_pCAL);
    +      return PNG_INFO_pCAL;
        }
     
    -   return (0);
    +   return 0;
     }
     #endif
     
    @@ -960,6 +960,8 @@ png_uint_32 PNGAPI
     png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
         int *unit, png_fixed_point *width, png_fixed_point *height)
     {
    +   png_debug1(1, "in %s retrieval function", "sCAL");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_sCAL) != 0)
        {
    @@ -971,10 +973,10 @@ png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr,
           *width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width");
           *height = png_fixed(png_ptr, atof(info_ptr->scal_s_height),
               "sCAL height");
    -      return (PNG_INFO_sCAL);
    +      return PNG_INFO_sCAL;
        }
     
    -   return(0);
    +   return 0;
     }
     #    endif /* FLOATING_ARITHMETIC */
     #  endif /* FIXED_POINT */
    @@ -983,32 +985,36 @@ png_uint_32 PNGAPI
     png_get_sCAL(png_const_structrp png_ptr, png_const_inforp info_ptr,
         int *unit, double *width, double *height)
     {
    +   png_debug1(1, "in %s retrieval function", "sCAL(float)");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_sCAL) != 0)
        {
           *unit = info_ptr->scal_unit;
           *width = atof(info_ptr->scal_s_width);
           *height = atof(info_ptr->scal_s_height);
    -      return (PNG_INFO_sCAL);
    +      return PNG_INFO_sCAL;
        }
     
    -   return(0);
    +   return 0;
     }
     #  endif /* FLOATING POINT */
     png_uint_32 PNGAPI
     png_get_sCAL_s(png_const_structrp png_ptr, png_const_inforp info_ptr,
         int *unit, png_charpp width, png_charpp height)
     {
    +   png_debug1(1, "in %s retrieval function", "sCAL(str)");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_sCAL) != 0)
        {
           *unit = info_ptr->scal_unit;
           *width = info_ptr->scal_s_width;
           *height = info_ptr->scal_s_height;
    -      return (PNG_INFO_sCAL);
    +      return PNG_INFO_sCAL;
        }
     
    -   return(0);
    +   return 0;
     }
     #endif /* sCAL */
     
    @@ -1043,7 +1049,7 @@ png_get_pHYs(png_const_structrp png_ptr, png_const_inforp info_ptr,
           }
        }
     
    -   return (retval);
    +   return retval;
     }
     #endif /* pHYs */
     
    @@ -1059,10 +1065,10 @@ png_get_PLTE(png_const_structrp png_ptr, png_inforp info_ptr,
           *palette = info_ptr->palette;
           *num_palette = info_ptr->num_palette;
           png_debug1(3, "num_palette = %d", *num_palette);
    -      return (PNG_INFO_PLTE);
    +      return PNG_INFO_PLTE;
        }
     
    -   return (0);
    +   return 0;
     }
     
     #ifdef PNG_sBIT_SUPPORTED
    @@ -1076,10 +1082,10 @@ png_get_sBIT(png_const_structrp png_ptr, png_inforp info_ptr,
            (info_ptr->valid & PNG_INFO_sBIT) != 0 && sig_bit != NULL)
        {
           *sig_bit = &(info_ptr->sig_bit);
    -      return (PNG_INFO_sBIT);
    +      return PNG_INFO_sBIT;
        }
     
    -   return (0);
    +   return 0;
     }
     #endif
     
    @@ -1090,7 +1096,7 @@ png_get_text(png_const_structrp png_ptr, png_inforp info_ptr,
     {
        if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0)
        {
    -      png_debug1(1, "in 0x%lx retrieval function",
    +      png_debug1(1, "in text retrieval function, chunk typeid = 0x%lx",
              (unsigned long)png_ptr->chunk_name);
     
           if (text_ptr != NULL)
    @@ -1105,7 +1111,7 @@ png_get_text(png_const_structrp png_ptr, png_inforp info_ptr,
        if (num_text != NULL)
           *num_text = 0;
     
    -   return(0);
    +   return 0;
     }
     #endif
     
    @@ -1120,10 +1126,10 @@ png_get_tIME(png_const_structrp png_ptr, png_inforp info_ptr,
            (info_ptr->valid & PNG_INFO_tIME) != 0 && mod_time != NULL)
        {
           *mod_time = &(info_ptr->mod_time);
    -      return (PNG_INFO_tIME);
    +      return PNG_INFO_tIME;
        }
     
    -   return (0);
    +   return 0;
     }
     #endif
     
    @@ -1133,11 +1139,12 @@ png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr,
         png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color)
     {
        png_uint_32 retval = 0;
    +
    +   png_debug1(1, "in %s retrieval function", "tRNS");
    +
        if (png_ptr != NULL && info_ptr != NULL &&
            (info_ptr->valid & PNG_INFO_tRNS) != 0)
        {
    -      png_debug1(1, "in %s retrieval function", "tRNS");
    -
           if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
           {
              if (trans_alpha != NULL)
    @@ -1169,7 +1176,7 @@ png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr,
           }
        }
     
    -   return (retval);
    +   return retval;
     }
     #endif
     
    @@ -1184,7 +1191,7 @@ png_get_unknown_chunks(png_const_structrp png_ptr, png_inforp info_ptr,
           return info_ptr->unknown_chunks_num;
        }
     
    -   return (0);
    +   return 0;
     }
     #endif
     
    @@ -1280,7 +1287,7 @@ png_get_palette_max(png_const_structp png_ptr, png_const_infop info_ptr)
        if (png_ptr != NULL && info_ptr != NULL)
           return png_ptr->num_palette_max;
     
    -   return (-1);
    +   return -1;
     }
     #  endif
     #endif
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
    index e98d49cf0ccd9..e238ccdb9a4d9 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
    @@ -31,7 +31,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      */
    -/* libpng version 1.6.40 */
    +/* libpng version 1.6.43 */
     
     /* Copyright (c) 2018-2023 Cosmin Truta */
     /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngpread.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngpread.c
    index a98b201325612..816631cae189b 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngpread.c
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngpread.c
    @@ -29,7 +29,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * Copyright (c) 2018 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -173,10 +173,10 @@ png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr)
            num_to_check);
        png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check);
     
    -   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
    +   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check) != 0)
        {
           if (num_checked < 4 &&
    -          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
    +          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4) != 0)
              png_error(png_ptr, "Not a PNG file");
     
           else
    @@ -322,6 +322,14 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr)
           png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length);
        }
     
    +#endif
    +#ifdef PNG_READ_eXIf_SUPPORTED
    +   else if (png_ptr->chunk_name == png_eXIf)
    +   {
    +      PNG_PUSH_SAVE_BUFFER_IF_FULL
    +      png_handle_eXIf(png_ptr, info_ptr, png_ptr->push_length);
    +   }
    +
     #endif
     #ifdef PNG_READ_sRGB_SUPPORTED
        else if (chunk_name == png_sRGB)
    @@ -1117,7 +1125,7 @@ png_voidp PNGAPI
     png_get_progressive_ptr(png_const_structrp png_ptr)
     {
        if (png_ptr == NULL)
    -      return (NULL);
    +      return NULL;
     
        return png_ptr->io_ptr;
     }
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
    index 914d0b97b1da9..18424542b00bf 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
    @@ -29,7 +29,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * Copyright (c) 2018-2023 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -64,7 +64,7 @@
      * still required (as of 2011-05-02.)
      */
     #ifndef _POSIX_SOURCE
    -# define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */
    +#  define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */
     #endif
     
     #ifndef PNG_VERSION_INFO_ONLY
    @@ -218,13 +218,27 @@
     #endif /* PNG_ARM_NEON_OPT > 0 */
     
     #ifndef PNG_MIPS_MSA_OPT
    -#  if defined(__mips_msa) && (__mips_isa_rev >= 5) && defined(PNG_ALIGNED_MEMORY_SUPPORTED)
    +#  if defined(__mips_msa) && (__mips_isa_rev >= 5) && \
    +   defined(PNG_ALIGNED_MEMORY_SUPPORTED)
     #     define PNG_MIPS_MSA_OPT 2
     #  else
     #     define PNG_MIPS_MSA_OPT 0
     #  endif
     #endif
     
    +#ifndef PNG_MIPS_MMI_OPT
    +#  ifdef PNG_MIPS_MMI
    +#    if defined(__mips_loongson_mmi) && (_MIPS_SIM == _ABI64) && \
    +     defined(PNG_ALIGNED_MEMORY_SUPPORTED)
    +#       define PNG_MIPS_MMI_OPT 1
    +#    else
    +#       define PNG_MIPS_MMI_OPT 0
    +#    endif
    +#  else
    +#    define PNG_MIPS_MMI_OPT 0
    +#  endif
    +#endif
    +
     #ifndef PNG_POWERPC_VSX_OPT
     #  if defined(__PPC64__) && defined(__ALTIVEC__) && defined(__VSX__)
     #     define PNG_POWERPC_VSX_OPT 2
    @@ -233,13 +247,21 @@
     #  endif
     #endif
     
    +#ifndef PNG_LOONGARCH_LSX_OPT
    +#  if defined(__loongarch_sx)
    +#     define PNG_LOONGARCH_LSX_OPT 1
    +#  else
    +#     define PNG_LOONGARCH_LSX_OPT 0
    +#  endif
    +#endif
    +
     #ifndef PNG_INTEL_SSE_OPT
     #   ifdef PNG_INTEL_SSE
           /* Only check for SSE if the build configuration has been modified to
            * enable SSE optimizations.  This means that these optimizations will
            * be off by default.  See contrib/intel for more details.
            */
    -#     if defined(__SSE4_1__) || defined(__AVX__) || defined(__SSSE3__) || \
    +#      if defined(__SSE4_1__) || defined(__AVX__) || defined(__SSSE3__) || \
            defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \
            (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
     #         define PNG_INTEL_SSE_OPT 1
    @@ -276,7 +298,6 @@
     #endif
     
     #if PNG_MIPS_MSA_OPT > 0
    -#  define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_msa
     #  ifndef PNG_MIPS_MSA_IMPLEMENTATION
     #     if defined(__mips_msa)
     #        if defined(__clang__)
    @@ -292,11 +313,28 @@
     
     #  ifndef PNG_MIPS_MSA_IMPLEMENTATION
     #     define PNG_MIPS_MSA_IMPLEMENTATION 1
    +#     define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_mips
     #  endif
     #else
     #  define PNG_MIPS_MSA_IMPLEMENTATION 0
     #endif /* PNG_MIPS_MSA_OPT > 0 */
     
    +#if PNG_MIPS_MMI_OPT > 0
    +#  ifndef PNG_MIPS_MMI_IMPLEMENTATION
    +#     if defined(__mips_loongson_mmi) && (_MIPS_SIM == _ABI64)
    +#        define PNG_MIPS_MMI_IMPLEMENTATION 2
    +#     else /* !defined __mips_loongson_mmi  || _MIPS_SIM != _ABI64 */
    +#        define PNG_MIPS_MMI_IMPLEMENTATION 0
    +#     endif /* __mips_loongson_mmi  && _MIPS_SIM == _ABI64 */
    +#  endif /* !PNG_MIPS_MMI_IMPLEMENTATION */
    +
    +#   if PNG_MIPS_MMI_IMPLEMENTATION > 0
    +#      define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_mips
    +#   endif
    +#else
    +#   define PNG_MIPS_MMI_IMPLEMENTATION 0
    +#endif /* PNG_MIPS_MMI_OPT > 0 */
    +
     #if PNG_POWERPC_VSX_OPT > 0
     #  define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_vsx
     #  define PNG_POWERPC_VSX_IMPLEMENTATION 1
    @@ -304,6 +342,12 @@
     #  define PNG_POWERPC_VSX_IMPLEMENTATION 0
     #endif
     
    +#if PNG_LOONGARCH_LSX_OPT > 0
    +#   define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_lsx
    +#   define PNG_LOONGARCH_LSX_IMPLEMENTATION 1
    +#else
    +#   define PNG_LOONGARCH_LSX_IMPLEMENTATION 0
    +#endif
     
     /* Is this a build of a DLL where compilation of the object modules requires
      * different preprocessor settings to those required for a simple library?  If
    @@ -542,18 +586,8 @@
         */
     #  include 
     
    -#  if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \
    -    defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC)
    -   /* We need to check that  hasn't already been included earlier
    -    * as it seems it doesn't agree with , yet we should really use
    -    *  if possible.
    -    */
    -#    if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__)
    -#      include 
    -#    endif
    -#  else
    -#    include 
    -#  endif
    +#  include 
    +
     #  if defined(_AMIGA) && defined(__SASC) && defined(_M68881)
        /* Amiga SAS/C: We must include builtin FPU functions when compiling using
         * MATH=68881
    @@ -1334,7 +1368,7 @@ PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop
         row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
     #endif
     
    -#if PNG_MIPS_MSA_OPT > 0
    +#if PNG_MIPS_MSA_IMPLEMENTATION == 1
     PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_msa,(png_row_infop row_info,
         png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
     PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_msa,(png_row_infop
    @@ -1351,6 +1385,23 @@ PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_msa,(png_row_infop
         row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
     #endif
     
    +#if PNG_MIPS_MMI_IMPLEMENTATION > 0
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_mmi,(png_row_infop row_info,
    +    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_mmi,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_mmi,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_mmi,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_mmi,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_mmi,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_mmi,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +#endif
    +
     #if PNG_POWERPC_VSX_OPT > 0
     PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_vsx,(png_row_infop row_info,
         png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    @@ -1383,6 +1434,23 @@ PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop
         row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
     #endif
     
    +#if PNG_LOONGARCH_LSX_IMPLEMENTATION == 1
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_lsx,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_lsx,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_lsx,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_lsx,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_lsx,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_lsx,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_lsx,(png_row_infop
    +    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
    +#endif
    +
     /* Choose the best filter to use and filter the row data */
     PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
         png_row_infop row_info),PNG_EMPTY);
    @@ -2122,17 +2190,27 @@ PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon,
        (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
     #endif
     
    -#if PNG_MIPS_MSA_OPT > 0
    -PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_msa,
    +#if PNG_MIPS_MSA_IMPLEMENTATION == 1
    +PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_mips,
        (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
     #endif
     
    +#  if PNG_MIPS_MMI_IMPLEMENTATION > 0
    +PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_mips,
    +   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
    +#  endif
    +
     #  if PNG_INTEL_SSE_IMPLEMENTATION > 0
     PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_sse2,
        (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
     #  endif
     #endif
     
    +#if PNG_LOONGARCH_LSX_OPT > 0
    +PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_lsx,
    +    (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
    +#endif
    +
     PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword, (png_structrp png_ptr,
        png_const_charp key, png_bytep new_key), PNG_EMPTY);
     
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
    index 3631e60f36ba2..e9e9447754523 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
    @@ -29,7 +29,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * Copyright (c) 2018-2019 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -596,7 +596,11 @@ png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row)
     #endif
     
     #ifdef PNG_READ_TRANSFORMS_SUPPORTED
    -   if (png_ptr->transformations)
    +   if (png_ptr->transformations
    +#     ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
    +         || png_ptr->num_palette_max >= 0
    +#     endif
    +      )
           png_do_read_transformations(png_ptr, &row_info);
     #endif
     
    @@ -813,7 +817,7 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr)
     #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
        /* Report invalid palette index; added at libng-1.5.10 */
        if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
    -       png_ptr->num_palette_max > png_ptr->num_palette)
    +       png_ptr->num_palette_max >= png_ptr->num_palette)
           png_benign_error(png_ptr, "Read palette index exceeding num_palette");
     #endif
     
    @@ -1077,6 +1081,8 @@ void PNGAPI
     png_read_png(png_structrp png_ptr, png_inforp info_ptr,
         int transforms, voidp params)
     {
    +   png_debug(1, "in png_read_png");
    +
        if (png_ptr == NULL || info_ptr == NULL)
           return;
     
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
    index 843eb5fff2adb..a393de4b79d07 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
    @@ -29,7 +29,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * Copyright (c) 2018-2019 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -318,21 +318,20 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode,
        int compose = 0;
        png_fixed_point file_gamma;
     
    -   png_debug(1, "in png_set_alpha_mode");
    +   png_debug(1, "in png_set_alpha_mode_fixed");
     
        if (png_rtran_ok(png_ptr, 0) == 0)
           return;
     
        output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/);
     
    -   /* Validate the value to ensure it is in a reasonable range. The value
    +   /* Validate the value to ensure it is in a reasonable range.  The value
         * is expected to be 1 or greater, but this range test allows for some
    -    * viewing correction values.  The intent is to weed out users of this API
    -    * who use the inverse of the gamma value accidentally!  Since some of these
    -    * values are reasonable this may have to be changed:
    +    * viewing correction values.  The intent is to weed out the API users
    +    * who might use the inverse of the gamma value accidentally!
         *
    -    * 1.6.x: changed from 0.07..3 to 0.01..100 (to accommodate the optimal 16-bit
    -    * gamma of 36, and its reciprocal.)
    +    * In libpng 1.6.0, we changed from 0.07..3 to 0.01..100, to accommodate
    +    * the optimal 16-bit gamma of 36 and its reciprocal.
         */
        if (output_gamma < 1000 || output_gamma > 10000000)
           png_error(png_ptr, "output gamma out of expected range");
    @@ -469,7 +468,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette,
           int i;
     
           png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr,
    -          (png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte))));
    +          (png_alloc_size_t)num_palette);
           for (i = 0; i < num_palette; i++)
              png_ptr->quantize_index[i] = (png_byte)i;
        }
    @@ -486,7 +485,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette,
     
              /* Initialize an array to sort colors */
              png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr,
    -             (png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte))));
    +             (png_alloc_size_t)num_palette);
     
              /* Initialize the quantize_sort array */
              for (i = 0; i < num_palette; i++)
    @@ -620,11 +619,9 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette,
     
              /* Initialize palette index arrays */
              png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr,
    -             (png_alloc_size_t)((png_uint_32)num_palette *
    -             (sizeof (png_byte))));
    +             (png_alloc_size_t)num_palette);
              png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr,
    -             (png_alloc_size_t)((png_uint_32)num_palette *
    -             (sizeof (png_byte))));
    +             (png_alloc_size_t)num_palette);
     
              /* Initialize the sort array */
              for (i = 0; i < num_palette; i++)
    @@ -789,12 +786,11 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette,
           size_t num_entries = ((size_t)1 << total_bits);
     
           png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr,
    -          (png_alloc_size_t)(num_entries * (sizeof (png_byte))));
    +          (png_alloc_size_t)(num_entries));
     
    -      distance = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)(num_entries *
    -          (sizeof (png_byte))));
    +      distance = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)num_entries);
     
    -      memset(distance, 0xff, num_entries * (sizeof (png_byte)));
    +      memset(distance, 0xff, num_entries);
     
           for (i = 0; i < num_palette; i++)
           {
    @@ -998,7 +994,7 @@ void PNGFAPI
     png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action,
         png_fixed_point red, png_fixed_point green)
     {
    -   png_debug(1, "in png_set_rgb_to_gray");
    +   png_debug(1, "in png_set_rgb_to_gray_fixed");
     
        /* Need the IHDR here because of the check on color_type below. */
        /* TODO: fix this */
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
    index 524297c5a10b7..5280140d12bcb 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
    @@ -29,7 +29,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * Copyright (c) 2018-2022 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -54,7 +54,7 @@ png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf)
        if (uval > PNG_UINT_31_MAX)
           png_error(png_ptr, "PNG unsigned integer out of range");
     
    -   return (uval);
    +   return uval;
     }
     
     #if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED)
    @@ -168,7 +168,7 @@ png_read_sig(png_structrp png_ptr, png_inforp info_ptr)
        if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check) != 0)
        {
           if (num_checked < 4 &&
    -          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
    +          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4) != 0)
              png_error(png_ptr, "Not a PNG file");
           else
              png_error(png_ptr, "PNG file corrupted by ASCII conversion");
    @@ -199,7 +199,7 @@ png_read_chunk_header(png_structrp png_ptr)
        /* Put the chunk name into png_ptr->chunk_name. */
        png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4);
     
    -   png_debug2(0, "Reading %lx chunk, length = %lu",
    +   png_debug2(0, "Reading chunk typeid = 0x%lx, length = %lu",
            (unsigned long)png_ptr->chunk_name, (unsigned long)length);
     
        /* Reset the crc and run it over the chunk name. */
    @@ -266,10 +266,10 @@ png_crc_finish(png_structrp png_ptr, png_uint_32 skip)
           else
              png_chunk_error(png_ptr, "CRC error");
     
    -      return (1);
    +      return 1;
        }
     
    -   return (0);
    +   return 0;
     }
     
     /* Compare the CRC stored in the PNG file with that calculated by libpng from
    @@ -305,11 +305,11 @@ png_crc_error(png_structrp png_ptr)
        if (need_crc != 0)
        {
           crc = png_get_uint_32(crc_bytes);
    -      return ((int)(crc != png_ptr->crc));
    +      return crc != png_ptr->crc;
        }
     
        else
    -      return (0);
    +      return 0;
     }
     
     #if defined(PNG_READ_iCCP_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) ||\
    @@ -449,8 +449,7 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
                 png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
           }
     
    -#if ZLIB_VERNUM >= 0x1290 && \
    -   defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_IGNORE_ADLER32)
    +#ifdef PNG_DISABLE_ADLER32_CHECK_SUPPORTED
           if (((png_ptr->options >> PNG_IGNORE_ADLER32) & 3) == PNG_OPTION_ON)
              /* Turn off validation of the ADLER32 checksum in IDAT chunks */
              ret = inflateValidate(&png_ptr->zstream, 0);
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
    index 62612a0227893..f53ab6fa1d18f 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
    @@ -29,7 +29,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * Copyright (c) 2018-2023 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -791,11 +791,11 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
     {
        int i;
     
    -   png_debug1(1, "in %lx storage function", png_ptr == NULL ? 0xabadca11U :
    -      (unsigned long)png_ptr->chunk_name);
    +   png_debug1(1, "in text storage function, chunk typeid = 0x%lx",
    +      png_ptr == NULL ? 0xabadca11UL : (unsigned long)png_ptr->chunk_name);
     
        if (png_ptr == NULL || info_ptr == NULL || num_text <= 0 || text_ptr == NULL)
    -      return(0);
    +      return 0;
     
        /* Make sure we have enough space in the "text" array in info_struct
         * to hold all of the incoming text_ptr objects.  This compare can't overflow
    @@ -975,7 +975,7 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
           png_debug1(3, "transferred text chunk %d", info_ptr->num_text);
        }
     
    -   return(0);
    +   return 0;
     }
     #endif
     
    @@ -1091,6 +1091,8 @@ png_set_sPLT(png_const_structrp png_ptr,
     {
        png_sPLT_tp np;
     
    +   png_debug1(1, "in %s storage function", "sPLT");
    +
        if (png_ptr == NULL || info_ptr == NULL || nentries <= 0 || entries == NULL)
           return;
     
    @@ -1565,7 +1567,7 @@ void PNGAPI
     png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr,
         png_bytepp row_pointers)
     {
    -   png_debug1(1, "in %s storage function", "rows");
    +   png_debug(1, "in png_set_rows");
     
        if (png_ptr == NULL || info_ptr == NULL)
           return;
    @@ -1584,6 +1586,8 @@ png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr,
     void PNGAPI
     png_set_compression_buffer_size(png_structrp png_ptr, size_t size)
     {
    +   png_debug(1, "in png_set_compression_buffer_size");
    +
        if (png_ptr == NULL)
           return;
     
    @@ -1655,6 +1659,8 @@ void PNGAPI
     png_set_user_limits(png_structrp png_ptr, png_uint_32 user_width_max,
         png_uint_32 user_height_max)
     {
    +   png_debug(1, "in png_set_user_limits");
    +
        /* Images with dimensions larger than these limits will be
         * rejected by png_set_IHDR().  To accept any PNG datastream
         * regardless of dimensions, set both limits to 0x7fffffff.
    @@ -1670,6 +1676,8 @@ png_set_user_limits(png_structrp png_ptr, png_uint_32 user_width_max,
     void PNGAPI
     png_set_chunk_cache_max(png_structrp png_ptr, png_uint_32 user_chunk_cache_max)
     {
    +   png_debug(1, "in png_set_chunk_cache_max");
    +
        if (png_ptr != NULL)
           png_ptr->user_chunk_cache_max = user_chunk_cache_max;
     }
    @@ -1679,6 +1687,8 @@ void PNGAPI
     png_set_chunk_malloc_max(png_structrp png_ptr,
         png_alloc_size_t user_chunk_malloc_max)
     {
    +   png_debug(1, "in png_set_chunk_malloc_max");
    +
        if (png_ptr != NULL)
           png_ptr->user_chunk_malloc_max = user_chunk_malloc_max;
     }
    diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
    index 89a62191b6f18..2350057e70e54 100644
    --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
    +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
    @@ -29,7 +29,7 @@
      * However, the following notice accompanied the original version of this
      * file and, per its terms, should not be removed:
      *
    - * Copyright (c) 2018 Cosmin Truta
    + * Copyright (c) 2018-2024 Cosmin Truta
      * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
      * Copyright (c) 1996-1997 Andreas Dilger
      * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
    @@ -131,10 +131,10 @@ png_set_interlace_handling(png_structrp png_ptr)
        if (png_ptr != 0 && png_ptr->interlaced != 0)
        {
           png_ptr->transformations |= PNG_INTERLACE;
    -      return (7);
    +      return 7;
        }
     
    -   return (1);
    +   return 1;
     }
     #endif
     
    @@ -526,6 +526,8 @@ png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
        png_bytep dp = row; /* destination pointer */
        png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */
     
    +   png_debug(1, "in png_do_strip_channel");
    +
        /* At the start sp will point to the first byte to copy and dp to where
         * it is copied to.  ep always points just beyond the end of the row, so
         * the loop simply copies (channels-1) channels until sp reaches ep.
    @@ -726,6 +728,8 @@ png_do_bgr(png_row_infop row_info, png_bytep row)
     void /* PRIVATE */
     png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
     {
    +   png_debug(1, "in png_do_check_palette_indexes");
    +
        if (png_ptr->num_palette < (1 << row_info->bit_depth) &&
           png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */
        {
    @@ -736,7 +740,7 @@ png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
            * forms produced on either GCC or MSVC.
            */
           int padding = PNG_PADBITS(row_info->pixel_depth, row_info->width);
    -      png_bytep rp = png_ptr->row_buf + row_info->rowbytes - 1;
    +      png_bytep rp = png_ptr->row_buf + row_info->rowbytes;
     
           switch (row_info->bit_depth)
           {
    @@ -861,7 +865,7 @@ png_voidp PNGAPI
     png_get_user_transform_ptr(png_const_structrp png_ptr)
     {
        if (png_ptr == NULL)
    -      return (NULL);
    +      return NULL;
     
        return png_ptr->user_transform_ptr;
     }
    diff --git a/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java b/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java
    index d785823ea8e82..7446c196a46f3 100644
    --- a/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java
    +++ b/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java
    @@ -25,15 +25,37 @@
     package sun.awt;
     
     import java.awt.RenderingHints;
    -import static java.awt.RenderingHints.*;
    +
    +import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
    +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT;
    +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR;
    +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB;
    +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR;
    +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB;
    +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
    +
     import static java.util.concurrent.TimeUnit.SECONDS;
     import java.awt.color.ColorSpace;
    -import java.awt.image.*;
    +
    +import java.awt.Window;
    +import java.awt.event.WindowAdapter;
    +import java.awt.event.WindowEvent;
    +import java.awt.event.WindowFocusListener;
    +import java.awt.image.BufferedImage;
    +import java.awt.image.ColorModel;
    +import java.awt.image.ComponentColorModel;
    +import java.awt.image.DataBuffer;
    +import java.awt.image.DataBufferByte;
    +import java.awt.image.Raster;
    +import java.awt.image.WritableRaster;
     import java.io.BufferedReader;
     import java.io.IOException;
     import java.io.InputStreamReader;
     import java.security.AccessController;
     import java.security.PrivilegedAction;
    +import java.util.Arrays;
    +
    +import sun.awt.X11.XBaseWindow;
     
     import sun.security.action.GetIntegerAction;
     import com.sun.java.swing.plaf.gtk.GTKConstants.TextDirection;
    @@ -491,4 +513,113 @@ public static boolean isGtkVerbose() {
             return AccessController.doPrivileged((PrivilegedAction)()
                     -> Boolean.getBoolean("jdk.gtk.verbose"));
         }
    +
    +    private static volatile Boolean isOnWayland = null;
    +
    +    @SuppressWarnings("removal")
    +    public static boolean isOnWayland() {
    +        Boolean result = isOnWayland;
    +        if (result == null) {
    +            synchronized (GTK_LOCK) {
    +                result = isOnWayland;
    +                if (result == null) {
    +                    isOnWayland
    +                            = result
    +                            = AccessController.doPrivileged(
    +                            (PrivilegedAction) () -> {
    +                                final String display =
    +                                        System.getenv("WAYLAND_DISPLAY");
    +
    +                                return display != null
    +                                        && !display.trim().isEmpty();
    +                            }
    +                    );
    +                }
    +            }
    +        }
    +        return result;
    +    }
    +
    +    @Override
    +    public boolean isRunningOnWayland() {
    +        return isOnWayland();
    +    }
    +
    +    // We rely on the X11 input grab mechanism, but for the Wayland session
    +    // it only works inside the XWayland server, so mouse clicks outside of it
    +    // will not be detected.
    +    // (window decorations, pure Wayland applications, desktop, etc.)
    +    //
    +    // As a workaround, we can dismiss menus when the window loses focus.
    +    //
    +    // However, there are "blind spots" though, which, when clicked, don't
    +    // transfer the focus away and don't dismiss the menu
    +    // (e.g. the window's own title or the area in the side dock without
    +    // application icons).
    +    private static final WindowFocusListener waylandWindowFocusListener;
    +
    +    static {
    +        if (isOnWayland()) {
    +            waylandWindowFocusListener = new WindowAdapter() {
    +                @Override
    +                public void windowLostFocus(WindowEvent e) {
    +                    Window window = e.getWindow();
    +                    Window oppositeWindow = e.getOppositeWindow();
    +
    +                    // The focus can move between the window calling the popup,
    +                    // and the popup window itself.
    +                    // We only dismiss the popup in other cases.
    +                    if (oppositeWindow != null) {
    +                        if (window == oppositeWindow.getParent() ) {
    +                            addWaylandWindowFocusListenerToWindow(oppositeWindow);
    +                            return;
    +                        }
    +                        if (window.getParent() == oppositeWindow) {
    +                            return;
    +                        }
    +                    }
    +
    +                    window.removeWindowFocusListener(this);
    +
    +                    // AWT
    +                    XBaseWindow.ungrabInput();
    +
    +                    // Swing
    +                    window.dispatchEvent(new UngrabEvent(window));
    +                }
    +            };
    +        } else {
    +            waylandWindowFocusListener = null;
    +        }
    +    }
    +
    +    private static void addWaylandWindowFocusListenerToWindow(Window window) {
    +        if (!Arrays
    +                .asList(window.getWindowFocusListeners())
    +                .contains(waylandWindowFocusListener)
    +        ) {
    +            window.addWindowFocusListener(waylandWindowFocusListener);
    +        }
    +    }
    +
    +    @Override
    +    public void dismissPopupOnFocusLostIfNeeded(Window invoker) {
    +        if (!isOnWayland() || invoker == null) {
    +            return;
    +        }
    +
    +        addWaylandWindowFocusListenerToWindow(invoker);
    +    }
    +
    +    @Override
    +    public void dismissPopupOnFocusLostIfNeededCleanUp(Window invoker) {
    +        if (!isOnWayland() || invoker == null) {
    +            return;
    +        }
    +
    +        invoker.removeWindowFocusListener(waylandWindowFocusListener);
    +        for (Window ownedWindow : invoker.getOwnedWindows()) {
    +            ownedWindow.removeWindowFocusListener(waylandWindowFocusListener);
    +        }
    +    }
     }
    diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java b/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java
    index 117e38652412f..78b8844f061f4 100644
    --- a/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java
    +++ b/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -58,9 +58,13 @@
      * @since       1.5
      */
     
    -import jdk.internal.misc.Unsafe;
     import java.util.HashMap;
     
    +import jdk.internal.misc.Unsafe;
    +
    +import static java.nio.charset.StandardCharsets.ISO_8859_1;
    +import static java.nio.charset.StandardCharsets.UTF_8;
    +
     public final class XAtom {
     
         // Order of lock:  XAWTLock -> XAtom.class
    @@ -308,12 +312,7 @@ public void setPropertyUTF8(long window, String str) {
                 throw new IllegalStateException("Atom should be initialized");
             }
             checkWindow(window);
    -        byte[] bdata = null;
    -        try {
    -            bdata = str.getBytes("UTF-8");
    -        } catch (java.io.UnsupportedEncodingException uee) {
    -            uee.printStackTrace();
    -        }
    +        byte[] bdata = str.getBytes(UTF_8);
             if (bdata != null) {
                 setAtomData(window, XA_UTF8_STRING.atom, bdata);
             }
    @@ -327,12 +326,7 @@ public void setProperty8(long window, String str) {
                 throw new IllegalStateException("Atom should be initialized");
             }
             checkWindow(window);
    -        byte[] bdata = null;
    -        try {
    -            bdata = str.getBytes("ISO-8859-1");
    -        } catch (java.io.UnsupportedEncodingException uee) {
    -            uee.printStackTrace();
    -        }
    +        byte[] bdata = str.getBytes(ISO_8859_1);
             if (bdata != null) {
                 setAtomData(window, XA_STRING, bdata);
             }
    diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XBaseWindow.java b/src/java.desktop/unix/classes/sun/awt/X11/XBaseWindow.java
    index 939fbcbbcd4dd..aaad0f15047c9 100644
    --- a/src/java.desktop/unix/classes/sun/awt/X11/XBaseWindow.java
    +++ b/src/java.desktop/unix/classes/sun/awt/X11/XBaseWindow.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -25,9 +25,13 @@
     
     package sun.awt.X11;
     
    -import java.awt.*;
    -import sun.awt.*;
    -import java.util.*;
    +import java.awt.Dimension;
    +import java.awt.Point;
    +import java.awt.Rectangle;
    +import java.util.HashSet;
    +import java.util.Set;
    +
    +import sun.awt.SunToolkit;
     import sun.util.logging.PlatformLogger;
     
     public class XBaseWindow {
    @@ -912,7 +916,7 @@ public boolean grabInput() {
             }
         }
     
    -    static void ungrabInput() {
    +    public static void ungrabInput() {
             XToolkit.awtLock();
             try {
                 XBaseWindow grabWindow = XAwtState.getGrabWindow();
    diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XDragSourceContextPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XDragSourceContextPeer.java
    index c885d36b20e95..29d3c962e796c 100644
    --- a/src/java.desktop/unix/classes/sun/awt/X11/XDragSourceContextPeer.java
    +++ b/src/java.desktop/unix/classes/sun/awt/X11/XDragSourceContextPeer.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -27,6 +27,7 @@
     
     import java.awt.Component;
     import java.awt.Cursor;
    +import java.awt.Toolkit;
     import java.awt.Window;
     
     import java.awt.datatransfer.DataFlavor;
    @@ -392,6 +393,40 @@ private boolean updateSourceAction(int state) {
             return true;
         }
     
    +    /**
    +     * Our X11 code expects the drop target window to be a top level window
    +     * and to have the XA_WM_STATE property.
    +     * This is not true when performing drag and drop from XWayland
    +     * to a native Wayland application.
    +     * In this case XWayland creates a dummy window with only one property,
    +     * XdndAware.
    +     *
    +     * @param window to test
    +     * @return true if window has XdndAware property when running under Wayland
    +     */
    +    private static boolean isXWaylandDndAwareWindow(long window) {
    +        Toolkit toolkit = Toolkit.getDefaultToolkit();
    +        if (!(toolkit instanceof SunToolkit)
    +                || !((SunToolkit) toolkit).isRunningOnWayland()) {
    +            return false;
    +        }
    +
    +        WindowPropertyGetter wpg =
    +            new WindowPropertyGetter(window, XDnDConstants.XA_XdndAware, 0, 1,
    +                                     false, XConstants.AnyPropertyType);
    +
    +        try {
    +            int status =
    +                wpg.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
    +
    +            return status == XConstants.Success
    +                   && wpg.getData() != 0
    +                   && wpg.getActualType() == XAtom.XA_ATOM;
    +        } finally {
    +            wpg.dispose();
    +        }
    +    }
    +
         /**
          * Returns the client window under the specified root subwindow.
          */
    @@ -400,6 +435,10 @@ private static long findClientWindow(long window) {
                 return window;
             }
     
    +        if (isXWaylandDndAwareWindow(window)) {
    +            return window;
    +        }
    +
             Set children = XlibUtil.getChildWindows(window);
             for (Long child : children) {
                 long win = findClientWindow(child);
    diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java b/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java
    index ab929c324c218..37d9969001368 100644
    --- a/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java
    +++ b/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -24,16 +24,21 @@
      */
     package sun.awt.X11;
     
    -import java.awt.*;
    -import java.awt.peer.*;
    -import java.awt.event.*;
    -
    -import java.awt.image.BufferedImage;
    -import java.awt.geom.Point2D;
    +import java.awt.Dimension;
    +import java.awt.Graphics;
    +import java.awt.MenuItem;
    +import java.awt.Point;
    +import java.awt.Rectangle;
    +import java.awt.Toolkit;
    +import java.awt.Window;
     
     import java.util.Vector;
    +
    +import sun.awt.SunToolkit;
     import sun.util.logging.PlatformLogger;
     
    +import javax.swing.SwingUtilities;
    +
     public class XMenuWindow extends XBaseMenuWindow {
     
         /************************************************
    @@ -389,6 +394,16 @@ boolean ensureCreated() {
             return true;
         }
     
    +    protected Window getMenuTarget() {
    +        if (target instanceof Window targetWindow) {
    +            return targetWindow;
    +        } else {
    +            return target == null
    +                    ? null
    +                    : SwingUtilities.getWindowAncestor(target);
    +        }
    +    }
    +
         /**
          * Init window if it's not inited yet
          * and show it at specified coordinates
    @@ -405,6 +420,9 @@ void show(Rectangle bounds) {
             XToolkit.awtLock();
             try {
                 reshape(bounds.x, bounds.y, bounds.width, bounds.height);
    +            if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) {
    +                sunToolkit.dismissPopupOnFocusLostIfNeeded(getMenuTarget());
    +            }
                 xSetVisible(true);
                 //Fixed 6267182: PIT: Menu is not visible after
                 //showing and disposing a file dialog, XToolkit
    @@ -421,6 +439,9 @@ void show(Rectangle bounds) {
         void hide() {
             selectItem(null, false);
             xSetVisible(false);
    +        if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) {
    +            sunToolkit.dismissPopupOnFocusLostIfNeededCleanUp(getMenuTarget());
    +        }
         }
     
         /************************************************
    diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XNETProtocol.java b/src/java.desktop/unix/classes/sun/awt/X11/XNETProtocol.java
    index 2d82ace98454f..81c31b277fb99 100644
    --- a/src/java.desktop/unix/classes/sun/awt/X11/XNETProtocol.java
    +++ b/src/java.desktop/unix/classes/sun/awt/X11/XNETProtocol.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -23,14 +23,17 @@
      * questions.
      */
     
    -
     package sun.awt.X11;
     
     import java.awt.Frame;
    +import java.nio.charset.Charset;
     
     import sun.awt.IconInfo;
     import sun.util.logging.PlatformLogger;
     
    +import static java.nio.charset.StandardCharsets.US_ASCII;
    +import static java.nio.charset.StandardCharsets.UTF_8;
    +
     final class XNETProtocol extends XProtocol implements XStateProtocol, XLayerProtocol
     {
         private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XNETProtocol");
    @@ -382,22 +385,18 @@ public String getWMName() {
              * mandates UTF8_STRING for _NET_WM_NAME but at least sawfish-1.0
              * still uses STRING.  (mmm, moving targets...).
              */
    -        String charSet = "UTF8";
    +        Charset charSet = UTF_8;
             byte[] net_wm_name = XA_NET_WM_NAME.getByteArrayProperty(NetWindow, XA_UTF8_STRING.getAtom());
             if (net_wm_name == null) {
                 net_wm_name = XA_NET_WM_NAME.getByteArrayProperty(NetWindow, XAtom.XA_STRING);
    -            charSet = "ASCII";
    +            charSet = US_ASCII;
             }
     
             if (net_wm_name == null) {
                 return null;
             }
    -        try {
    -            net_wm_name_cache = new String(net_wm_name, charSet);
    -            return net_wm_name_cache;
    -        } catch (java.io.UnsupportedEncodingException uex) {
    -            return null;
    -        }
    +        net_wm_name_cache = new String(net_wm_name, charSet);
    +        return net_wm_name_cache;
         }
     
         /**
    diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XPopupMenuPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XPopupMenuPeer.java
    index a19f56249aec8..7a3bd18ae2846 100644
    --- a/src/java.desktop/unix/classes/sun/awt/X11/XPopupMenuPeer.java
    +++ b/src/java.desktop/unix/classes/sun/awt/X11/XPopupMenuPeer.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -24,12 +24,25 @@
      */
     package sun.awt.X11;
     
    -import java.awt.*;
    -import java.awt.peer.*;
    -import java.awt.event.*;
    +import java.awt.AWTEvent;
    +import java.awt.Component;
    +import java.awt.Dimension;
    +import java.awt.Event;
    +import java.awt.Font;
    +import java.awt.FontMetrics;
    +import java.awt.Graphics;
    +import java.awt.MenuItem;
    +import java.awt.Point;
    +import java.awt.PopupMenu;
    +import java.awt.Rectangle;
    +import java.awt.Toolkit;
    +import java.awt.event.KeyEvent;
    +import java.awt.event.MouseEvent;
     
    +import java.awt.peer.PopupMenuPeer;
     import java.util.Vector;
     import sun.awt.AWTAccessor;
    +import sun.awt.SunToolkit;
     import sun.util.logging.PlatformLogger;
     
     public class XPopupMenuPeer extends XMenuWindow implements PopupMenuPeer {
    @@ -125,6 +138,9 @@ public void show(Event e) {
                 //near the periphery of the screen, XToolkit
                 Rectangle bounds = getWindowBounds(pt, dim);
                 reshape(bounds);
    +            if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) {
    +                sunToolkit.dismissPopupOnFocusLostIfNeeded(getMenuTarget());
    +            }
                 xSetVisible(true);
                 toFront();
                 selectItem(null, false);
    diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java
    index 2fd47bf458a93..6d155c0bcc8b7 100644
    --- a/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java
    +++ b/src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java
    @@ -35,21 +35,43 @@
     import sun.awt.UNIXToolkit;
     import sun.awt.X11GraphicsConfig;
     import sun.awt.X11GraphicsDevice;
    +import sun.awt.screencast.ScreencastHelper;
     import sun.security.action.GetPropertyAction;
     
     @SuppressWarnings("removal")
     final class XRobotPeer implements RobotPeer {
     
         private static final boolean tryGtk;
    +    private static final String screenshotMethod;
    +    private static final String METHOD_X11 = "x11";
    +    private static final String METHOD_SCREENCAST = "dbusScreencast";
    +
         static {
             loadNativeLibraries();
    +
             tryGtk = Boolean.parseBoolean(
    -                            AccessController.doPrivileged(
    -                                    new GetPropertyAction("awt.robot.gtk", "true")
    -                            ));
    +                     AccessController.doPrivileged(
    +                             new GetPropertyAction("awt.robot.gtk",
    +                                     "true")
    +                     ));
    +
    +        boolean isOnWayland = false;
    +
    +        if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) {
    +            isOnWayland = sunToolkit.isRunningOnWayland();
    +        }
    +
    +        screenshotMethod = AccessController.doPrivileged(
    +                new GetPropertyAction(
    +                        "awt.robot.screenshotMethod",
    +                        isOnWayland
    +                            ? METHOD_SCREENCAST
    +                            : METHOD_X11
    +                ));
         }
    +
         private static volatile boolean useGtk;
    -    private final X11GraphicsConfig  xgc;
    +    private final X11GraphicsConfig xgc;
     
         XRobotPeer(X11GraphicsDevice gd) {
             xgc = (X11GraphicsConfig) gd.getDefaultConfiguration();
    @@ -100,15 +122,31 @@ public void keyRelease(int keycode) {
         @Override
         public int getRGBPixel(int x, int y) {
             int[] pixelArray = new int[1];
    -        getRGBPixelsImpl(xgc, x, y, 1, 1, pixelArray, useGtk);
    +        if (screenshotMethod.equals(METHOD_SCREENCAST)
    +            && ScreencastHelper.isAvailable()) {
    +
    +            ScreencastHelper.getRGBPixels(x, y, 1, 1, pixelArray);
    +        } else {
    +            getRGBPixelsImpl(xgc, x, y, 1, 1, pixelArray, useGtk);
    +        }
             return pixelArray[0];
         }
     
         @Override
    -    public int [] getRGBPixels(Rectangle bounds) {
    -        int[] pixelArray = new int[bounds.width*bounds.height];
    -        getRGBPixelsImpl(xgc, bounds.x, bounds.y, bounds.width, bounds.height,
    -                            pixelArray, useGtk);
    +    public int[] getRGBPixels(Rectangle bounds) {
    +        int[] pixelArray = new int[bounds.width * bounds.height];
    +        if (screenshotMethod.equals(METHOD_SCREENCAST)
    +            && ScreencastHelper.isAvailable()) {
    +
    +            ScreencastHelper.getRGBPixels(bounds.x, bounds.y,
    +                                          bounds.width, bounds.height,
    +                                          pixelArray);
    +        } else {
    +            getRGBPixelsImpl(xgc,
    +                             bounds.x, bounds.y,
    +                             bounds.width, bounds.height,
    +                             pixelArray, useGtk);
    +        }
             return pixelArray;
         }
     
    diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XWM.java b/src/java.desktop/unix/classes/sun/awt/X11/XWM.java
    index 409b12b1425dc..0365bffebcb28 100644
    --- a/src/java.desktop/unix/classes/sun/awt/X11/XWM.java
    +++ b/src/java.desktop/unix/classes/sun/awt/X11/XWM.java
    @@ -1369,6 +1369,9 @@ Insets guessInsets(XDecoratedPeer window) {
                   case UNITY_COMPIZ_WM:
                       res = new Insets(28, 1, 1, 1);
                       break;
    +              case MUTTER_WM:
    +                  res = new Insets(37, 0, 0, 0);
    +                  break;
                   case MOTIF_WM:
                   case OPENLOOK_WM:
                   default:
    @@ -1380,6 +1383,7 @@ Insets guessInsets(XDecoratedPeer window) {
             }
             return res;
         }
    +
         /*
          * Some buggy WMs ignore window gravity when processing
          * ConfigureRequest and position window as if the gravity is Static.
    diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java
    index 2587db4459ab7..6c63ec56e9d39 100644
    --- a/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java
    +++ b/src/java.desktop/unix/classes/sun/awt/X11/XWindowPeer.java
    @@ -47,7 +47,6 @@
     import java.awt.event.WindowEvent;
     import java.awt.peer.ComponentPeer;
     import java.awt.peer.WindowPeer;
    -import java.io.UnsupportedEncodingException;
     import java.security.AccessController;
     import java.security.PrivilegedAction;
     import java.util.ArrayList;
    @@ -67,6 +66,8 @@
     import sun.java2d.pipe.Region;
     import sun.util.logging.PlatformLogger;
     
    +import static java.nio.charset.StandardCharsets.UTF_8;
    +
     class XWindowPeer extends XPanelPeer implements WindowPeer,
                                                     DisplayChangedListener {
     
    @@ -772,6 +773,7 @@ protected Point getNewLocation(XConfigureEvent xe, int leftInset, int topInset)
                 // TODO this should be the default for every case.
                 switch (runningWM) {
                     case XWM.CDE_WM:
    +                case XWM.KDE2_WM:
                     case XWM.MOTIF_WM:
                     case XWM.METACITY_WM:
                     case XWM.MUTTER_WM:
    @@ -1358,12 +1360,7 @@ public String run() {
             }
             messageBuilder.append('"');
             messageBuilder.append('\0');
    -        final byte[] message;
    -        try {
    -            message = messageBuilder.toString().getBytes("UTF-8");
    -        } catch (UnsupportedEncodingException cannotHappen) {
    -            return;
    -        }
    +        final byte[] message = messageBuilder.toString().getBytes(UTF_8);
     
             XClientMessageEvent req = null;
     
    diff --git a/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java b/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java
    index 8206d18906f67..93f5eaf6656c0 100644
    --- a/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java
    +++ b/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -67,7 +67,7 @@ public Object run() {
                     System.loadLibrary("awt");
     
                     /*
    -                 * Note: The MToolkit object depends on the static initializer
    +                 * Note: The XToolkit object depends on the static initializer
                      * of X11GraphicsEnvironment to initialize the connection to
                      * the X11 server.
                      */
    diff --git a/src/java.desktop/unix/classes/sun/awt/XSettings.java b/src/java.desktop/unix/classes/sun/awt/XSettings.java
    index 812255ea850c9..4136a35017284 100644
    --- a/src/java.desktop/unix/classes/sun/awt/XSettings.java
    +++ b/src/java.desktop/unix/classes/sun/awt/XSettings.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -26,12 +26,10 @@
     package sun.awt;
     
     import java.awt.Color;
    -
    -import java.io.UnsupportedEncodingException;
    -
     import java.util.HashMap;
     import java.util.Map;
     
    +import static java.nio.charset.StandardCharsets.UTF_8;
     
     /**
      * Per-screen XSETTINGS.
    @@ -198,12 +196,7 @@ private String getString(int len)
             {
                 needBytes(len);
     
    -            String str = null;
    -            try {
    -                str = new String(data, idx, len, "UTF-8");
    -            } catch (UnsupportedEncodingException e) {
    -                // XXX: cannot happen, "UTF-8" is always supported
    -            }
    +            String str = new String(data, idx, len, UTF_8);
     
                 idx = (idx + len + 3) & ~0x3;
                 return str;
    diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java b/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java
    new file mode 100644
    index 0000000000000..78661587554e3
    --- /dev/null
    +++ b/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java
    @@ -0,0 +1,233 @@
    +/*
    + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
    + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    + *
    + * This code is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License version 2 only, as
    + * published by the Free Software Foundation.  Oracle designates this
    + * particular file as subject to the "Classpath" exception as provided
    + * by Oracle in the LICENSE file that accompanied this code.
    + *
    + * This code is distributed in the hope that it will be useful, but WITHOUT
    + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    + * version 2 for more details (a copy is included in the LICENSE file that
    + * accompanied this code).
    + *
    + * You should have received a copy of the GNU General Public License version
    + * 2 along with this work; if not, write to the Free Software Foundation,
    + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    + *
    + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    + * or visit www.oracle.com if you need additional information or have any
    + * questions.
    + */
    +
    +package sun.awt.screencast;
    +
    +import sun.awt.UNIXToolkit;
    +import sun.java2d.pipe.Region;
    +import sun.security.action.GetPropertyAction;
    +
    +import java.awt.GraphicsConfiguration;
    +import java.awt.GraphicsEnvironment;
    +import java.awt.Rectangle;
    +import java.awt.Toolkit;
    +import java.awt.geom.AffineTransform;
    +import java.security.AccessController;
    +import java.util.Arrays;
    +import java.util.List;
    +import java.util.Set;
    +import java.util.Timer;
    +import java.util.TimerTask;
    +import java.util.stream.IntStream;
    +
    +/**
    + * Helper class for grabbing pixels from the screen using the
    + * 
    + * org.freedesktop.portal.ScreenCast API
    + */
    +
    +@SuppressWarnings("removal")
    +public class ScreencastHelper {
    +
    +    static final boolean SCREENCAST_DEBUG;
    +    private static final boolean IS_NATIVE_LOADED;
    +
    +
    +    private static final int ERROR = -1;
    +    private static final int DENIED = -11;
    +    private static final int OUT_OF_BOUNDS = -12;
    +
    +    private static final int DELAY_BEFORE_SESSION_CLOSE = 2000;
    +
    +    private static volatile TimerTask timerTask = null;
    +    private static final Timer timerCloseSession
    +            = new Timer("auto-close screencast session", true);
    +
    +
    +    private ScreencastHelper() {
    +    }
    +
    +    static {
    +        SCREENCAST_DEBUG = Boolean.parseBoolean(
    +                               AccessController.doPrivileged(
    +                                       new GetPropertyAction(
    +                                               "awt.robot.screenshotDebug",
    +                                               "false"
    +                                       )
    +                               ));
    +
    +        boolean loadFailed = false;
    +
    +        if (!(Toolkit.getDefaultToolkit() instanceof UNIXToolkit tk
    +              && tk.loadGTK())
    +              || !loadPipewire(SCREENCAST_DEBUG)) {
    +
    +            System.err.println(
    +                    "Could not load native libraries for ScreencastHelper"
    +            );
    +
    +            loadFailed = true;
    +        }
    +
    +        IS_NATIVE_LOADED = !loadFailed;
    +    }
    +
    +    public static boolean isAvailable() {
    +        return IS_NATIVE_LOADED;
    +    }
    +
    +    private static native boolean loadPipewire(boolean screencastDebug);
    +
    +    private static native int getRGBPixelsImpl(
    +            int x, int y, int width, int height,
    +            int[] pixelArray,
    +            int[] affectedScreensBoundsArray,
    +            String token
    +    );
    +
    +    private static List getSystemScreensBounds() {
    +        return Arrays
    +                .stream(GraphicsEnvironment
    +                        .getLocalGraphicsEnvironment()
    +                        .getScreenDevices())
    +                .map(graphicsDevice -> {
    +                    GraphicsConfiguration gc =
    +                            graphicsDevice.getDefaultConfiguration();
    +                    Rectangle screen = gc.getBounds();
    +                    AffineTransform tx = gc.getDefaultTransform();
    +
    +                    return new Rectangle(
    +                            Region.clipRound(screen.x * tx.getScaleX()),
    +                            Region.clipRound(screen.y * tx.getScaleY()),
    +                            Region.clipRound(screen.width * tx.getScaleX()),
    +                            Region.clipRound(screen.height * tx.getScaleY())
    +                    );
    +                })
    +                .toList();
    +    }
    +
    +    private static synchronized native void closeSession();
    +
    +    private static void timerCloseSessionRestart() {
    +        if (timerTask != null) {
    +            timerTask.cancel();
    +        }
    +
    +        timerTask = new TimerTask() {
    +            @Override
    +            public void run() {
    +                closeSession();
    +            }
    +        };
    +
    +        timerCloseSession.schedule(timerTask, DELAY_BEFORE_SESSION_CLOSE);
    +    }
    +
    +    public static synchronized void getRGBPixels(
    +            int x, int y, int width, int height, int[] pixelArray
    +    ) {
    +        if (!IS_NATIVE_LOADED) return;
    +
    +        timerCloseSessionRestart();
    +
    +        Rectangle captureArea = new Rectangle(x, y, width, height);
    +
    +        List affectedScreenBounds = getSystemScreensBounds()
    +                .stream()
    +                .filter(captureArea::intersects)
    +                .toList();
    +
    +        if (SCREENCAST_DEBUG) {
    +            System.out.printf("// getRGBPixels in %s, affectedScreenBounds %s\n",
    +                    captureArea, affectedScreenBounds);
    +        }
    +
    +        if (affectedScreenBounds.isEmpty()) {
    +            if (SCREENCAST_DEBUG) {
    +                System.out.println("// getRGBPixels - requested area "
    +                        + "outside of any screen");
    +            }
    +            return;
    +        }
    +
    +        int retVal;
    +        Set tokensForRectangle =
    +                TokenStorage.getTokens(affectedScreenBounds);
    +
    +        int[] affectedScreenBoundsArray = affectedScreenBounds
    +                .stream()
    +                .filter(captureArea::intersects)
    +                .flatMapToInt(bounds -> IntStream.of(
    +                        bounds.x, bounds.y,
    +                        bounds.width, bounds.height
    +                ))
    +                .toArray();
    +
    +        for (TokenItem tokenItem : tokensForRectangle) {
    +            retVal = getRGBPixelsImpl(
    +                    x, y, width, height,
    +                    pixelArray,
    +                    affectedScreenBoundsArray,
    +                    tokenItem.token
    +            );
    +
    +            if (retVal >= 0) { // we have received a screen data
    +                return;
    +            } else if (!checkReturnValue(retVal)) {
    +                return;
    +            } // else, try other tokens
    +        }
    +
    +        // we do not have a saved token or it did not work,
    +        // try without the token to show the system's permission request window
    +        retVal = getRGBPixelsImpl(
    +                x, y, width, height,
    +                pixelArray,
    +                affectedScreenBoundsArray,
    +                null
    +        );
    +
    +        checkReturnValue(retVal);
    +    }
    +
    +    private static boolean checkReturnValue(int retVal) {
    +        if (retVal == DENIED) {
    +            // user explicitly denied the capture, no more tries.
    +            throw new SecurityException(
    +                    "Screen Capture in the selected area was not allowed"
    +            );
    +        } else if (retVal == ERROR) {
    +            if (SCREENCAST_DEBUG) {
    +                System.err.println("Screen capture failed.");
    +            }
    +        } else if (retVal == OUT_OF_BOUNDS) {
    +            if (SCREENCAST_DEBUG) {
    +                System.err.println(
    +                        "Token does not provide access to requested area.");
    +            }
    +        }
    +        return retVal != ERROR;
    +    }
    +}
    diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/TokenItem.java b/src/java.desktop/unix/classes/sun/awt/screencast/TokenItem.java
    new file mode 100644
    index 0000000000000..74055d821b24a
    --- /dev/null
    +++ b/src/java.desktop/unix/classes/sun/awt/screencast/TokenItem.java
    @@ -0,0 +1,154 @@
    +/*
    + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
    + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    + *
    + * This code is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License version 2 only, as
    + * published by the Free Software Foundation.  Oracle designates this
    + * particular file as subject to the "Classpath" exception as provided
    + * by Oracle in the LICENSE file that accompanied this code.
    + *
    + * This code is distributed in the hope that it will be useful, but WITHOUT
    + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    + * version 2 for more details (a copy is included in the LICENSE file that
    + * accompanied this code).
    + *
    + * You should have received a copy of the GNU General Public License version
    + * 2 along with this work; if not, write to the Free Software Foundation,
    + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    + *
    + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    + * or visit www.oracle.com if you need additional information or have any
    + * questions.
    + */
    +
    +package sun.awt.screencast;
    +
    +import java.awt.Dimension;
    +import java.awt.Rectangle;
    +import java.util.ArrayList;
    +import java.util.Arrays;
    +import java.util.List;
    +import java.util.stream.Collectors;
    +import java.util.stream.IntStream;
    +
    +import static sun.awt.screencast.ScreencastHelper.SCREENCAST_DEBUG;
    +
    +/**
    + * Helper class used by {@link TokenStorage} as restore token record
    + * with its associated screen boundaries.
    + *
    + * It helps in serialization/deserialization of screen boundaries
    + * to/from string format.
    + *
    + * The screen boundary is represented as {@code _x_y_width_height}
    + * and can be repeated several times.
    + */
    +final class TokenItem {
    +
    +    final String token;
    +    final List allowedScreensBounds;
    +
    +    public TokenItem(String token, int[] allowedScreenBounds) {
    +        if (token == null || token.isBlank()) {
    +            throw new RuntimeException("empty or null tokens are not allowed");
    +        }
    +        if (allowedScreenBounds.length % 4 != 0) {
    +            throw new RuntimeException("array with incorrect length provided");
    +        }
    +
    +        this.token = token;
    +
    +        this.allowedScreensBounds = IntStream
    +                        .iterate(0,
    +                                i -> i < allowedScreenBounds.length,
    +                                i -> i + 4)
    +                        .mapToObj(i -> new Rectangle(
    +                                allowedScreenBounds[i], allowedScreenBounds[i+1],
    +                                allowedScreenBounds[i+2], allowedScreenBounds[i+3]
    +                        ))
    +                        .collect(Collectors.toList());
    +    }
    +
    +    public boolean hasAllScreensWithExactMatch(List bounds) {
    +        return allowedScreensBounds.containsAll(bounds);
    +    }
    +
    +    public boolean hasAllScreensOfSameSize(List screenSizes) {
    +        // We also need to consider duplicates, since there may be
    +        // multiple screens of the same size.
    +        // The token item must also have at least the same number
    +        // of screens with that size.
    +
    +        List tokenSizes = allowedScreensBounds
    +                .stream()
    +                .map(bounds -> new Dimension(bounds.width, bounds.height))
    +                .collect(Collectors.toCollection(ArrayList::new));
    +
    +        return screenSizes.size() == screenSizes
    +                .stream()
    +                .filter(tokenSizes::remove)
    +                .count();
    +    }
    +
    +    private static final int MAX_SIZE = 50000;
    +    private static final int MIN_SIZE = 1;
    +
    +    public boolean hasValidBounds() {
    +        //This check is very rough, in order to filter out abnormal values
    +        for (Rectangle bounds : allowedScreensBounds) {
    +            if (bounds.x < -MAX_SIZE || bounds.x > MAX_SIZE
    +                    || bounds.y < -MAX_SIZE || bounds.y > MAX_SIZE
    +                    || bounds.width < MIN_SIZE || bounds.width > MAX_SIZE
    +                    || bounds.height < MIN_SIZE || bounds.height > MAX_SIZE
    +            ) {
    +                return false;
    +            }
    +        }
    +        return true;
    +    }
    +
    +    public String dump() {
    +        StringBuilder sb = new StringBuilder();
    +        for (Rectangle bounds : allowedScreensBounds) {
    +            sb.append("_%d_%d_%d_%d"
    +                    .formatted(bounds.x, bounds.y, bounds.width, bounds.height));
    +        }
    +        return sb.toString();
    +    }
    +
    +    public static TokenItem parse(String token, Object input) {
    +        if (token == null || input == null) return null;
    +
    +        try {
    +            int[] integers = Arrays.stream(String.valueOf(input)
    +                    .split("_"))
    +                    .filter(s -> !s.isBlank())
    +                    .mapToInt(Integer::parseInt)
    +                    .toArray();
    +
    +            if (integers.length % 4 == 0) {
    +                TokenItem tokenItem = new TokenItem(token, integers);
    +                if (tokenItem.hasValidBounds()) {
    +                    return tokenItem;
    +                }
    +            }
    +        } catch (NumberFormatException ignored) {}
    +
    +        if (SCREENCAST_DEBUG) {
    +            System.err.printf("Malformed record for token %s: %s\n",
    +                    token, input);
    +        }
    +        return null;
    +    }
    +
    +    @Override
    +    public String toString() {
    +        StringBuilder sb = new StringBuilder("Token: " + token + "\n");
    +        for (Rectangle bounds : allowedScreensBounds) {
    +            sb.append("\t").append(bounds).append("\n");
    +        }
    +        return sb.toString();
    +    }
    +}
    \ No newline at end of file
    diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java
    new file mode 100644
    index 0000000000000..b05ff7f8c4add
    --- /dev/null
    +++ b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java
    @@ -0,0 +1,476 @@
    +/*
    + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
    + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    + *
    + * This code is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License version 2 only, as
    + * published by the Free Software Foundation.  Oracle designates this
    + * particular file as subject to the "Classpath" exception as provided
    + * by Oracle in the LICENSE file that accompanied this code.
    + *
    + * This code is distributed in the hope that it will be useful, but WITHOUT
    + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    + * version 2 for more details (a copy is included in the LICENSE file that
    + * accompanied this code).
    + *
    + * You should have received a copy of the GNU General Public License version
    + * 2 along with this work; if not, write to the Free Software Foundation,
    + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    + *
    + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    + * or visit www.oracle.com if you need additional information or have any
    + * questions.
    + */
    +
    +package sun.awt.screencast;
    +
    +import java.awt.Dimension;
    +import java.awt.Rectangle;
    +import java.io.BufferedReader;
    +import java.io.BufferedWriter;
    +import java.io.IOException;
    +import java.nio.file.FileSystems;
    +import java.nio.file.Files;
    +import java.nio.file.Path;
    +import java.nio.file.WatchEvent;
    +import java.nio.file.WatchKey;
    +import java.nio.file.WatchService;
    +import java.nio.file.attribute.PosixFilePermission;
    +import java.security.AccessController;
    +import java.security.PrivilegedAction;
    +import java.util.Arrays;
    +import java.util.HashSet;
    +import java.util.LinkedHashSet;
    +import java.util.List;
    +import java.util.Objects;
    +import java.util.Properties;
    +import java.util.Set;
    +
    +import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
    +import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
    +import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
    +import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
    +import static sun.awt.screencast.ScreencastHelper.SCREENCAST_DEBUG;
    +
    +/**
    + * Helper class for persistent storage of ScreenCast restore tokens
    + * and associated screen boundaries.
    + *
    + * The restore token allows the ScreenCast session to be restored
    + * with previously granted screen access permissions.
    + */
    +final class TokenStorage {
    +
    +    private TokenStorage() {}
    +
    +    private static final String REL_NAME =
    +            ".java/robot/screencast-tokens.properties";
    +    private static final String REL_NAME_SECONDARY =
    +            ".awt/robot/screencast-tokens.properties";
    +
    +    private static final Properties PROPS = new Properties();
    +    private static final Path PROPS_PATH;
    +    private static final Path PROP_FILENAME;
    +
    +    @SuppressWarnings("removal")
    +    private static void doPrivilegedRunnable(Runnable runnable) {
    +        AccessController.doPrivileged((PrivilegedAction) () -> {
    +            runnable.run();
    +            return null;
    +        });
    +    }
    +
    +    static {
    +        @SuppressWarnings("removal")
    +        Path propsPath = AccessController
    +                .doPrivileged((PrivilegedAction) () -> setupPath());
    +
    +        PROPS_PATH = propsPath;
    +
    +        if (PROPS_PATH != null) {
    +            PROP_FILENAME = PROPS_PATH.getFileName();
    +            if (SCREENCAST_DEBUG) {
    +                System.out.println("Token storage: using " + PROPS_PATH);
    +            }
    +            setupWatch();
    +        } else {
    +            // We can still work with tokens,
    +            // but they are not saved between sessions.
    +            PROP_FILENAME = null;
    +        }
    +    }
    +
    +    private static Path setupPath() {
    +        String userHome = System.getProperty("user.home", null);
    +        if (userHome == null) {
    +            return null;
    +        }
    +
    +        Path path = Path.of(userHome, REL_NAME);
    +        Path secondaryPath = Path.of(userHome, REL_NAME_SECONDARY);
    +
    +        boolean copyFromSecondary = !Files.isWritable(path)
    +                && Files.isWritable(secondaryPath);
    +
    +        Path workdir = path.getParent();
    +
    +        if (!Files.isWritable(path)) {
    +            if (!Files.exists(workdir)) {
    +                try {
    +                    Files.createDirectories(workdir);
    +                } catch (Exception e) {
    +                    if (SCREENCAST_DEBUG) {
    +                        System.err.printf("Token storage: cannot create" +
    +                                " directory %s %s\n", workdir, e);
    +                    }
    +                    return null;
    +                }
    +            }
    +
    +            if (!Files.isWritable(workdir)) {
    +                if (SCREENCAST_DEBUG) {
    +                    System.err.printf("Token storage: %s is not writable\n", workdir);
    +                }
    +                return null;
    +            }
    +        }
    +
    +        try {
    +            Files.setPosixFilePermissions(
    +                    workdir,
    +                    Set.of(PosixFilePermission.OWNER_READ,
    +                           PosixFilePermission.OWNER_WRITE,
    +                           PosixFilePermission.OWNER_EXECUTE)
    +            );
    +        } catch (IOException e) {
    +            if (SCREENCAST_DEBUG) {
    +                System.err.printf("Token storage: cannot set permissions " +
    +                        "for directory %s %s\n", workdir, e);
    +            }
    +        }
    +
    +        if (copyFromSecondary) {
    +            if (SCREENCAST_DEBUG) {
    +                System.out.println("Token storage: copying from the secondary location "
    +                                        + secondaryPath);
    +            }
    +            synchronized (PROPS) {
    +                if (readTokens(secondaryPath)) {
    +                    store(path, "copy from the secondary location");
    +                }
    +            }
    +        } else if (Files.exists(path)) {
    +            if (!setFilePermission(path)) {
    +                return null;
    +            }
    +
    +            readTokens(path);
    +        }
    +
    +        return path;
    +    }
    +
    +    private static boolean setFilePermission(Path path) {
    +        try {
    +            Files.setPosixFilePermissions(
    +                    path,
    +                    Set.of(PosixFilePermission.OWNER_READ,
    +                           PosixFilePermission.OWNER_WRITE)
    +            );
    +            return true;
    +        } catch (IOException e) {
    +            if (SCREENCAST_DEBUG) {
    +                System.err.printf("Token storage: failed to set " +
    +                        "property file permission %s %s\n", path, e);
    +            }
    +        }
    +        return false;
    +    }
    +
    +    private static class WatcherThread extends Thread {
    +        private final WatchService watcher;
    +
    +        public WatcherThread(WatchService watchService) {
    +            this.watcher = watchService;
    +            setName("ScreencastWatcher");
    +            setDaemon(true);
    +        }
    +
    +        @Override
    +        public void run() {
    +            if (SCREENCAST_DEBUG) {
    +                System.out.println("ScreencastWatcher: started");
    +            }
    +            for (;;) {
    +                WatchKey key;
    +                try {
    +                    key = watcher.take();
    +                } catch (InterruptedException e) {
    +                    if (SCREENCAST_DEBUG) {
    +                        System.err.println("ScreencastWatcher: interrupted");
    +                    }
    +                    return;
    +                }
    +
    +                for (WatchEvent event: key.pollEvents()) {
    +                    WatchEvent.Kind kind = event.kind();
    +                    if (kind == OVERFLOW
    +                            || !event.context().equals(PROP_FILENAME)) {
    +                        continue;
    +                    }
    +
    +                    if (SCREENCAST_DEBUG) {
    +                        System.out.printf("ScreencastWatcher: %s %s\n",
    +                                kind, event.context());
    +                    }
    +
    +                    if (kind == ENTRY_CREATE) {
    +                        doPrivilegedRunnable(() -> setFilePermission(PROPS_PATH));
    +                    } else if (kind == ENTRY_MODIFY) {
    +                        doPrivilegedRunnable(() -> readTokens(PROPS_PATH));
    +                    } else if (kind == ENTRY_DELETE) {
    +                        synchronized (PROPS) {
    +                            PROPS.clear();
    +                        }
    +                    }
    +                }
    +
    +                key.reset();
    +            }
    +        }
    +    }
    +
    +    private static WatchService watchService;
    +
    +    private static void setupWatch() {
    +        doPrivilegedRunnable(() -> {
    +            try {
    +                watchService =
    +                        FileSystems.getDefault().newWatchService();
    +
    +                PROPS_PATH
    +                        .getParent()
    +                        .register(watchService,
    +                                ENTRY_CREATE,
    +                                ENTRY_DELETE,
    +                                ENTRY_MODIFY);
    +
    +            } catch (Exception e) {
    +                if (SCREENCAST_DEBUG) {
    +                    System.err.printf("Token storage: failed to setup " +
    +                            "file watch %s\n", e);
    +                }
    +            }
    +        });
    +
    +        if (watchService != null) {
    +            new WatcherThread(watchService).start();
    +        }
    +    }
    +
    +    // called from native
    +    private static void storeTokenFromNative(String oldToken,
    +                                             String newToken,
    +                                             int[] allowedScreenBounds) {
    +        if (SCREENCAST_DEBUG) {
    +            System.out.printf("// storeToken old: |%s| new |%s| " +
    +                            "allowed bounds %s\n",
    +                    oldToken, newToken,
    +                    Arrays.toString(allowedScreenBounds));
    +        }
    +
    +        if (allowedScreenBounds == null) return;
    +
    +        TokenItem tokenItem = new TokenItem(newToken, allowedScreenBounds);
    +
    +        if (SCREENCAST_DEBUG) {
    +            System.out.printf("// Storing TokenItem:\n%s\n", tokenItem);
    +        }
    +
    +        synchronized (PROPS) {
    +            String oldBoundsRecord = PROPS.getProperty(tokenItem.token, null);
    +            String newBoundsRecord = tokenItem.dump();
    +
    +            boolean changed = false;
    +
    +            if (oldBoundsRecord == null
    +                    || !oldBoundsRecord.equals(newBoundsRecord)) {
    +                PROPS.setProperty(tokenItem.token, newBoundsRecord);
    +                if (SCREENCAST_DEBUG) {
    +                    System.out.printf(
    +                            "// Writing new TokenItem:\n%s\n", tokenItem);
    +                }
    +                changed = true;
    +            }
    +
    +            if (oldToken != null && !oldToken.equals(newToken)) {
    +                // old token is no longer valid
    +                if (SCREENCAST_DEBUG) {
    +                    System.out.printf(
    +                            "// storeTokenFromNative old token |%s| is "
    +                                    + "no longer valid, removing\n", oldToken);
    +                }
    +
    +                PROPS.remove(oldToken);
    +                changed = true;
    +            }
    +
    +            if (changed) {
    +                doPrivilegedRunnable(() -> store(PROPS_PATH, "save tokens"));
    +            }
    +        }
    +    }
    +
    +    private static boolean readTokens(Path path) {
    +        if (path == null) return false;
    +
    +        try (BufferedReader reader = Files.newBufferedReader(path)) {
    +            synchronized (PROPS) {
    +                PROPS.clear();
    +                PROPS.load(reader);
    +            }
    +        } catch (IOException | IllegalArgumentException e) {
    +            if (SCREENCAST_DEBUG) {
    +                System.err.printf("""
    +                        Token storage: failed to load property file %s
    +                        %s
    +                        """, path, e);
    +            }
    +            return false;
    +        }
    +
    +        return true;
    +    }
    +
    +    static Set getTokens(List affectedScreenBounds) {
    +        // We need an ordered set to store tokens
    +        // with exact matches at the beginning.
    +        LinkedHashSet result = new LinkedHashSet<>();
    +
    +        Set malformed = new HashSet<>();
    +        List allTokenItems;
    +
    +        synchronized (PROPS) {
    +            allTokenItems =
    +                    PROPS.entrySet()
    +                    .stream()
    +                    .map(o -> {
    +                        String token = String.valueOf(o.getKey());
    +                        TokenItem tokenItem =
    +                                TokenItem.parse(token, o.getValue());
    +                        if (tokenItem == null) {
    +                            malformed.add(token);
    +                        }
    +                        return tokenItem;
    +                    })
    +                    .filter(Objects::nonNull)
    +                    .sorted((t1, t2) -> //Token with more screens preferred
    +                            t2.allowedScreensBounds.size()
    +                            - t1.allowedScreensBounds.size()
    +                    )
    +                    .toList();
    +        }
    +
    +        doPrivilegedRunnable(() -> removeMalformedRecords(malformed));
    +
    +        // 1. Try to find exact matches
    +        for (TokenItem tokenItem : allTokenItems) {
    +            if (tokenItem != null
    +                && tokenItem.hasAllScreensWithExactMatch(affectedScreenBounds)) {
    +
    +                result.add(tokenItem);
    +            }
    +        }
    +
    +        if (SCREENCAST_DEBUG) {
    +            System.out.println("// getTokens exact matches 1. " + result);
    +        }
    +
    +        // 2. Try screens of the same size but in different locations,
    +        // screens may have been moved while the token is still valid
    +        List dimensions =
    +                affectedScreenBounds
    +                .stream()
    +                .map(rectangle -> new Dimension(
    +                        rectangle.width,
    +                        rectangle.height
    +                ))
    +                .toList();
    +
    +        for (TokenItem tokenItem : allTokenItems) {
    +            if (tokenItem != null
    +                && tokenItem.hasAllScreensOfSameSize(dimensions)) {
    +
    +                result.add(tokenItem);
    +            }
    +        }
    +
    +        if (SCREENCAST_DEBUG) {
    +            System.out.println("// getTokens same sizes 2. " + result);
    +        }
    +
    +        // 3. add tokens with the same or greater number of screens
    +        // This is useful if we once received a token with one screen resolution
    +        // and the same screen was later scaled in the system.
    +        // In that case, the token is still valid.
    +
    +        allTokenItems
    +                .stream()
    +                .filter(t ->
    +                        t.allowedScreensBounds.size() >= affectedScreenBounds.size())
    +                .forEach(result::add);
    +
    +        return result;
    +    }
    +
    +    private static void removeMalformedRecords(Set malformedRecords) {
    +        if (!isWritable(PROPS_PATH)
    +            || malformedRecords == null
    +            || malformedRecords.isEmpty()) {
    +            return;
    +        }
    +
    +        synchronized (PROPS) {
    +            for (String token : malformedRecords) {
    +                Object remove = PROPS.remove(token);
    +                if (SCREENCAST_DEBUG) {
    +                    System.err.println("removing malformed record\n" + remove);
    +                }
    +            }
    +
    +            store(PROPS_PATH, "remove malformed records");
    +        }
    +    }
    +
    +    private static void store(Path path, String failMsg) {
    +        if (!isWritable(path)) {
    +            return;
    +        }
    +
    +        synchronized (PROPS) {
    +            try (BufferedWriter writer = Files.newBufferedWriter(path)) {
    +                PROPS.store(writer, null);
    +            } catch (IOException e) {
    +                if (SCREENCAST_DEBUG) {
    +                    System.err.printf(
    +                            "Token storage: unable to %s\n%s\n", failMsg, e);
    +                }
    +            }
    +        }
    +    }
    +
    +    private static boolean isWritable(Path path) {
    +        if (path == null
    +            || (Files.exists(path) && !Files.isWritable(path))) {
    +
    +            if (SCREENCAST_DEBUG) {
    +                System.err.printf(
    +                        "Token storage: %s is not writable\n", path);
    +            }
    +            return false;
    +        } else {
    +            return true;
    +        }
    +    }
    +}
    diff --git a/src/java.desktop/unix/classes/sun/font/FcFontConfiguration.java b/src/java.desktop/unix/classes/sun/font/FcFontConfiguration.java
    index 0ea741c1ccfe5..c65fa3744005b 100644
    --- a/src/java.desktop/unix/classes/sun/font/FcFontConfiguration.java
    +++ b/src/java.desktop/unix/classes/sun/font/FcFontConfiguration.java
    @@ -32,23 +32,24 @@
     import java.net.InetAddress;
     import java.net.UnknownHostException;
     import java.nio.charset.Charset;
    -import java.nio.charset.StandardCharsets;
     import java.nio.file.Files;
     import java.util.HashMap;
     import java.util.HashSet;
     import java.util.Locale;
     import java.util.Properties;
     import java.util.Scanner;
    +
     import sun.awt.FcFontManager;
     import sun.awt.FontConfiguration;
     import sun.awt.FontDescriptor;
     import sun.awt.SunToolkit;
    -import sun.font.CompositeFontDescriptor;
    -import sun.font.FontConfigManager.FontConfigInfo;
     import sun.font.FontConfigManager.FcCompFont;
     import sun.font.FontConfigManager.FontConfigFont;
    +import sun.font.FontConfigManager.FontConfigInfo;
     import sun.util.logging.PlatformLogger;
     
    +import static java.nio.charset.StandardCharsets.ISO_8859_1;
    +
     public class FcFontConfiguration extends FontConfiguration {
     
         /** Version of the cache file format understood by this code.
    @@ -178,7 +179,7 @@ protected FontDescriptor[] buildFontDescriptors(int fontIndex, int styleIndex) {
             String[] componentFaceNames = cfi[idx].getComponentFaceNames();
             FontDescriptor[] ret = new FontDescriptor[componentFaceNames.length];
             for (int i = 0; i < componentFaceNames.length; i++) {
    -            ret[i] = new FontDescriptor(componentFaceNames[i], StandardCharsets.ISO_8859_1.newEncoder(), new int[0]);
    +            ret[i] = new FontDescriptor(componentFaceNames[i], ISO_8859_1.newEncoder(), new int[0]);
             }
     
             return ret;
    @@ -319,7 +320,9 @@ protected void setOsNameAndVersion() {
                          * For Ubuntu the ID is "Ubuntu".
                          */
                         Properties props = new Properties();
    -                    props.load(new FileInputStream(f));
    +                    try (FileInputStream fis = new FileInputStream(f)) {
    +                        props.load(fis);
    +                    }
                         osName = props.getProperty("DISTRIB_ID");
                         osVersion =  props.getProperty("DISTRIB_RELEASE");
                 } else if ((f = new File("/etc/redhat-release")).canRead()) {
    @@ -416,10 +419,9 @@ private void writeFcInfo() {
                 File dir = fcInfoFile.getParentFile();
                 dir.mkdirs();
                 File tempFile = Files.createTempFile(dir.toPath(), "fcinfo", null).toFile();
    -            FileOutputStream fos = new FileOutputStream(tempFile);
    -            props.store(fos,
    -                      "JDK Font Configuration Generated File: *Do Not Edit*");
    -            fos.close();
    +            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
    +                props.store(fos, "JDK Font Configuration Generated File: *Do Not Edit*");
    +            }
                 boolean renamed = tempFile.renameTo(fcInfoFile);
                 if (!renamed && FontUtilities.debugFonts()) {
                     System.out.println("rename failed");
    diff --git a/src/java.desktop/unix/classes/sun/font/MFontConfiguration.java b/src/java.desktop/unix/classes/sun/font/MFontConfiguration.java
    index 8ba2ca7448eb9..e4e6d6d4fdda5 100644
    --- a/src/java.desktop/unix/classes/sun/font/MFontConfiguration.java
    +++ b/src/java.desktop/unix/classes/sun/font/MFontConfiguration.java
    @@ -27,8 +27,6 @@
     
     import sun.awt.FontConfiguration;
     import sun.awt.X11FontManager;
    -import sun.font.FontUtilities;
    -import sun.font.SunFontManager;
     import sun.util.logging.PlatformLogger;
     
     import java.io.File;
    @@ -109,7 +107,9 @@ protected void setOsNameAndVersion(){
                          * For Ubuntu the ID is "Ubuntu".
                          */
                         Properties props = new Properties();
    -                    props.load(new FileInputStream(f));
    +                    try (FileInputStream fis = new FileInputStream(f)) {
    +                        props.load(fis);
    +                    }
                         osName = props.getProperty("DISTRIB_ID");
                         osVersion =  props.getProperty("DISTRIB_RELEASE");
                     } else if ((f = new File("/etc/os-release")).canRead()) {
    diff --git a/src/java.desktop/unix/classes/sun/font/NativeFont.java b/src/java.desktop/unix/classes/sun/font/NativeFont.java
    index b30b6459c3bcb..3bd212b252576 100644
    --- a/src/java.desktop/unix/classes/sun/font/NativeFont.java
    +++ b/src/java.desktop/unix/classes/sun/font/NativeFont.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -32,10 +32,10 @@
     import java.awt.geom.GeneralPath;
     import java.awt.geom.Point2D;
     import java.awt.geom.Rectangle2D;
    -import java.io.UnsupportedEncodingException;
    -import java.lang.ref.WeakReference;
     import java.util.Locale;
     
    +import static java.nio.charset.StandardCharsets.UTF_8;
    +
     /*
      * Ideally there would be no native fonts used, and this class would be
      * unneeded and removed. Presently it is still needed until such time
    @@ -213,23 +213,11 @@ static boolean hasExternalBitmaps(String platName) {
                 pos = sb.indexOf("-0-", pos);
             };
             String xlfd = sb.toString();
    -        byte[] bytes = null;
    -        try {
    -            bytes = xlfd.getBytes("UTF-8");
    -        } catch (UnsupportedEncodingException e) {
    -            bytes = xlfd.getBytes();
    -        }
    -        return haveBitmapFonts(bytes);
    +        return haveBitmapFonts(xlfd.getBytes(UTF_8));
         }
     
         public static boolean fontExists(String xlfd) {
    -        byte[] bytes = null;
    -        try {
    -            bytes = xlfd.getBytes("UTF-8");
    -        } catch (UnsupportedEncodingException e) {
    -            bytes = xlfd.getBytes();
    -        }
    -        return fontExists(bytes);
    +        return fontExists(xlfd.getBytes(UTF_8));
         }
     
         private static native boolean haveBitmapFonts(byte[] xlfd);
    @@ -380,13 +368,7 @@ byte[] getPlatformNameBytes(int ptSize) {
             }
     
             String xlfd = sb.toString();
    -        byte[] bytes = null;
    -        try {
    -            bytes = xlfd.getBytes("UTF-8");
    -        } catch (UnsupportedEncodingException e) {
    -            bytes = xlfd.getBytes();
    -        }
    -        return bytes;
    +        return xlfd.getBytes(UTF_8);
         }
     
         public String toString() {
    diff --git a/src/java.desktop/unix/classes/sun/java2d/opengl/GLXGraphicsConfig.java b/src/java.desktop/unix/classes/sun/java2d/opengl/GLXGraphicsConfig.java
    index 536962bd82030..588dfbf882aef 100644
    --- a/src/java.desktop/unix/classes/sun/java2d/opengl/GLXGraphicsConfig.java
    +++ b/src/java.desktop/unix/classes/sun/java2d/opengl/GLXGraphicsConfig.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -216,7 +216,7 @@ public String toString() {
         }
     
         /**
    -     * The following methods are invoked from MToolkit or XToolkit.java and
    +     * The following methods are invoked from XToolkit.java and
          * X11ComponentPeer.java rather than having the X11-dependent
          * implementations hardcoded in those classes.  This way the appropriate
          * actions are taken based on the peer's GraphicsConfig, whether it is
    diff --git a/src/java.desktop/unix/classes/sun/print/AttributeClass.java b/src/java.desktop/unix/classes/sun/print/AttributeClass.java
    index 76a652b2a9349..0fb27909a26df 100644
    --- a/src/java.desktop/unix/classes/sun/print/AttributeClass.java
    +++ b/src/java.desktop/unix/classes/sun/print/AttributeClass.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -22,10 +22,13 @@
      * or visit www.oracle.com if you need additional information or have any
      * questions.
      */
    +
     package sun.print;
     
    -import java.util.Objects;
     import java.io.ByteArrayInputStream;
    +import java.util.Objects;
    +
    +import static java.nio.charset.StandardCharsets.UTF_8;
     
     public class AttributeClass {
         private String myName;
    @@ -187,10 +190,7 @@ public String getStringValue() {
     
                 byte[] strBytes = new byte[valLength];
                 bufStream.read(strBytes, 0, valLength);
    -            try {
    -                strVal = new String(strBytes, "UTF-8");
    -            } catch (java.io.UnsupportedEncodingException uee) {
    -            }
    +            strVal = new String(strBytes, UTF_8);
             }
             return strVal;
         }
    @@ -219,10 +219,7 @@ public String[] getArrayOfStringValues() {
                     int valLength = bufStream.read();
                     byte[] bufBytes = new byte[valLength];
                     bufStream.read(bufBytes, 0, valLength);
    -                try {
    -                    valueArray[i] = new String(bufBytes, "UTF-8");
    -                } catch (java.io.UnsupportedEncodingException uee) {
    -                }
    +                valueArray[i] = new String(bufBytes, UTF_8);
                 }
                 return valueArray;
             }
    diff --git a/src/java.desktop/unix/classes/sun/print/IPPPrintService.java b/src/java.desktop/unix/classes/sun/print/IPPPrintService.java
    index fee8bc3d3cd98..7ef37b116ab85 100644
    --- a/src/java.desktop/unix/classes/sun/print/IPPPrintService.java
    +++ b/src/java.desktop/unix/classes/sun/print/IPPPrintService.java
    @@ -27,42 +27,82 @@
     
     import java.awt.GraphicsEnvironment;
     import java.awt.Toolkit;
    -import javax.print.attribute.*;
    -import javax.print.attribute.standard.*;
    -import javax.print.DocFlavor;
    -import javax.print.DocPrintJob;
    -import javax.print.PrintService;
    -import javax.print.ServiceUIFactory;
    -import java.util.ArrayList;
    -import java.util.HashMap;
    -import java.util.Locale;
    -import java.util.Date;
    -import java.util.Arrays;
    -import java.security.AccessController;
    -import java.security.PrivilegedActionException;
    -import java.security.PrivilegedExceptionAction;
    -import javax.print.event.PrintServiceAttributeListener;
    -
    -import java.net.URI;
    -import java.net.URISyntaxException;
    -import java.net.URL;
    -import java.net.URLConnection;
    -import java.net.HttpURLConnection;
    +import java.io.BufferedReader;
    +import java.io.ByteArrayOutputStream;
    +import java.io.DataInputStream;
     import java.io.File;
     import java.io.InputStream;
    +import java.io.InputStreamReader;
     import java.io.OutputStream;
     import java.io.OutputStreamWriter;
    -import java.io.DataInputStream;
    -import java.io.ByteArrayOutputStream;
    -import java.io.ByteArrayInputStream;
    -import java.io.BufferedReader;
    -import java.io.InputStreamReader;
    -import java.nio.charset.Charset;
    -
    -import java.util.Iterator;
    +import java.net.HttpURLConnection;
    +import java.net.URI;
    +import java.net.URISyntaxException;
    +import java.net.URL;
    +import java.net.URLConnection;
    +import java.util.ArrayList;
    +import java.util.Arrays;
    +import java.util.HashMap;
     import java.util.HashSet;
    +import java.util.Locale;
     import java.util.Map;
     
    +import javax.print.DocFlavor;
    +import javax.print.DocPrintJob;
    +import javax.print.PrintService;
    +import javax.print.ServiceUIFactory;
    +import javax.print.attribute.Attribute;
    +import javax.print.attribute.AttributeSet;
    +import javax.print.attribute.AttributeSetUtilities;
    +import javax.print.attribute.EnumSyntax;
    +import javax.print.attribute.HashAttributeSet;
    +import javax.print.attribute.HashPrintServiceAttributeSet;
    +import javax.print.attribute.PrintRequestAttribute;
    +import javax.print.attribute.PrintServiceAttribute;
    +import javax.print.attribute.PrintServiceAttributeSet;
    +import javax.print.attribute.Size2DSyntax;
    +import javax.print.attribute.standard.Chromaticity;
    +import javax.print.attribute.standard.ColorSupported;
    +import javax.print.attribute.standard.Copies;
    +import javax.print.attribute.standard.CopiesSupported;
    +import javax.print.attribute.standard.Destination;
    +import javax.print.attribute.standard.DialogOwner;
    +import javax.print.attribute.standard.DialogTypeSelection;
    +import javax.print.attribute.standard.Fidelity;
    +import javax.print.attribute.standard.Finishings;
    +import javax.print.attribute.standard.JobName;
    +import javax.print.attribute.standard.JobSheets;
    +import javax.print.attribute.standard.Media;
    +import javax.print.attribute.standard.MediaPrintableArea;
    +import javax.print.attribute.standard.MediaSize;
    +import javax.print.attribute.standard.MediaSizeName;
    +import javax.print.attribute.standard.MediaTray;
    +import javax.print.attribute.standard.NumberUp;
    +import javax.print.attribute.standard.OrientationRequested;
    +import javax.print.attribute.standard.PDLOverrideSupported;
    +import javax.print.attribute.standard.PageRanges;
    +import javax.print.attribute.standard.PagesPerMinute;
    +import javax.print.attribute.standard.PagesPerMinuteColor;
    +import javax.print.attribute.standard.PrinterInfo;
    +import javax.print.attribute.standard.PrinterIsAcceptingJobs;
    +import javax.print.attribute.standard.PrinterLocation;
    +import javax.print.attribute.standard.PrinterMakeAndModel;
    +import javax.print.attribute.standard.PrinterMessageFromOperator;
    +import javax.print.attribute.standard.PrinterMoreInfo;
    +import javax.print.attribute.standard.PrinterMoreInfoManufacturer;
    +import javax.print.attribute.standard.PrinterName;
    +import javax.print.attribute.standard.PrinterResolution;
    +import javax.print.attribute.standard.PrinterState;
    +import javax.print.attribute.standard.PrinterStateReasons;
    +import javax.print.attribute.standard.PrinterURI;
    +import javax.print.attribute.standard.QueuedJobCount;
    +import javax.print.attribute.standard.RequestingUserName;
    +import javax.print.attribute.standard.SheetCollate;
    +import javax.print.attribute.standard.Sides;
    +import javax.print.event.PrintServiceAttributeListener;
    +
    +import static java.nio.charset.StandardCharsets.ISO_8859_1;
    +import static java.nio.charset.StandardCharsets.UTF_8;
     
     public class IPPPrintService implements PrintService, SunPrinterJobService {
     
    @@ -325,11 +365,7 @@ protected static void debug_println(String str) {
             if ((name == null) || (url == null)){
                 throw new IllegalArgumentException("null uri or printer name");
             }
    -        try {
    -            printer = java.net.URLDecoder.decode(name, "UTF-8");
    -        } catch (java.io.UnsupportedEncodingException e) {
    -            printer = name;
    -        }
    +        printer = java.net.URLDecoder.decode(name, UTF_8);
             supportedDocFlavors = null;
             supportedCats = null;
             mediaSizeNames = null;
    @@ -359,11 +395,7 @@ protected static void debug_println(String str) {
             if ((name == null) || (uriStr == null)){
                 throw new IllegalArgumentException("null uri or printer name");
             }
    -        try {
    -            printer = java.net.URLDecoder.decode(name, "UTF-8");
    -        } catch (java.io.UnsupportedEncodingException e) {
    -            printer = name;
    -        }
    +        printer = java.net.URLDecoder.decode(name, UTF_8);
             supportedDocFlavors = null;
             supportedCats = null;
             mediaSizeNames = null;
    @@ -1735,9 +1767,8 @@ public synchronized boolean isPostscript() {
     
                        InputStream is = urlConnection.getInputStream();
                        if (is != null) {
    -                       BufferedReader d =
    -                           new BufferedReader(new InputStreamReader(is,
    -                                                          Charset.forName("ISO-8859-1")));
    +                       BufferedReader d = new BufferedReader(
    +                               new InputStreamReader(is, ISO_8859_1));
                            String lineStr;
                            while ((lineStr = d.readLine()) != null) {
                                if (lineStr.startsWith("*cupsFilter:")) {
    @@ -1829,13 +1860,7 @@ public OutputStream run() {
         public static boolean writeIPPRequest(OutputStream os,
                                                String operCode,
                                                AttributeClass[] attCl) {
    -        OutputStreamWriter osw;
    -        try {
    -            osw = new OutputStreamWriter(os, "UTF-8");
    -        } catch (java.io.UnsupportedEncodingException exc) {
    -            debug_println(debugPrefix+"writeIPPRequest, UTF-8 not supported? Exception: "+exc);
    -            return false;
    -        }
    +        OutputStreamWriter osw = new OutputStreamWriter(os, UTF_8);
             debug_println(debugPrefix+"writeIPPRequest, op code= "+operCode);
             char[] opCode =  new char[2];
             opCode[0] =  (char)Byte.parseByte(operCode.substring(0,2), 16);
    diff --git a/src/java.desktop/unix/legal/pipewire.md b/src/java.desktop/unix/legal/pipewire.md
    new file mode 100644
    index 0000000000000..88389a74e69dd
    --- /dev/null
    +++ b/src/java.desktop/unix/legal/pipewire.md
    @@ -0,0 +1,41 @@
    +## PipeWire 0.3.68
    +
    +### PipeWire license:
    +
    +All PipeWire header files are licensed under the MIT License:
    +
    +
    +
    +Copyright © 2018-2023 Wim Taymans
    +
    +Permission is hereby granted, free of charge, to any person obtaining a
    +copy of this software and associated documentation files (the "Software"),
    +to deal in the Software without restriction, including without limitation
    +the rights to use, copy, modify, merge, publish, distribute, sublicense,
    +and/or sell copies of the Software, and to permit persons to whom the
    +Software is furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice (including the next
    +paragraph) shall be included in all copies or substantial portions of the
    +Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
    +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    +DEALINGS IN THE SOFTWARE.
    +
    + +The below copyright applies to the following files: + +spa/include/spa/monitor/type-info.h +``` +Copyright © 2021 Collabora Ltd. +``` + +spa/include/spa/utils/string.h +``` +Copyright © 2021 Red Hat, Inc. +``` diff --git a/src/java.desktop/unix/native/common/awt/awt.h b/src/java.desktop/unix/native/common/awt/awt.h index dbe236eee47ec..817707789c947 100644 --- a/src/java.desktop/unix/native/common/awt/awt.h +++ b/src/java.desktop/unix/native/common/awt/awt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ typedef char Boolean; #endif /* !HEADLESS && !MACOSX */ -/* The JVM instance: defined in awt_MToolkit.c */ +/* The JVM instance: defined in awt_LoadLibrary.c */ extern JavaVM *jvm; extern jclass tkClass; diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/fp_pipewire.h b/src/java.desktop/unix/native/libawt_xawt/awt/fp_pipewire.h new file mode 100644 index 0000000000000..e5985af5eee14 --- /dev/null +++ b/src/java.desktop/unix/native/libawt_xawt/awt/fp_pipewire.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef HEADLESS +#error This file should not be included in headless library +#endif +#ifndef _FP_PIPEWIRE_H +#define _FP_PIPEWIRE_H + + +struct pw_buffer *(*fp_pw_stream_dequeue_buffer)(struct pw_stream *stream); +const char * (*fp_pw_stream_state_as_string)(enum pw_stream_state state); +int (*fp_pw_stream_queue_buffer)(struct pw_stream *stream, + struct pw_buffer *buffer); +int (*fp_pw_stream_set_active)(struct pw_stream *stream, bool active); + +int (*fp_pw_stream_connect)( + struct pw_stream *stream, + enum pw_direction direction, + uint32_t target_id, + enum pw_stream_flags flags, + const struct spa_pod **params, + uint32_t n_params); + +struct pw_stream *(*fp_pw_stream_new)( + struct pw_core *core, + const char *name, + struct pw_properties *props +); +void (*fp_pw_stream_add_listener)(struct pw_stream *stream, + struct spa_hook *listener, + const struct pw_stream_events *events, + void *data); +int (*fp_pw_stream_disconnect)(struct pw_stream *stream); +void (*fp_pw_stream_destroy)(struct pw_stream *stream); + + +void (*fp_pw_init)(int *argc, char **argv[]); +void (*fp_pw_deinit)(void); + +struct pw_core * +(*fp_pw_context_connect_fd)(struct pw_context *context, + int fd, + struct pw_properties *properties, + size_t user_data_size); + +int (*fp_pw_core_disconnect)(struct pw_core *core); + +struct pw_context * (*fp_pw_context_new)(struct pw_loop *main_loop, + struct pw_properties *props, + size_t user_data_size); + +struct pw_thread_loop * +(*fp_pw_thread_loop_new)(const char *name, const struct spa_dict *props); +struct pw_loop * (*fp_pw_thread_loop_get_loop)(struct pw_thread_loop *loop); +void (*fp_pw_thread_loop_signal)(struct pw_thread_loop *loop, + bool wait_for_accept); +void (*fp_pw_thread_loop_wait)(struct pw_thread_loop *loop); +void (*fp_pw_thread_loop_accept)(struct pw_thread_loop *loop); +int (*fp_pw_thread_loop_start)(struct pw_thread_loop *loop); +void (*fp_pw_thread_loop_stop)(struct pw_thread_loop *loop); +void (*fp_pw_thread_loop_destroy)(struct pw_thread_loop *loop); +void (*fp_pw_thread_loop_lock)(struct pw_thread_loop *loop); +void (*fp_pw_thread_loop_unlock)(struct pw_thread_loop *loop); + +struct pw_properties * (*fp_pw_properties_new)(const char *key, ...); + + +#endif //_FP_PIPEWIRE_H diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h index d786117ce7847..8075d4f419fad 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,13 +55,11 @@ typedef enum } GParamFlags; /* We define all structure pointers to be void* */ -typedef void GMainContext; typedef void GVfs; typedef void GdkColormap; typedef void GdkDrawable; typedef void GdkGC; -typedef void GdkPixbuf; typedef void GdkPixmap; typedef void GtkFixed; @@ -86,13 +84,6 @@ typedef struct { gushort revents; } GPollFD; -typedef struct { - gint x; - gint y; - gint width; - gint height; -} GdkRectangle; - typedef struct { gint x; gint y; diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c index 467ca3f8f1a68..112b1b28f5777 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,10 @@ static void *gtk3_libhandle = NULL; static void *gthread_libhandle = NULL; +static void transform_detail_string (const gchar *detail, + GtkStyleContext *context); +static void gtk3_init(GtkApi* gtk); + static jmp_buf j; /* Widgets */ @@ -244,6 +248,7 @@ static void empty() {} static gboolean gtk3_version_3_10 = TRUE; static gboolean gtk3_version_3_14 = FALSE; static gboolean gtk3_version_3_20 = FALSE; +gboolean glib_version_2_68 = FALSE; GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) { @@ -312,6 +317,10 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) /* Pixbuf */ fp_gdk_pixbuf_new = dl_symbol("gdk_pixbuf_new"); + fp_gdk_pixbuf_new_from_data = dl_symbol("gdk_pixbuf_new_from_data"); + fp_gdk_pixbuf_scale_simple = dl_symbol("gdk_pixbuf_scale_simple"); + fp_gdk_pixbuf_copy_area = dl_symbol("gdk_pixbuf_copy_area"); + fp_gdk_pixbuf_new_from_file = dl_symbol("gdk_pixbuf_new_from_file"); fp_gdk_pixbuf_get_from_drawable = @@ -565,6 +574,51 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) fp_g_list_append = dl_symbol("g_list_append"); fp_g_list_free = dl_symbol("g_list_free"); fp_g_list_free_full = dl_symbol("g_list_free_full"); + + /** + * other + */ + + fp_g_bus_get_sync = dl_symbol("g_bus_get_sync"); + fp_g_dbus_proxy_call_sync = dl_symbol("g_dbus_proxy_call_sync"); + fp_g_dbus_proxy_new_sync = dl_symbol("g_dbus_proxy_new_sync"); + fp_g_dbus_connection_get_unique_name = dl_symbol("g_dbus_connection_get_unique_name"); + fp_g_dbus_connection_call_sync = dl_symbol("g_dbus_connection_call_sync"); + fp_g_dbus_connection_signal_subscribe = dl_symbol("g_dbus_connection_signal_subscribe"); + fp_g_dbus_connection_signal_unsubscribe = dl_symbol("g_dbus_connection_signal_unsubscribe"); + fp_g_dbus_proxy_call_with_unix_fd_list_sync = dl_symbol("g_dbus_proxy_call_with_unix_fd_list_sync"); + + fp_g_variant_builder_init = dl_symbol("g_variant_builder_init"); + fp_g_variant_builder_add = dl_symbol("g_variant_builder_add"); + fp_g_variant_new = dl_symbol("g_variant_new"); + fp_g_variant_new_string = dl_symbol("g_variant_new_string"); + fp_g_variant_new_uint32 = dl_symbol("g_variant_new_uint32"); + fp_g_variant_new_boolean = dl_symbol("g_variant_new_boolean"); + fp_g_variant_get = dl_symbol("g_variant_get"); + fp_g_variant_get_string = dl_symbol("g_variant_get_string"); + fp_g_variant_get_uint32 = dl_symbol("g_variant_get_uint32"); + fp_g_variant_iter_loop = dl_symbol("g_variant_iter_loop"); + fp_g_variant_unref = dl_symbol("g_variant_unref"); + fp_g_variant_lookup = dl_symbol("g_variant_lookup"); + fp_g_variant_lookup_value = dl_symbol("g_variant_lookup_value"); + fp_g_variant_iter_init = dl_symbol("g_variant_iter_init"); + fp_g_variant_iter_n_children = dl_symbol("g_variant_iter_n_children"); + + fp_g_string_new = dl_symbol("g_string_new"); + fp_g_string_erase = dl_symbol("g_string_erase"); + fp_g_string_set_size = dl_symbol("g_string_set_size"); + fp_g_string_free = dl_symbol("g_string_free"); + + glib_version_2_68 = !fp_glib_check_version(2, 68, 0); + if (glib_version_2_68) { + fp_g_string_replace = dl_symbol("g_string_replace"); //since: 2.68 + fp_g_uuid_string_is_valid = //since: 2.52 + dl_symbol("g_uuid_string_is_valid"); + } + fp_g_string_printf = dl_symbol("g_string_printf"); + + fp_g_error_free = dl_symbol("g_error_free"); + fp_g_unix_fd_list_get = dl_symbol("g_unix_fd_list_get"); } /* Now we have only one kind of exceptions: NO_SYMBOL_EXCEPTION * Otherwise we can check the return value of setjmp method. @@ -3008,4 +3062,53 @@ static void gtk3_init(GtkApi* gtk) { gtk->g_list_append = fp_g_list_append; gtk->g_list_free = fp_g_list_free; gtk->g_list_free_full = fp_g_list_free_full; + + gtk->g_bus_get_sync = fp_g_bus_get_sync; + gtk->g_dbus_proxy_call_sync = fp_g_dbus_proxy_call_sync; + gtk->g_dbus_proxy_new_sync = fp_g_dbus_proxy_new_sync; + gtk->g_dbus_connection_get_unique_name = fp_g_dbus_connection_get_unique_name; + gtk->g_dbus_connection_signal_subscribe = fp_g_dbus_connection_signal_subscribe; + gtk->g_dbus_connection_signal_unsubscribe = fp_g_dbus_connection_signal_unsubscribe; + gtk->g_dbus_proxy_call_with_unix_fd_list_sync = fp_g_dbus_proxy_call_with_unix_fd_list_sync; + gtk->g_dbus_connection_call_sync = fp_g_dbus_connection_call_sync; + + gtk->g_variant_new = fp_g_variant_new; + gtk->g_variant_new_string = fp_g_variant_new_string; + gtk->g_variant_new_boolean = fp_g_variant_new_boolean; + gtk->g_variant_new_uint32 = fp_g_variant_new_uint32; + + gtk->g_variant_get = fp_g_variant_get; + gtk->g_variant_get_string = fp_g_variant_get_string; + gtk->g_variant_get_uint32 = fp_g_variant_get_uint32; + + gtk->g_variant_lookup = fp_g_variant_lookup; + + gtk->g_variant_iter_loop = fp_g_variant_iter_loop; + + gtk->g_variant_unref = fp_g_variant_unref; + + gtk->g_variant_builder_init = fp_g_variant_builder_init; + gtk->g_variant_builder_add = fp_g_variant_builder_add; + + gtk->g_variant_lookup_value = fp_g_variant_lookup_value; + gtk->g_variant_iter_init = fp_g_variant_iter_init; + gtk->g_variant_iter_n_children = fp_g_variant_iter_n_children; + + gtk->g_string_new = fp_g_string_new; + gtk->g_string_erase = fp_g_string_erase; + gtk->g_string_set_size = fp_g_string_set_size; + gtk->g_string_free = fp_g_string_free; + gtk->g_string_replace = fp_g_string_replace; + gtk->g_string_printf = fp_g_string_printf; + gtk->g_uuid_string_is_valid = fp_g_uuid_string_is_valid; + + gtk->g_main_context_iteration = fp_g_main_context_iteration; + gtk->g_error_free = fp_g_error_free; + gtk->g_unix_fd_list_get = fp_g_unix_fd_list_get; + + gtk->gdk_pixbuf_new = fp_gdk_pixbuf_new; + gtk->gdk_pixbuf_new_from_data = fp_gdk_pixbuf_new_from_data; + gtk->gdk_pixbuf_scale_simple = fp_gdk_pixbuf_scale_simple; + gtk->gdk_pixbuf_get_pixels = fp_gdk_pixbuf_get_pixels; + gtk->gdk_pixbuf_copy_area = fp_gdk_pixbuf_copy_area; } diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h index 19a1e2ddaf537..054510d488b10 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -180,8 +180,6 @@ typedef enum _cairo_status { } cairo_status_t; /* We define all structure pointers to be void* */ -typedef void GdkPixbuf; -typedef void GMainContext; typedef void GVfs; typedef void GdkColormap; @@ -235,13 +233,6 @@ typedef struct { gushort revents; } GPollFD; -typedef struct { - gint x; - gint y; - gint width; - gint height; -} GdkRectangle; - typedef struct { int x, y; int width, height; @@ -375,7 +366,6 @@ static gboolean (*fp_gtk_show_uri)(GdkScreen *screen, const gchar *uri, guint32 timestamp, GError **error); // Implementation functions prototypes -static void gtk3_init(GtkApi* gtk); static GValue* (*fp_g_value_init)(GValue *value, GType g_type); static gboolean (*fp_g_type_is_a)(GType type, GType is_a_type); static gboolean (*fp_g_value_get_boolean)(const GValue *value); @@ -494,16 +484,15 @@ static void (*fp_gtk_render_background)(GtkStyleContext *context, cairo_t *cr, gdouble x, gdouble y, gdouble width, gdouble height); static gboolean (*fp_gtk_style_context_has_class)(GtkStyleContext *context, const gchar *class_name); -static void transform_detail_string (const gchar *detail, - GtkStyleContext *context); -void (*fp_gtk_style_context_set_junction_sides)(GtkStyleContext *context, + +static void (*fp_gtk_style_context_set_junction_sides)(GtkStyleContext *context, GtkJunctionSides sides); -void (*fp_gtk_style_context_add_region)(GtkStyleContext *context, +static void (*fp_gtk_style_context_add_region)(GtkStyleContext *context, const gchar *region_name, GtkRegionFlags flags); -void (*fp_gtk_render_arrow)(GtkStyleContext *context, cairo_t *cr, +static void (*fp_gtk_render_arrow)(GtkStyleContext *context, cairo_t *cr, gdouble angle, gdouble x, gdouble y, gdouble size); -void (*fp_gtk_bin_set_child)(GtkBin *bin, GtkWidget *widget); -void (*fp_gtk_scrolled_window_set_shadow_type)( +static void (*fp_gtk_bin_set_child)(GtkBin *bin, GtkWidget *widget); +static void (*fp_gtk_scrolled_window_set_shadow_type)( GtkScrolledWindow *scrolled_window, GtkShadowType type); static void (*fp_gtk_render_slider)(GtkStyleContext *context, cairo_t *cr, gdouble x, gdouble y, gdouble width, gdouble height, @@ -522,7 +511,7 @@ static GdkPixbuf* (*fp_gtk_icon_theme_load_icon)(GtkIconTheme *icon_theme, static void (*fp_gtk_adjustment_set_lower)(GtkAdjustment *adjustment, gdouble lower); static void (*fp_gtk_adjustment_set_page_increment)(GtkAdjustment *adjustment, - gdouble page_increment); + gdouble page_increment); static void (*fp_gtk_adjustment_set_page_size)(GtkAdjustment *adjustment, gdouble page_size); static void (*fp_gtk_adjustment_set_step_increment)(GtkAdjustment *adjustment, @@ -537,6 +526,30 @@ static void (*fp_gdk_draw_rectangle)(GdkDrawable*, GdkGC*, gboolean, gint, gint, gint, gint); static GdkPixbuf *(*fp_gdk_pixbuf_new)(GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height); + +static GdkPixbuf *(*fp_gdk_pixbuf_new_from_data)( + const guchar *data, + GdkColorspace colorspace, + gboolean has_alpha, + int bits_per_sample, + int width, + int height, + int rowstride, + GdkPixbufDestroyNotify destroy_fn, + gpointer destroy_fn_data +); + +static void (*fp_gdk_pixbuf_copy_area) ( + const GdkPixbuf* src_pixbuf, + int src_x, + int src_y, + int width, + int height, + GdkPixbuf* dest_pixbuf, + int dest_x, + int dest_y +); + static void (*fp_gdk_drawable_get_size)(GdkDrawable *drawable, gint* width, gint* height); static gboolean (*fp_gtk_init_check)(int* argc, char** argv); @@ -637,4 +650,159 @@ static void (*fp_gtk_style_context_set_path) static void (*fp_gtk_widget_path_unref) (GtkWidgetPath *path); static GtkStyleContext* (*fp_gtk_style_context_new) (void); + +// ---------- fp_g_dbus_* ---------- +static GVariant *(*fp_g_dbus_proxy_call_sync)( + GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusCallFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error +); + +static GDBusProxy *(*fp_g_dbus_proxy_new_sync)( + GDBusConnection *connection, + GDBusProxyFlags flags, + GDBusInterfaceInfo *info, + const gchar *name, + const gchar *object_path, + const gchar *interface_name, + GCancellable *cancellable, + GError **error +); + +static const gchar *(*fp_g_dbus_connection_get_unique_name)( + GDBusConnection *connection +); + +static GDBusConnection *(*fp_g_bus_get_sync)(GBusType bus_type, + GCancellable *cancellable, + GError **error); + +static guint (*fp_g_dbus_connection_signal_subscribe)( + GDBusConnection *connection, + const gchar *sender, + const gchar *interface_name, + const gchar *member, + const gchar *object_path, + const gchar *arg0, + GDBusSignalFlags flags, + GDBusSignalCallback callback, + gpointer user_data, + GDestroyNotify user_data_free_func +); + +static void (*fp_g_dbus_connection_signal_unsubscribe)( + GDBusConnection *connection, + guint subscription_id +); + +static GVariant *(*fp_g_dbus_proxy_call_with_unix_fd_list_sync)( + GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusCallFlags flags, + gint timeout_msec, + GUnixFDList *fd_list, + GUnixFDList **out_fd_list, + GCancellable *cancellable, + GError **error +); + +static GVariant *(*fp_g_dbus_connection_call_sync)( + GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error +); + +// ---------- fp_g_variant_* ---------- + +static GVariant *(*fp_g_variant_new)(const gchar *format_string, ...); + +static GVariant *(*fp_g_variant_new_string)(const gchar *string); + +static GVariant *(*fp_g_variant_new_boolean)(gboolean value); + +static GVariant *(*fp_g_variant_new_uint32)(guint32 value); + +static void (*fp_g_variant_get)(GVariant *value, + const gchar *format_string, + ...); + +static const gchar *(*fp_g_variant_get_string)(GVariant *value, + gsize *length); + +static guint32 (*fp_g_variant_get_uint32)(GVariant *value); + +static gboolean (*fp_g_variant_lookup)(GVariant *dictionary, + const gchar *key, + const gchar *format_string, + ...); + +static gboolean (*fp_g_variant_iter_loop)(GVariantIter *iter, + const gchar *format_string, + ...); + +static void (*fp_g_variant_unref)(GVariant *value); + +static void (*fp_g_variant_builder_init)(GVariantBuilder *builder, + const GVariantType *type); + +static void (*fp_g_variant_builder_add)(GVariantBuilder *builder, + const gchar *format_string, + ...); + +static GVariant *(*fp_g_variant_lookup_value)(GVariant *dictionary, + const gchar *key, + const GVariantType *expected_type); + +static gsize (*fp_g_variant_iter_init)(GVariantIter *iter, + GVariant *value); + +static gsize (*fp_g_variant_iter_n_children)(GVariantIter *iter); + + +// ---------- fp_g_string_* ---------- + +static GString *(*fp_g_string_new)(const gchar *init); + +static GString *(*fp_g_string_erase)(GString *string, + gssize pos, + gssize len); + +static GString *(*fp_g_string_set_size)(GString* string, + gsize len); + +static gchar *(*fp_g_string_free)(GString *string, + gboolean free_segment); + +static guint (*fp_g_string_replace)(GString *string, + const gchar *find, + const gchar *replace, + guint limit); + +static void *(*fp_g_string_printf)(GString *string, + const gchar *format, + ...); + +static gboolean (*fp_g_uuid_string_is_valid)(const gchar *str); + + +// ---------- * ---------- +static void (*fp_g_error_free)(GError *error); + +static gint (*fp_g_unix_fd_list_get)(GUnixFDList *list, + gint index_, + GError **error); + #endif /* !_GTK3_INTERFACE_H */ diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h index 98097e183012a..aac1857181500 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,7 +108,7 @@ typedef unsigned short gushort; typedef unsigned short guint16; typedef unsigned int guint; typedef unsigned int guint32; -typedef unsigned int gsize; +typedef unsigned long gsize; typedef unsigned long gulong; typedef signed long long gint64; typedef unsigned long long guint64; @@ -128,6 +128,93 @@ struct _GSList { GSList *next; }; +typedef signed long gssize; +typedef struct _GString GString; + +struct _GString +{ + gchar *str; + gsize len; + gsize allocated_len; +}; + +typedef struct _GVariant GVariant; +typedef struct _GVariantIter GVariantIter; +struct _GVariantIter { + /*< private >*/ + gsize x[16]; +}; + +typedef struct _GVariantType GVariantType; +typedef struct _GVariantBuilder GVariantBuilder; + +struct _GVariantBuilder { + /*< private >*/ + union + { + struct { + gsize partial_magic; + const GVariantType *type; + gsize y[14]; + } s; + gsize x[16]; + } u; +}; + + +#define G_VARIANT_TYPE_VARDICT ((const GVariantType *) "a{sv}") +#define G_VARIANT_TYPE_ARRAY ((const GVariantType *) "a*") +#define G_VARIANT_TYPE_STRING ((const GVariantType *) "s") + +typedef struct _GDBusProxy GDBusProxy; +typedef enum { + G_DBUS_CALL_FLAGS_NONE = 0, + G_DBUS_CALL_FLAGS_NO_AUTO_START = (1<<0), + G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION = (1<<1) +} GDBusCallFlags; + +typedef void GMainContext; +typedef void GUnixFDList; + +typedef void GDBusConnection; +typedef enum /*< flags >*/ +{ + G_DBUS_SIGNAL_FLAGS_NONE = 0, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE = (1<<0), + G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE = (1<<1), + G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH = (1<<2) +} GDBusSignalFlags; + +typedef void (*GDBusSignalCallback) (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data); + +typedef struct _GCancellable GCancellable; + +typedef enum +{ + G_BUS_TYPE_STARTER = -1, + G_BUS_TYPE_NONE = 0, + G_BUS_TYPE_SYSTEM = 1, + G_BUS_TYPE_SESSION = 2 +} GBusType; + +typedef enum +{ + G_DBUS_PROXY_FLAGS_NONE = 0, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0), + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1), + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2), + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES = (1<<3), + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION = (1<<4) +} GDBusProxyFlags; + +typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo; + typedef enum { BUTTON, /* GtkButton */ CHECK_BOX, /* GtkCheckButton */ @@ -409,14 +496,29 @@ typedef enum { } GConnectFlags; //------------------------------ +typedef guint32 GQuark; +struct _GError +{ + GQuark domain; + gint code; + gchar *message; +}; +typedef struct _GError GError; -typedef void GError; typedef void GdkScreen; typedef void GtkWindow; typedef void GdkWindow; typedef void GClosure; typedef void GtkFileChooser; typedef void GtkFileFilter; + +typedef struct { + gint x; + gint y; + gint width; + gint height; +} GdkRectangle; + typedef struct { GtkFileFilterFlags contains; const gchar *filename; @@ -430,6 +532,8 @@ typedef void (*GClosureNotify)(gpointer data, GClosure *closure); typedef void (*GDestroyNotify)(gpointer data); typedef void (*GCallback)(void); +typedef void GdkPixbuf; +typedef void (* GdkPixbufDestroyNotify) (guchar *pixels, gpointer data); typedef struct GtkApi { int version; @@ -513,7 +617,6 @@ typedef struct GtkApi { jint jwidth, int dx, int dy, jint scale); void (*g_free)(gpointer mem); - gchar* (*gtk_file_chooser_get_filename)(GtkFileChooser *chooser); void (*gtk_widget_hide)(void* widget); void (*gtk_main_quit)(void); @@ -558,6 +661,185 @@ typedef struct GtkApi { GList* (*g_list_append) (GList *list, gpointer data); void (*g_list_free) (GList *list); void (*g_list_free_full) (GList *list, GDestroyNotify free_func); + + + /* */ + GVariant *(*g_dbus_proxy_call_sync)( + GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusCallFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error); + + GVariant *(*g_variant_new)(const gchar *format_string, ...); + GVariant *(*g_variant_new_string)(const gchar *string); + GVariant *(*g_variant_new_boolean)(gboolean value); + GVariant *(*g_variant_new_uint32)(guint32 value); + + + void (*g_variant_get)(GVariant *value, + const gchar *format_string, + ...); + const gchar *(*g_variant_get_string)(GVariant *value, gsize *length); + guint32 (*g_variant_get_uint32)(GVariant *value); + + gboolean (*g_variant_lookup)(GVariant *dictionary, + const gchar *key, + const gchar *format_string, + ...); + gboolean (*g_variant_iter_loop)(GVariantIter *iter, + const gchar *format_string, + ...); + + void (*g_variant_unref)(GVariant *value); + + void (*g_variant_builder_init)(GVariantBuilder *builder, //+ + const GVariantType *type); + + void (*g_variant_builder_add)(GVariantBuilder *builder, //+ + const gchar *format_string, + ...); + + GVariant *(*g_variant_lookup_value)(GVariant *dictionary, + const gchar *key, + const GVariantType *expected_type); + + gsize (*g_variant_iter_init)(GVariantIter *iter, + GVariant *value); + + gsize (*g_variant_iter_n_children)(GVariantIter *iter); + + + GString *(*g_string_new)(const gchar *init); + + GString *(*g_string_erase)(GString *string, + gssize pos, + gssize len); + + GString *(*g_string_set_size)(GString* string, + gsize len); + + + gchar *(*g_string_free)(GString *string, + gboolean free_segment); + + guint (*g_string_replace)(GString *string, + const gchar *find, + const gchar *replace, + guint limit); + + void *(*g_string_printf)(GString *string, + const gchar *format, + ...); + + gboolean (*g_uuid_string_is_valid)(const gchar *str); + + + GDBusConnection *(*g_bus_get_sync)(GBusType bus_type, + GCancellable *cancellable, + GError **error); + + GDBusProxy *(*g_dbus_proxy_new_sync)(GDBusConnection *connection, + GDBusProxyFlags flags, + GDBusInterfaceInfo *info, + const gchar *name, + const gchar *object_path, + const gchar *interface_name, + GCancellable *cancellable, + GError **error); + + const gchar *(*g_dbus_connection_get_unique_name)(GDBusConnection *connection); + + + + guint (*g_dbus_connection_signal_subscribe)(GDBusConnection *connection, + const gchar *sender, + const gchar *interface_name, + const gchar *member, + const gchar *object_path, + const gchar *arg0, + GDBusSignalFlags flags, + GDBusSignalCallback callback, + gpointer user_data, + GDestroyNotify user_data_free_func); + + void (*g_dbus_connection_signal_unsubscribe)(GDBusConnection *connection, + guint subscription_id); + + GVariant *(*g_dbus_proxy_call_with_unix_fd_list_sync)(GDBusProxy *proxy, + const gchar *method_name, + GVariant *parameters, + GDBusCallFlags flags, + gint timeout_msec, + GUnixFDList *fd_list, + GUnixFDList **out_fd_list, + GCancellable *cancellable, + GError **error); + + GVariant *(*g_dbus_connection_call_sync)(GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + gint timeout_msec, + GCancellable *cancellable, + GError **error); + + gboolean (*g_main_context_iteration)(GMainContext *context, + gboolean may_block); + + void (*g_error_free)(GError *error); + + gint (*g_unix_fd_list_get)(GUnixFDList *list, + gint index_, + GError **error); + + GdkPixbuf *(*gdk_pixbuf_new)(GdkColorspace colorspace, + gboolean has_alpha, + int bits_per_sample, + int width, + int height); + + + GdkPixbuf *(*gdk_pixbuf_new_from_data)( + const guchar *data, + GdkColorspace colorspace, + gboolean has_alpha, + int bits_per_sample, + int width, + int height, + int rowstride, + GdkPixbufDestroyNotify destroy_fn, + gpointer destroy_fn_data + ); + + + GdkPixbuf *(*gdk_pixbuf_scale_simple)(GdkPixbuf *src, + int dest_width, + int dest_heigh, + GdkInterpType interp_type + ); + + guchar* (*gdk_pixbuf_get_pixels) (const GdkPixbuf* pixbuf); + + + void (*gdk_pixbuf_copy_area) ( + const GdkPixbuf* src_pixbuf, + int src_x, + int src_y, + int width, + int height, + GdkPixbuf* dest_pixbuf, + int dest_x, + int dest_y + ); + + /* */ } GtkApi; gboolean gtk_load(JNIEnv *env, GtkVersion version, gboolean verbose); diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c new file mode 100644 index 0000000000000..e2f4ea31d2edb --- /dev/null +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c @@ -0,0 +1,1038 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef HEADLESS +#error This file should not be included in headless library +#endif + +#include +#include "jni_util.h" +#include "awt.h" +#include "screencast_pipewire.h" +#include "fp_pipewire.h" +#include + +#include "gtk_interface.h" +#include "gtk3_interface.h" + +int DEBUG_SCREENCAST_ENABLED = FALSE; + +#define EXCEPTION_CHECK_DESCRIBE() if ((*env)->ExceptionCheck(env)) { \ + (*env)->ExceptionDescribe(env); \ + } + +static gboolean hasPipewireFailed = FALSE; +static gboolean sessionClosed = TRUE; +static GString *activeSessionToken; + +struct ScreenSpace screenSpace = {0}; +static struct PwLoopData pw = {0}; + +jclass tokenStorageClass = NULL; +jmethodID storeTokenMethodID = NULL; + +#if defined(AIX) && defined(__open_xl_version__) && __open_xl_version__ >= 17 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + +inline void debug_screencast( + const char *__restrict fmt, + ... +) { + if (DEBUG_SCREENCAST_ENABLED) { + va_list myargs; + va_start(myargs, fmt); + vfprintf(stdout, fmt, myargs); + va_end(myargs); + } +} + +/** + * @return TRUE on success + */ +static gboolean initScreenSpace() { + screenSpace.screenCount = 0; + screenSpace.allocated = SCREEN_SPACE_DEFAULT_ALLOCATED; + screenSpace.screens = calloc( + SCREEN_SPACE_DEFAULT_ALLOCATED, + sizeof(struct ScreenProps) + ); + + if (!screenSpace.screens) { + ERR("failed to allocate memory\n"); + return FALSE; + } + return TRUE; +} + +static void doCleanup() { + if (pw.loop) { + DEBUG_SCREENCAST("STOPPING loop\n", NULL); + fp_pw_thread_loop_stop(pw.loop); + } + + for (int i = 0; i < screenSpace.screenCount; ++i) { + struct ScreenProps *screenProps = &screenSpace.screens[i]; + if (screenProps->data) { + if (screenProps->data->stream) { + fp_pw_thread_loop_lock(pw.loop); + fp_pw_stream_disconnect(screenProps->data->stream); + fp_pw_stream_destroy(screenProps->data->stream); + fp_pw_thread_loop_unlock(pw.loop); + screenProps->data->stream = NULL; + } + free(screenProps->data); + screenProps->data = NULL; + } + } + + if (pw.pwFd > 0) { + close(pw.pwFd); + pw.pwFd = -1; + } + + portalScreenCastCleanup(); + + if (pw.core) { + fp_pw_core_disconnect(pw.core); + pw.core = NULL; + } + + if (pw.loop) { + fp_pw_thread_loop_destroy(pw.loop); + pw.loop = NULL; + } + + if (screenSpace.screens) { + free(screenSpace.screens); + screenSpace.screens = NULL; + screenSpace.screenCount = 0; + } + + if (!sessionClosed) { + fp_pw_deinit(); + } + + gtk->g_string_set_size(activeSessionToken, 0); + sessionClosed = TRUE; +} + +/** + * @return TRUE on success + */ +static gboolean initScreencast(const gchar *token, + GdkRectangle *affectedBounds, + gint affectedBoundsLength) { + gboolean isSameToken = !token + ? FALSE + : strcmp(token, activeSessionToken->str) == 0; + + if (!sessionClosed) { + if (isSameToken) { + DEBUG_SCREENCAST("Reusing active session.\n", NULL); + return TRUE; + } else { + DEBUG_SCREENCAST( + "Active session has a different token |%s| -> |%s|," + " closing current session.\n", + activeSessionToken->str, token + ); + doCleanup(); + } + } + + fp_pw_init(NULL, NULL); + + pw.pwFd = RESULT_ERROR; + + if (!initScreenSpace() + || !initXdgDesktopPortal() + || (pw.pwFd = getPipewireFd(token, + affectedBounds, + affectedBoundsLength)) < 0) { + doCleanup(); + return FALSE; + } + + gtk->g_string_printf(activeSessionToken, "%s", token); + hasPipewireFailed = FALSE; + sessionClosed = FALSE; + return TRUE; +} + +static void onStreamParamChanged( + void *userdata, + uint32_t id, + const struct spa_pod *param +) { + struct PwStreamData *data = userdata; + uint32_t mediaType; + uint32_t mediaSubtype; + + DEBUG_SCREEN_PREFIX(data->screenProps, "param event id %i\n", id); + + if (param == NULL || id != SPA_PARAM_Format) { + return; + } + + if (spa_format_parse(param, + &mediaType, + &mediaSubtype) < 0) { + return; + } + + if (mediaType != SPA_MEDIA_TYPE_video || + mediaSubtype != SPA_MEDIA_SUBTYPE_raw) { + return; + } + + if (spa_format_video_raw_parse(param, &data->rawFormat) < 0) { + return; + } + + DEBUG_SCREEN_PREFIX(data->screenProps, "stream format: %s (%d)\t%dx%d\n", + spa_debug_type_find_name( + spa_type_video_format, + data->rawFormat.format + ), + data->rawFormat.format, + data->rawFormat.size.width, + data->rawFormat.size.height); + + data->hasFormat = TRUE; + fp_pw_thread_loop_signal(pw.loop, TRUE); +} + +static void onStreamProcess(void *userdata) { + struct PwStreamData *data = userdata; + + struct ScreenProps *screen = data->screenProps; + + DEBUG_SCREEN_PREFIX(screen, + "hasFormat %i " + "captureDataReady %i shouldCapture %i\n", + data->hasFormat, + screen->captureDataReady, + screen->shouldCapture + ); + if ( + !data->hasFormat + || !screen->shouldCapture + || screen->captureDataReady + ) { + return; + } + + struct pw_buffer *pwBuffer; + struct spa_buffer *spaBuffer; + + if (!data->stream + || (pwBuffer = fp_pw_stream_dequeue_buffer(data->stream)) == NULL) { + DEBUG_SCREEN_PREFIX(screen, "!!! out of buffers\n", NULL); + return; + } + + spaBuffer = pwBuffer->buffer; + if (!spaBuffer + || spaBuffer->n_datas < 1 + || spaBuffer->datas[0].data == NULL) { + DEBUG_SCREEN_PREFIX(screen, "!!! no data, n_datas %d\n", + spaBuffer->n_datas); + return; + } + + struct spa_data spaData = spaBuffer->datas[0]; + + gint streamWidth = data->rawFormat.size.width; + gint streamHeight = data->rawFormat.size.height; + + DEBUG_SCREEN(screen); + DEBUG_SCREEN_PREFIX(screen, + "got a frame of size %d offset %d stride %d " + "flags %d FD %li captureDataReady %i of stream %dx%d\n", + spaBuffer->datas[0].chunk->size, + spaData.chunk->offset, + spaData.chunk->stride, + spaData.chunk->flags, + spaData.fd, + screen->captureDataReady, + streamWidth, + streamHeight + ); + + GdkRectangle captureArea = screen->captureArea; + GdkRectangle screenBounds = screen->bounds; + + GdkPixbuf *pixbuf = gtk->gdk_pixbuf_new_from_data(spaData.data, + GDK_COLORSPACE_RGB, + TRUE, + 8, + streamWidth, + streamHeight, + spaData.chunk->stride, + NULL, + NULL); + + if (screen->bounds.width != streamWidth + || screen->bounds.height != streamHeight) { + + DEBUG_SCREEN_PREFIX(screen, "scaling stream data %dx%d -> %dx%d\n", + streamWidth, streamHeight, + screen->bounds.width, screen->bounds.height + ); + + GdkPixbuf *scaled = gtk->gdk_pixbuf_scale_simple(pixbuf, + screen->bounds.width, + screen->bounds.height, + GDK_INTERP_BILINEAR); + + gtk->g_object_unref(pixbuf); + pixbuf = scaled; + } + + GdkPixbuf *cropped = NULL; + if (captureArea.width != screenBounds.width + || captureArea.height != screenBounds.height) { + + cropped = gtk->gdk_pixbuf_new(GDK_COLORSPACE_RGB, + TRUE, + 8, + captureArea.width, + captureArea.height); + if (cropped) { + gtk->gdk_pixbuf_copy_area(pixbuf, + captureArea.x, + captureArea.y, + captureArea.width, + captureArea.height, + cropped, + 0, 0); + } else { + ERR("Cannot create a new pixbuf.\n"); + } + + gtk->g_object_unref(pixbuf); + pixbuf = NULL; + + data->screenProps->captureDataPixbuf = cropped; + } else { + data->screenProps->captureDataPixbuf = pixbuf; + } + + screen->captureDataReady = TRUE; + + DEBUG_SCREEN_PREFIX(screen, "data ready\n", NULL); + fp_pw_stream_queue_buffer(data->stream, pwBuffer); + + fp_pw_thread_loop_signal(pw.loop, FALSE); +} + +static void onStreamStateChanged( + void *userdata, + enum pw_stream_state old, + enum pw_stream_state state, + const char *error +) { + struct PwStreamData *data = userdata; + DEBUG_SCREEN_PREFIX(data->screenProps, "state %i (%s) -> %i (%s) err %s\n", + old, fp_pw_stream_state_as_string(old), + state, fp_pw_stream_state_as_string(state), + error); + if (state == PW_STREAM_STATE_ERROR + || state == PW_STREAM_STATE_UNCONNECTED) { + + hasPipewireFailed = TRUE; + fp_pw_thread_loop_signal(pw.loop, FALSE); + } +} + +static const struct pw_stream_events streamEvents = { + PW_VERSION_STREAM_EVENTS, + .param_changed = onStreamParamChanged, + .process = onStreamProcess, + .state_changed = onStreamStateChanged, +}; + + +static bool startStream( + struct pw_stream *stream, + uint32_t node +) { + char buffer[1024]; + struct spa_pod_builder builder = + SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + const struct spa_pod *param; + + + param = spa_pod_builder_add_object( + &builder, + SPA_TYPE_OBJECT_Format, + SPA_PARAM_EnumFormat, + SPA_FORMAT_mediaType, + SPA_POD_Id(SPA_MEDIA_TYPE_video), + SPA_FORMAT_mediaSubtype, + SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_VIDEO_format, + SPA_POD_Id(SPA_VIDEO_FORMAT_BGRx), + SPA_FORMAT_VIDEO_size, + SPA_POD_CHOICE_RANGE_Rectangle( + &SPA_RECTANGLE(320, 240), + &SPA_RECTANGLE(1, 1), + &SPA_RECTANGLE(8192, 8192) + ), + SPA_FORMAT_VIDEO_framerate, + SPA_POD_CHOICE_RANGE_Fraction( + &SPA_FRACTION(25, 1), + &SPA_FRACTION(0, 1), + &SPA_FRACTION(1000, 1) + ) + ); + + DEBUG_SCREENCAST("screenId#%i: stream connecting %p\n", node, stream); + + return fp_pw_stream_connect( + stream, + PW_DIRECTION_INPUT, + node, + PW_STREAM_FLAG_AUTOCONNECT + | PW_STREAM_FLAG_MAP_BUFFERS, + ¶m, + 1 + ) >= 0; +} + +/** + * @param index of a screen + * @return TRUE on success + */ +static gboolean connectStream(int index) { + DEBUG_SCREENCAST("@@@ using screen %i\n", index); + if (index >= screenSpace.screenCount) { + DEBUG_SCREENCAST("!!! Wrong index for screen\n", NULL); + return FALSE; + } + + struct PwStreamData *data = screenSpace.screens[index].data; + + data->screenProps = &screenSpace.screens[index]; + + if (!sessionClosed && data->stream) { + fp_pw_thread_loop_lock(pw.loop); + int result = fp_pw_stream_set_active(data->stream, TRUE); + fp_pw_thread_loop_unlock(pw.loop); + + DEBUG_SCREEN_PREFIX(data->screenProps, + "stream %p: activate result |%i|\n", + data->stream, result); + + return result == 0; // 0 - success + }; + + data->hasFormat = FALSE; + + data->stream = fp_pw_stream_new( + pw.core, + "AWT Screen Stream", + fp_pw_properties_new( + PW_KEY_MEDIA_TYPE, "Video", + PW_KEY_MEDIA_CATEGORY, "Capture", + PW_KEY_MEDIA_ROLE, "Screen", + NULL + ) + ); + + if (!data->stream) { + DEBUG_SCREEN_PREFIX(data->screenProps, + "!!! Could not create a pipewire stream\n", NULL); + fp_pw_thread_loop_unlock(pw.loop); + return FALSE; + } + + fp_pw_stream_add_listener( + data->stream, + &data->streamListener, + &streamEvents, + data + ); + + DEBUG_SCREEN(data->screenProps); + + if (!startStream(data->stream, screenSpace.screens[index].id)){ + DEBUG_SCREEN_PREFIX(data->screenProps, + "!!! Could not start a pipewire stream\n", NULL); + fp_pw_thread_loop_unlock(pw.loop); + return FALSE; + } + + while (!data->hasFormat) { + fp_pw_thread_loop_wait(pw.loop); + fp_pw_thread_loop_accept(pw.loop); + if (hasPipewireFailed) { + fp_pw_thread_loop_unlock(pw.loop); + return FALSE; + } + } + + DEBUG_SCREEN_PREFIX(data->screenProps, + "frame size: %dx%d\n", + data->rawFormat.size.width, data->rawFormat.size.height + ); + + return TRUE; +} + +/** + * @return TRUE if requested screenshot area intersects with a screen + */ +static gboolean checkScreen(int index, GdkRectangle requestedArea) { + if (index >= screenSpace.screenCount) { + DEBUG_SCREENCAST("!!! Wrong index for screen %i >= %i\n", + index, screenSpace.screenCount); + return FALSE; + } + + struct ScreenProps * screen = &screenSpace.screens[index]; + + int x1 = MAX(requestedArea.x, screen->bounds.x); + int y1 = MAX(requestedArea.y, screen->bounds.y); + + int x2 = MIN( + requestedArea.x + requestedArea.width, + screen->bounds.x + screen->bounds.width + ); + int y2 = MIN( + requestedArea.y + requestedArea.height, + screen->bounds.y + screen->bounds.height + ); + + screen->shouldCapture = x2 > x1 && y2 > y1; + + if (screen->shouldCapture) { //intersects + //in screen coords: + GdkRectangle * captureArea = &(screen->captureArea); + + captureArea->x = x1 - screen->bounds.x; + captureArea->y = y1 - screen->bounds.y; + captureArea->width = x2 - x1; + captureArea->height = y2 - y1; + + screen->captureArea.x = x1 - screen->bounds.x; + } + + DEBUG_SCREEN(screen); + return screen->shouldCapture; +} + + +static void onCoreError( + void *data, + uint32_t id, + int seq, + int res, + const char *message +) { + DEBUG_SCREENCAST( + "!!! pipewire error: id %u, seq: %d, res: %d (%s): %s\n", + id, seq, res, strerror(res), message + ); + if (id == PW_ID_CORE) { + fp_pw_thread_loop_lock(pw.loop); + hasPipewireFailed = TRUE; + fp_pw_thread_loop_signal(pw.loop, FALSE); + fp_pw_thread_loop_unlock(pw.loop); + } +} + +static const struct pw_core_events coreEvents = { + PW_VERSION_CORE_EVENTS, + .error = onCoreError, +}; + +/** + * + * @param requestedArea requested screenshot area + * @return TRUE on success + */ +static gboolean doLoop(GdkRectangle requestedArea) { + gboolean isLoopLockTaken = FALSE; + if (!pw.loop && !sessionClosed) { + pw.loop = fp_pw_thread_loop_new("AWT Pipewire Thread", NULL); + + if (!pw.loop) { + DEBUG_SCREENCAST("!!! Could not create a loop\n", NULL); + doCleanup(); + return FALSE; + } + + pw.context = fp_pw_context_new( + fp_pw_thread_loop_get_loop(pw.loop), + NULL, + 0 + ); + + if (!pw.context) { + DEBUG_SCREENCAST("!!! Could not create a pipewire context\n", NULL); + doCleanup(); + return FALSE; + } + + if (fp_pw_thread_loop_start(pw.loop) != 0) { + DEBUG_SCREENCAST("!!! Could not start pipewire thread loop\n", NULL); + doCleanup(); + return FALSE; + } + + fp_pw_thread_loop_lock(pw.loop); + isLoopLockTaken = TRUE; + + pw.core = fp_pw_context_connect_fd( + pw.context, + pw.pwFd, + NULL, + 0 + ); + + if (!pw.core) { + DEBUG_SCREENCAST("!!! Could not create pipewire core\n", NULL); + goto fail; + } + + pw_core_add_listener(pw.core, &pw.coreListener, &coreEvents, NULL); + } + + for (int i = 0; i < screenSpace.screenCount; ++i) { + struct ScreenProps *screen = &screenSpace.screens[i]; + if (!screen->data && !sessionClosed) { + struct PwStreamData *data = + (struct PwStreamData*) malloc(sizeof (struct PwStreamData)); + if (!data) { + ERR("failed to allocate memory\n"); + goto fail; + } + + memset(data, 0, sizeof (struct PwStreamData)); + + screen->data = data; + } + + DEBUG_SCREEN_PREFIX(screen, "@@@ adding screen %i\n", i); + if (checkScreen(i, requestedArea)) { + if (!connectStream(i)){ + goto fail; + } + } + DEBUG_SCREEN_PREFIX(screen, "@@@ screen processed %i\n", i); + } + + if (isLoopLockTaken) { + fp_pw_thread_loop_unlock(pw.loop); + } + + return TRUE; + + fail: + if (isLoopLockTaken) { + fp_pw_thread_loop_unlock(pw.loop); + } + doCleanup(); + return FALSE; +} + +static gboolean isAllDataReady() { + for (int i = 0; i < screenSpace.screenCount; ++i) { + if (!screenSpace.screens[i].shouldCapture) { + continue; + } + if (!screenSpace.screens[i].captureDataReady ) { + return FALSE; + } + } + return TRUE; +} + + +static void *pipewire_libhandle = NULL; +//glib_version_2_68 false for gtk2, as it comes from gtk3_interface.c + +extern gboolean glib_version_2_68; + +#define LOAD_SYMBOL(fp_name, name) do { \ + (fp_name) = dlsym(pipewire_libhandle, name); \ + if (!(fp_name)) { \ + debug_screencast("!!! %s:%i error loading dl_symbol %s\n", \ + __func__, __LINE__, name); \ + goto fail; \ + } \ +} while(0); + +static gboolean loadSymbols() { + if (!glib_version_2_68) { + DEBUG_SCREENCAST("glib version 2.68+ required\n", NULL); + return FALSE; + } + + pipewire_libhandle = dlopen(VERSIONED_JNI_LIB_NAME("pipewire-0.3", "0"), + RTLD_LAZY | RTLD_LOCAL); + + if (!pipewire_libhandle) { + DEBUG_SCREENCAST("could not load pipewire library\n", NULL); + return FALSE; + } + + LOAD_SYMBOL(fp_pw_stream_dequeue_buffer, "pw_stream_dequeue_buffer"); + LOAD_SYMBOL(fp_pw_stream_state_as_string, "pw_stream_state_as_string"); + LOAD_SYMBOL(fp_pw_stream_queue_buffer, "pw_stream_queue_buffer"); + LOAD_SYMBOL(fp_pw_stream_set_active, "pw_stream_set_active"); + LOAD_SYMBOL(fp_pw_stream_connect, "pw_stream_connect"); + LOAD_SYMBOL(fp_pw_stream_new, "pw_stream_new"); + LOAD_SYMBOL(fp_pw_stream_add_listener, "pw_stream_add_listener"); + LOAD_SYMBOL(fp_pw_stream_disconnect, "pw_stream_disconnect"); + LOAD_SYMBOL(fp_pw_stream_destroy, "pw_stream_destroy"); + LOAD_SYMBOL(fp_pw_init, "pw_init"); + LOAD_SYMBOL(fp_pw_deinit, "pw_deinit"); + LOAD_SYMBOL(fp_pw_context_connect_fd, "pw_context_connect_fd"); + LOAD_SYMBOL(fp_pw_core_disconnect, "pw_core_disconnect"); + LOAD_SYMBOL(fp_pw_context_new, "pw_context_new"); + LOAD_SYMBOL(fp_pw_thread_loop_new, "pw_thread_loop_new"); + LOAD_SYMBOL(fp_pw_thread_loop_get_loop, "pw_thread_loop_get_loop"); + LOAD_SYMBOL(fp_pw_thread_loop_signal, "pw_thread_loop_signal"); + LOAD_SYMBOL(fp_pw_thread_loop_wait, "pw_thread_loop_wait"); + LOAD_SYMBOL(fp_pw_thread_loop_accept, "pw_thread_loop_accept"); + LOAD_SYMBOL(fp_pw_thread_loop_start, "pw_thread_loop_start"); + LOAD_SYMBOL(fp_pw_thread_loop_stop, "pw_thread_loop_stop"); + LOAD_SYMBOL(fp_pw_thread_loop_destroy, "pw_thread_loop_destroy"); + LOAD_SYMBOL(fp_pw_thread_loop_lock, "pw_thread_loop_lock"); + LOAD_SYMBOL(fp_pw_thread_loop_unlock, "pw_thread_loop_unlock"); + LOAD_SYMBOL(fp_pw_properties_new, "pw_properties_new"); + + return TRUE; + + fail: + dlclose(pipewire_libhandle); + pipewire_libhandle = NULL; + return FALSE; +} + +void storeRestoreToken(const gchar* oldToken, const gchar* newToken) { + + JNIEnv* env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2); + DEBUG_SCREENCAST("saving token, old: |%s| > new: |%s|\n", oldToken, newToken); + if (env) { + jstring jOldToken = NULL; + if (oldToken) { + jOldToken = (*env)->NewStringUTF(env, oldToken); + EXCEPTION_CHECK_DESCRIBE(); + if (!jOldToken) { + return; + } + } + jstring jNewToken = (*env)->NewStringUTF(env, newToken); + EXCEPTION_CHECK_DESCRIBE(); + if (!jNewToken) { + (*env)->DeleteLocalRef(env, jOldToken); + return; + } + + jintArray allowedBounds = NULL; + if (screenSpace.screenCount > 0) { + allowedBounds = (*env)->NewIntArray(env, screenSpace.screenCount*4); + EXCEPTION_CHECK_DESCRIBE(); + if (!allowedBounds) { + return; + } + jint* elements = (*env)->GetIntArrayElements(env, allowedBounds, NULL); + EXCEPTION_CHECK_DESCRIBE(); + if (!elements) { + return; + } + + for (int i = 0; i < screenSpace.screenCount; ++i) { + GdkRectangle bounds = screenSpace.screens[i].bounds; + elements[4 * i] = bounds.x; + elements[4 * i + 1] = bounds.y; + elements[4 * i + 2] = bounds.width; + elements[4 * i + 3] = bounds.height; + } + + (*env)->ReleaseIntArrayElements(env, allowedBounds, elements, 0); + + (*env)->CallStaticVoidMethod(env, tokenStorageClass, + storeTokenMethodID, + jOldToken, jNewToken, + allowedBounds); + EXCEPTION_CHECK_DESCRIBE(); + } + (*env)->DeleteLocalRef(env, jOldToken); + (*env)->DeleteLocalRef(env, jNewToken); + } else { + DEBUG_SCREENCAST("!!! Could not get env\n", NULL); + } +} + +/* + * Class: sun_awt_UNIXToolkit + * Method: load_gtk + * Signature: (IZ)Z + */ +JNIEXPORT jboolean JNICALL Java_sun_awt_screencast_ScreencastHelper_loadPipewire( + JNIEnv *env, jclass cls, jboolean screencastDebug +) { + DEBUG_SCREENCAST_ENABLED = screencastDebug; + + if (!loadSymbols()) { + return JNI_FALSE; + } + + tokenStorageClass = (*env)->FindClass(env, "sun/awt/screencast/TokenStorage"); + if (!tokenStorageClass) { + return JNI_FALSE; + } + + tokenStorageClass = (*env)->NewGlobalRef(env, tokenStorageClass); + + if (tokenStorageClass) { + storeTokenMethodID = (*env)->GetStaticMethodID( + env, + tokenStorageClass, + "storeTokenFromNative", + "(Ljava/lang/String;Ljava/lang/String;[I)V" + ); + if (!storeTokenMethodID) { + return JNI_FALSE; + } + } else { + DEBUG_SCREENCAST("!!! @@@ tokenStorageClass %p\n", + tokenStorageClass); + return JNI_FALSE; + } + + activeSessionToken = gtk->g_string_new(""); + + gboolean usable = initXdgDesktopPortal(); + portalScreenCastCleanup(); + return usable; +} + +static void releaseToken(JNIEnv *env, jstring jtoken, const gchar *token) { + if (token) { + (*env)->ReleaseStringUTFChars(env, jtoken, token); + } +} + +static void arrayToRectangles(JNIEnv *env, + jintArray boundsArray, + jint boundsLen, + GdkRectangle *out +) { + if (!boundsArray) { + return; + } + + jint * body = (*env)->GetIntArrayElements(env, boundsArray, 0); + EXCEPTION_CHECK_DESCRIBE(); + if (!body) { + return; + } + + for (int i = 0; i < boundsLen; i += 4) { + GdkRectangle screenBounds = { + body[i], body[i + 1], + body[i + 2], body[i + 3] + }; + out[i / 4] = screenBounds; + } + + (*env)->ReleaseIntArrayElements(env, boundsArray, body, 0); +} + +static int makeScreencast( + const gchar *token, + GdkRectangle *requestedArea, + GdkRectangle *affectedScreenBounds, + gint affectedBoundsLength +) { + if (!initScreencast(token, affectedScreenBounds, affectedBoundsLength)) { + return pw.pwFd; + } + + if (!doLoop(*requestedArea)) { + return RESULT_ERROR; + } + + while (!isAllDataReady()) { + fp_pw_thread_loop_lock(pw.loop); + fp_pw_thread_loop_wait(pw.loop); + fp_pw_thread_loop_unlock(pw.loop); + if (hasPipewireFailed) { + doCleanup(); + return RESULT_ERROR; + } + } + + return RESULT_OK; +} + +/* + * Class: sun_awt_screencast_ScreencastHelper + * Method: closeSession + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_sun_awt_screencast_ScreencastHelper_closeSession(JNIEnv *env, jclass cls) { + DEBUG_SCREENCAST("closing screencast session\n\n", NULL); + doCleanup(); +} + +/* + * Class: sun_awt_screencast_ScreencastHelper + * Method: getRGBPixelsImpl + * Signature: (IIII[I[ILjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl( + JNIEnv *env, + jclass cls, + jint jx, + jint jy, + jint jwidth, + jint jheight, + jintArray pixelArray, + jintArray affectedScreensBoundsArray, + jstring jtoken +) { + jsize boundsLen = 0; + gint affectedBoundsLength = 0; + if (affectedScreensBoundsArray) { + boundsLen = (*env)->GetArrayLength(env, affectedScreensBoundsArray); + EXCEPTION_CHECK_DESCRIBE(); + if (boundsLen % 4 != 0) { + DEBUG_SCREENCAST("incorrect array length\n", NULL); + return RESULT_ERROR; + } + affectedBoundsLength = boundsLen / 4; + } + + GdkRectangle affectedScreenBounds[affectedBoundsLength]; + arrayToRectangles(env, + affectedScreensBoundsArray, + boundsLen, + (GdkRectangle *) &affectedScreenBounds); + + GdkRectangle requestedArea = { jx, jy, jwidth, jheight}; + + const gchar *token = jtoken + ? (*env)->GetStringUTFChars(env, jtoken, NULL) + : NULL; + + DEBUG_SCREENCAST( + "taking screenshot at \n\tx: %5i y %5i w %5i h %5i with token |%s|\n", + jx, jy, jwidth, jheight, token + ); + + int attemptResult = makeScreencast( + token, &requestedArea, affectedScreenBounds, affectedBoundsLength); + + if (attemptResult) { + if (attemptResult == RESULT_DENIED) { + releaseToken(env, jtoken, token); + return attemptResult; + } + DEBUG_SCREENCAST("Screencast attempt failed with %i, re-trying...\n", + attemptResult); + attemptResult = makeScreencast( + token, &requestedArea, affectedScreenBounds, affectedBoundsLength); + if (attemptResult) { + releaseToken(env, jtoken, token); + return attemptResult; + } + } + + DEBUG_SCREENCAST("\nall data ready\n", NULL); + + for (int i = 0; i < screenSpace.screenCount; ++i) { + struct ScreenProps * screenProps = &screenSpace.screens[i]; + + if (screenProps->shouldCapture) { + GdkRectangle bounds = screenProps->bounds; + GdkRectangle captureArea = screenProps->captureArea; + DEBUG_SCREEN_PREFIX(screenProps, + "@@@ copying screen data %i, captureData %p\n" + "\t||\tx %5i y %5i w %5i h %5i %s\n" + "\t||\tx %5i y %5i w %5i h %5i %s\n" + "\t||\tx %5i y %5i w %5i h %5i %s\n\n", + i, screenProps->captureDataPixbuf, + requestedArea.x, requestedArea.y, + requestedArea.width, requestedArea.height, + "requested area", + + bounds.x, bounds.y, + bounds.width, bounds.height, + "screen bound", + + captureArea.x, captureArea.y, + captureArea.width, captureArea.height, + "in-screen coords capture area" + ); + + if (screenProps->captureDataPixbuf) { + for (int y = 0; y < captureArea.height; y++) { + jsize preY = (requestedArea.y > screenProps->bounds.y) + ? 0 + : screenProps->bounds.y - requestedArea.y; + jsize preX = (requestedArea.x > screenProps->bounds.x) + ? 0 + : screenProps->bounds.x - requestedArea.x; + jsize start = jwidth * (preY + y) + preX; + + jsize len = captureArea.width; + + (*env)->SetIntArrayRegion( + env, pixelArray, + start, len, + ((jint *) gtk->gdk_pixbuf_get_pixels( + screenProps->captureDataPixbuf + )) + + (captureArea.width * y) + ); + } + } + + if (screenProps->captureDataPixbuf) { + gtk->g_object_unref(screenProps->captureDataPixbuf); + screenProps->captureDataPixbuf = NULL; + } + screenProps->shouldCapture = FALSE; + + fp_pw_thread_loop_lock(pw.loop); + fp_pw_stream_set_active(screenProps->data->stream, FALSE); + fp_pw_thread_loop_unlock(pw.loop); + + screenProps->captureDataReady = FALSE; + } + } + + releaseToken(env, jtoken, token); + return 0; +} diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.h b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.h new file mode 100644 index 0000000000000..07a7e91304c50 --- /dev/null +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef HEADLESS +#error This file should not be included in headless library +#endif + + +#ifndef _SCREENCAST_PIPEWIRE_H +#define _SCREENCAST_PIPEWIRE_H + +#include "screencast_portal.h" + +#include +#include + +#include +#include + +void storeRestoreToken(const gchar* oldToken, const gchar* newToken); + +struct ScreenProps { + guint32 id; + GdkRectangle bounds; + + GdkRectangle captureArea; + struct PwStreamData *data; + + GdkPixbuf *captureDataPixbuf; + volatile gboolean shouldCapture; + volatile gboolean captureDataReady; +}; + + +#define SCREEN_SPACE_DEFAULT_ALLOCATED 2 +struct ScreenSpace { + struct ScreenProps *screens; + int screenCount; + int allocated; +}; + +#define DEBUG_SCREENCAST(FORMAT, ...) debug_screencast("%s:%i " FORMAT, \ + __func__, __LINE__, __VA_ARGS__); + +#define DEBUG_SCREEN(SCREEN) \ + DEBUG_SCREENCAST("screenId#%i\n" \ + "||\tbounds x %5i y %5i w %5i h %5i\n" \ + "||\tcapture area x %5i y %5i w %5i h %5i shouldCapture %i\n\n", \ + (SCREEN)->id, \ + (SCREEN)->bounds.x, (SCREEN)->bounds.y, \ + (SCREEN)->bounds.width, (SCREEN)->bounds.height, \ + (SCREEN)->captureArea.x, (SCREEN)->captureArea.y, \ + (SCREEN)->captureArea.width, (SCREEN)->captureArea.height, \ + (SCREEN)->shouldCapture); + +#define DEBUG_SCREEN_PREFIX(SCREEN, FORMAT, ...) \ + DEBUG_SCREENCAST("screenId#%i[loc(%d,%d) size(%dx%d)] "FORMAT, \ + (SCREEN)->id, (SCREEN)->bounds.x, (SCREEN)->bounds.y, \ + (SCREEN)->bounds.width, (SCREEN)->bounds.height, __VA_ARGS__); + +#define ERR(MSG) fprintf(stderr, "%s:%i " MSG, __func__, __LINE__); +#define ERR_HANDLE(ERROR) errHandle((ERROR), __func__, __LINE__); + +struct PwLoopData { + struct pw_thread_loop *loop; + struct pw_context *context; + struct pw_core *core; + struct spa_hook coreListener; + int pwFd; //negative values can also be used to store a failure reason +}; + +struct PwStreamData { + struct pw_stream *stream; + struct spa_hook streamListener; + + struct spa_video_info_raw rawFormat; + struct ScreenProps *screenProps; + + gboolean hasFormat; +}; + +#endif //_SCREENCAST_PIPEWIRE_H diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c new file mode 100644 index 0000000000000..8590cf27da20f --- /dev/null +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c @@ -0,0 +1,906 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "stdlib.h" +#include +#include +#include +#include +#include +#include "screencast_pipewire.h" +#include "screencast_portal.h" + + +extern struct ScreenSpace screenSpace; + +struct XdgDesktopPortalApi *portal = NULL; + +void errHandle( + GError *error, + const gchar *functionName, + int lineNum +) { + if (error) { + fprintf(stderr, "!!! %s:%i Error: domain %i code %i message: \"%s\"\n", + functionName, lineNum, + error->domain, error->code, error->message); + } + if (error) { + gtk->g_error_free(error); + } + error = NULL; +} + +gboolean validateToken(const gchar *token) { + if (!token) { + return FALSE; + } + + gboolean isValid = gtk->g_uuid_string_is_valid(token); + if (!isValid) { + DEBUG_SCREENCAST("!!! restore token " + "is not a valid UUID string:\n\"%s\"\n", + token); + } + return isValid; +} + +/** + * @return TRUE on success + */ +gboolean rebuildScreenData(GVariantIter *iterStreams, gboolean isTheOnlyMon) { + guint32 nodeID; + GVariant* prop = NULL; + + int screenIndex = 0; + + gboolean hasFailures = FALSE; + + while (gtk->g_variant_iter_loop( + iterStreams, + "(u@a{sv})", + &nodeID, + &prop + )) { + DEBUG_SCREENCAST("\n==== screenId#%i\n", nodeID); + + if (screenIndex >= screenSpace.allocated) { + screenSpace.screens = realloc( + screenSpace.screens, + ++screenSpace.allocated * sizeof(struct ScreenProps) + ); + if (!screenSpace.screens) { + ERR("failed to allocate memory\n"); + return FALSE; + } + } + + struct ScreenProps * screen = &screenSpace.screens[screenIndex]; + memset(screen, 0, sizeof(struct ScreenProps)); + + screenSpace.screenCount = screenIndex + 1; + + screen->id = nodeID; + + if ( + !gtk->g_variant_lookup( + prop, + "size", + "(ii)", + &screen->bounds.width, + &screen->bounds.height + ) + || ( + !gtk->g_variant_lookup( + prop, + "position", + "(ii)", + &screen->bounds.x, + &screen->bounds.y + ) + //Screen position is not specified in some cases + //(e.g. on Plasma). + //In this case, proceed only if there is only one screen. + && !isTheOnlyMon + ) + ) { + hasFailures = TRUE; + } + + DEBUG_SCREENCAST("-----------------------\n", NULL); + DEBUG_SCREEN(screen); + DEBUG_SCREENCAST("#---------------------#\n\n", NULL); + + gtk->g_variant_unref(prop); + screenIndex++; + }; + + if (hasFailures) { + DEBUG_SCREENCAST("screenId#%i hasFailures\n", nodeID); + } + + return !hasFailures; +} + +/** + * Checks screencast protocol version + * @return FALSE if version < 4, or could not be determined + */ +gboolean checkVersion() { + static guint32 version = 0; + if (version == 0) { + GError *error = NULL; + GVariant *retVersion = gtk->g_dbus_proxy_call_sync( + portal->screenCastProxy, + "org.freedesktop.DBus.Properties.Get", + gtk->g_variant_new("(ss)", + "org.freedesktop.portal.ScreenCast", + "version"), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL + ); + + if (!retVersion) { //no backend on system + DEBUG_SCREENCAST("!!! could not detect the screencast version\n", + NULL); + return FALSE; + } + + ERR_HANDLE(error); + + GVariant *varVersion = NULL; + gtk->g_variant_get(retVersion, "(v)", &varVersion); + + if (!varVersion){ + gtk->g_variant_unref(retVersion); + DEBUG_SCREENCAST("!!! could not get the screencast version\n", + NULL); + return FALSE; + } + + version = gtk->g_variant_get_uint32(varVersion); + + gtk->g_variant_unref(varVersion); + gtk->g_variant_unref(retVersion); + + } + + DEBUG_SCREENCAST("ScreenCast protocol version %d\n", version); + if (version < 4) { + DEBUG_SCREENCAST("!!! ScreenCast protocol version %d < 4," + " session restore is not available\n", + version); + } + + // restore_token was added in version 4, without it, + // user confirmation is required for every screenshot. + return version >= 4; +} + +/** + * @return TRUE on success + */ +gboolean initXdgDesktopPortal() { + portal = calloc(1, sizeof(*portal)); + + if (!portal) { + ERR("failed to allocate memory\n"); + return FALSE; + } + + GError* err = NULL; + + portal->connection = gtk->g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &err); + + if (err) { + ERR_HANDLE(err); + return FALSE; + } + + const gchar *name = gtk + ->g_dbus_connection_get_unique_name(portal->connection); + if (!name) { + ERR("Failed to get unique connection name\n"); + return FALSE; + } + + GString * nameStr = gtk->g_string_new(name); + gtk->g_string_erase(nameStr, 0, 1); //remove leading colon ":" + gtk->g_string_replace(nameStr, ".", "_", 0); + portal->senderName = nameStr->str; + + gtk->g_string_free(nameStr, FALSE); + + DEBUG_SCREENCAST("connection/sender name %s / %s\n", + name, + portal->senderName); + + portal->screenCastProxy = gtk->g_dbus_proxy_new_sync( + portal->connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.ScreenCast", + NULL, + &err + ); + + if (err) { + DEBUG_SCREENCAST("Failed to get ScreenCast portal: %s", err->message); + ERR_HANDLE(err); + return FALSE; + } + + return checkVersion(); +} + +static void updateRequestPath( + gchar **path, + gchar **token +) { + static uint64_t counter = 0; + ++counter; + + GString *tokenStr = gtk->g_string_new(NULL); + gtk->g_string_printf( + tokenStr, + PORTAL_TOKEN_TEMPLATE, + counter + ); + + *token = tokenStr->str; + gtk->g_string_free(tokenStr, FALSE); + + GString *pathStr = gtk->g_string_new(NULL); + + gtk->g_string_printf( + pathStr, + PORTAL_REQUEST_TEMPLATE, + portal->senderName, + counter + ); + + *path = pathStr->str; + gtk->g_string_free(pathStr, FALSE); +} + +static void updateSessionToken( + gchar **token +) { + static uint64_t counter = 0; + counter++; + + GString *tokenStr = gtk->g_string_new(NULL); + + gtk->g_string_printf( + tokenStr, + PORTAL_TOKEN_TEMPLATE, + counter + ); + + *token = tokenStr->str; + gtk->g_string_free(tokenStr, FALSE); +} + +static void registerScreenCastCallback( + const char *path, + struct DBusCallbackHelper *helper, + GDBusSignalCallback callback +) { + helper->id = gtk->g_dbus_connection_signal_subscribe( + portal->connection, + "org.freedesktop.portal.Desktop", + "org.freedesktop.portal.Request", + "Response", + path, + NULL, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + callback, + helper, + NULL + ); +} + +static void unregisterScreenCastCallback( + struct DBusCallbackHelper *helper +) { + if (helper->id) { + gtk->g_dbus_connection_signal_unsubscribe( + portal->connection, + helper->id + ); + } +} + +static void callbackScreenCastCreateSession( + GDBusConnection *connection, + const char *senderName, + const char *objectPath, + const char *interfaceName, + const char *signalName, + GVariant *parameters, + void *data +) { + struct DBusCallbackHelper *helper = data; + uint32_t status; + GVariant *result = NULL; + + gtk->g_variant_get( + parameters, + "(u@a{sv})", + &status, + &result + ); + + if (status != 0) { + DEBUG_SCREENCAST("Failed to create ScreenCast: %u\n", status); + } else { + gtk->g_variant_lookup(result, "session_handle", "s", helper->data); + } + + helper->isDone = TRUE; +} + +gboolean portalScreenCastCreateSession() { + GError *err = NULL; + + gchar *requestPath = NULL; + gchar *requestToken = NULL; + gchar *sessionToken = NULL; + + struct DBusCallbackHelper helper = { + .id = 0, + .data = &portal->screenCastSessionHandle + }; + + updateRequestPath( + &requestPath, + &requestToken + ); + updateSessionToken(&sessionToken); + + portal->screenCastSessionHandle = NULL; + + registerScreenCastCallback( + requestPath, + &helper, + callbackScreenCastCreateSession + ); + + GVariantBuilder builder; + + gtk->g_variant_builder_init( + &builder, + G_VARIANT_TYPE_VARDICT + ); + + gtk->g_variant_builder_add( + &builder, + "{sv}", + "handle_token", + gtk->g_variant_new_string(requestToken) + ); + + gtk->g_variant_builder_add( + &builder, + "{sv}", + "session_handle_token", + gtk->g_variant_new_string(sessionToken) + ); + + GVariant *response = gtk->g_dbus_proxy_call_sync( + portal->screenCastProxy, + "CreateSession", + gtk->g_variant_new("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &err + ); + + if (err) { + DEBUG_SCREENCAST("Failed to create ScreenCast session: %s\n", + err->message); + ERR_HANDLE(err); + } else { + while (!helper.isDone) { + gtk->g_main_context_iteration(NULL, TRUE); + } + } + + unregisterScreenCastCallback(&helper); + if (response) { + gtk->g_variant_unref(response); + } + + free(sessionToken); + free(requestPath); + free(requestToken); + + return portal->screenCastSessionHandle != NULL; +} + +static void callbackScreenCastSelectSources( + GDBusConnection *connection, + const char *senderName, + const char *objectPath, + const char *interfaceName, + const char *signalName, + GVariant *parameters, + void *data +) { + struct DBusCallbackHelper *helper = data; + + helper->data = (void *) 0; + + uint32_t status; + GVariant* result = NULL; + + gtk->g_variant_get(parameters, "(u@a{sv})", &status, &result); + + if (status != 0) { + DEBUG_SCREENCAST("Failed select sources: %u\n", status); + } else { + helper->data = (void *) 1; + } + + helper->isDone = TRUE; + + if (result) { + gtk->g_variant_unref(result); + } +} + +gboolean portalScreenCastSelectSources(const gchar *token) { + GError* err = NULL; + + gchar *requestPath = NULL; + gchar *requestToken = NULL; + + struct DBusCallbackHelper helper = {0}; + + updateRequestPath( + &requestPath, + &requestToken + ); + + registerScreenCastCallback( + requestPath, + &helper, + callbackScreenCastSelectSources + ); + + GVariantBuilder builder; + + gtk->g_variant_builder_init( + &builder, + G_VARIANT_TYPE_VARDICT + ); + + gtk->g_variant_builder_add( + &builder, + "{sv}", "handle_token", + gtk->g_variant_new_string(requestToken) + ); + + gtk->g_variant_builder_add( + &builder, + "{sv}", "multiple", + gtk->g_variant_new_boolean(TRUE)); + + // 1: MONITOR + // 2: WINDOW + // 4: VIRTUAL + gtk->g_variant_builder_add( + &builder, "{sv}", "types", + gtk->g_variant_new_uint32(1) + ); + + // 0: Do not persist (default) + // 1: Permissions persist as long as the application is running + // 2: Permissions persist until explicitly revoked + gtk->g_variant_builder_add( + &builder, + "{sv}", + "persist_mode", + gtk->g_variant_new_uint32(2) + ); + + if (validateToken(token)) { + gtk->g_variant_builder_add( + &builder, + "{sv}", + "restore_token", + gtk->g_variant_new_string(token) + ); + } + + GVariant *response = gtk->g_dbus_proxy_call_sync( + portal->screenCastProxy, + "SelectSources", + gtk->g_variant_new("(oa{sv})", portal->screenCastSessionHandle, &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &err + ); + + if (err) { + DEBUG_SCREENCAST("Failed to call SelectSources: %s\n", err->message); + ERR_HANDLE(err); + } else { + while (!helper.isDone) { + gtk->g_main_context_iteration(NULL, TRUE); + } + } + + unregisterScreenCastCallback(&helper); + if (response) { + gtk->g_variant_unref(response); + } + + free(requestPath); + free(requestToken); + + return helper.data != NULL; +} + +static void callbackScreenCastStart( + GDBusConnection *connection, + const char *senderName, + const char *objectPath, + const char *interfaceName, + const char *signalName, + GVariant *parameters, + void *data +) { + struct DBusCallbackHelper *helper = data; + struct StartHelper *startHelper = helper->data; + + uint32_t status; + GVariant* result = NULL; + const gchar *oldToken = startHelper->token; + + gtk->g_variant_get(parameters, "(u@a{sv})", &status, &result); + + if (status != 0) { + // Cancel pressed on the system dialog + DEBUG_SCREENCAST("Failed to start screencast: %u\n", status); + startHelper->result = RESULT_DENIED; + helper->isDone = TRUE; + return; + } + + GVariant *streams = gtk->g_variant_lookup_value( + result, + "streams", + G_VARIANT_TYPE_ARRAY + ); + + GVariantIter iter; + gtk->g_variant_iter_init( + &iter, + streams + ); + + size_t count = gtk->g_variant_iter_n_children(&iter); + + DEBUG_SCREENCAST("available screen count %i\n", count); + + startHelper->result = (rebuildScreenData(&iter, count == 1)) + ? RESULT_OK + : RESULT_ERROR; + + DEBUG_SCREENCAST("rebuildScreenData result |%i|\n", startHelper->result); + + if (startHelper->result == RESULT_OK) { + GVariant *restoreTokenVar = gtk->g_variant_lookup_value( + result, + "restore_token", + G_VARIANT_TYPE_STRING + ); + + if (restoreTokenVar) { + gsize len; + const gchar *newToken = gtk-> + g_variant_get_string(restoreTokenVar, &len); + DEBUG_SCREENCAST("restore_token |%s|\n", newToken); + + storeRestoreToken(oldToken, newToken); + + gtk->g_variant_unref(restoreTokenVar); + } + } + + helper->isDone = TRUE; + + if (streams) { + gtk->g_variant_unref(streams); + } +} + +ScreenCastResult portalScreenCastStart(const gchar *token) { + GError *err = NULL; + + gchar *requestPath = NULL; + gchar *requestToken = NULL; + + struct StartHelper startHelper = { 0 }; + startHelper.token = token; + + struct DBusCallbackHelper helper = { 0 }; + helper.data = &startHelper; + + updateRequestPath( + &requestPath, + &requestToken + ); + + registerScreenCastCallback( + requestPath, + &helper, + callbackScreenCastStart + ); + + GVariantBuilder builder; + + gtk->g_variant_builder_init( + &builder, + G_VARIANT_TYPE_VARDICT + ); + + gtk->g_variant_builder_add( + &builder, + "{sv}", + "handle_token", + gtk->g_variant_new_string(requestToken) + ); + + GVariant *response = gtk->g_dbus_proxy_call_sync( + portal->screenCastProxy, + "Start", + gtk->g_variant_new("(osa{sv})", portal->screenCastSessionHandle, "", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &err + ); + + if (err) { + DEBUG_SCREENCAST("Failed to start session: %s\n", err->message); + ERR_HANDLE(err); + } else { + while (!helper.isDone) { + gtk->g_main_context_iteration(NULL, TRUE); + } + } + + unregisterScreenCastCallback(&helper); + if (response) { + gtk->g_variant_unref(response); + } + + free(requestPath); + free(requestToken); + + DEBUG_SCREENCAST("ScreenCastResult |%i|\n", startHelper.result); + + return startHelper.result; +} + +int portalScreenCastOpenPipewireRemote() { + GError* err = NULL; + GUnixFDList* fdList = NULL; + + GVariantBuilder builder; + + gtk->g_variant_builder_init( + &builder, G_VARIANT_TYPE_VARDICT + ); + + GVariant *response = gtk->g_dbus_proxy_call_with_unix_fd_list_sync( + portal->screenCastProxy, + "OpenPipeWireRemote", + gtk->g_variant_new("(oa{sv})", portal->screenCastSessionHandle, &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &fdList, + NULL, + &err + ); + + if (err || !response) { + DEBUG_SCREENCAST("Failed to call OpenPipeWireRemote on session: %s\n", + err->message); + ERR_HANDLE(err); + return RESULT_ERROR; + } + + gint32 index; + gtk->g_variant_get( + response, + "(h)", + &index, + &err + ); + + gtk->g_variant_unref(response); + + if (err) { + DEBUG_SCREENCAST("Failed to get pipewire fd index: %s\n", + err->message); + ERR_HANDLE(err); + return RESULT_ERROR; + } + + int fd = gtk->g_unix_fd_list_get( + fdList, + index, + &err + ); + + if (fdList) { + gtk->g_object_unref(fdList); + } + + if (err) { + DEBUG_SCREENCAST("Failed to get pipewire fd: %s\n", err->message); + ERR_HANDLE(err); + return RESULT_ERROR; + } + + return fd; +} + +void portalScreenCastCleanup() { + if (!portal) { + return; + } + + if (portal->screenCastSessionHandle) { + gtk->g_dbus_connection_call_sync( + portal->connection, + "org.freedesktop.portal.Desktop", + portal->screenCastSessionHandle, + "org.freedesktop.portal.Session", + "Close", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL + ); + + gtk->g_free(portal->screenCastSessionHandle); + portal->screenCastSessionHandle = NULL; + } + + if (portal->connection) { + gtk->g_object_unref(portal->connection); + portal->connection = NULL; + } + + if (portal->screenCastProxy) { + gtk->g_object_unref(portal->screenCastProxy); + portal->screenCastProxy = NULL; + } + + if (portal->senderName) { + free(portal->senderName); + portal->senderName = NULL; + } + + free(portal); + portal = NULL; +} + +gboolean rectanglesEqual(GdkRectangle rect1, GdkRectangle rect2) { + return rect1.x == rect2.x + && rect1.y == rect2.y + && rect1.width == rect2.width + && rect1.height == rect2.height; +} + +gboolean checkCanCaptureAllRequiredScreens(GdkRectangle *affectedBounds, + gint affectedBoundsLength) { + + + if (affectedBoundsLength > screenSpace.screenCount) { + DEBUG_SCREENCAST("Requested screen count is greater " + "than allowed with token (%i > %i)\n", + affectedBoundsLength, screenSpace.screenCount); + return false; + } + + + for (int i = 0; i < affectedBoundsLength; ++i) { + gboolean found = false; + GdkRectangle affBounds = affectedBounds[i]; + for (int j = 0; j < screenSpace.screenCount; ++j) { + GdkRectangle allowedBounds = screenSpace.screens[j].bounds; + + if (rectanglesEqual(allowedBounds, affBounds)) { + DEBUG_SCREENCAST("Found allowed screen bounds in affected " + "screen bounds %i %i %i %i\n", + affBounds.x, affBounds.y, + affBounds.width, affBounds.height); + found = true; + break; + } + } + if (!found) { + DEBUG_SCREENCAST("Could not find required screen %i %i %i %i " + "in allowed bounds\n", + affBounds.x, affBounds.y, + affBounds.width, affBounds.height); + return false; + } + } + + return true; +} + + +int getPipewireFd(const gchar *token, + GdkRectangle *affectedBounds, + gint affectedBoundsLength) { + if (!portalScreenCastCreateSession()) { + DEBUG_SCREENCAST("Failed to create ScreenCast session\n", NULL); + return RESULT_ERROR; + } + + if (!portalScreenCastSelectSources(token)) { + DEBUG_SCREENCAST("Failed to select sources\n", NULL); + return RESULT_ERROR; + } + + ScreenCastResult startResult = portalScreenCastStart(token); + DEBUG_SCREENCAST("portalScreenCastStart result |%i|\n", startResult); + if (startResult != RESULT_OK) { + DEBUG_SCREENCAST("Failed to start\n", NULL); + return startResult; + } else { + if (!checkCanCaptureAllRequiredScreens(affectedBounds, + affectedBoundsLength)) { + DEBUG_SCREENCAST("The location of the screens has changed, " + "the capture area is outside the allowed " + "area.\n", NULL) + return RESULT_OUT_OF_BOUNDS; + } + } + + DEBUG_SCREENCAST("--- portalScreenCastStart\n", NULL); + + int pipewireFd = portalScreenCastOpenPipewireRemote(); + if (pipewireFd < 0) { + DEBUG_SCREENCAST("!!! Failed to get pipewire fd\n", NULL); + } + + DEBUG_SCREENCAST("pwFd %i\n", pipewireFd); + return pipewireFd; +} diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.h b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.h new file mode 100644 index 0000000000000..9ac210217fff3 --- /dev/null +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef HEADLESS +#error This file should not be included in headless library +#endif + +#ifndef _SCREENCAST_PORTAL_H +#define _SCREENCAST_PORTAL_H + +#include "gtk_interface.h" + +#define PORTAL_TOKEN_TEMPLATE "awtPipewire%lu" +#define PORTAL_REQUEST_TEMPLATE "/org/freedesktop/portal/desktop/" \ + "request/%s/awtPipewire%lu" + +void debug_screencast(const char *__restrict fmt, ...); + +int getPipewireFd(const gchar *token, + GdkRectangle *affectedBounds, + gint affectedBoundsLength); + +void portalScreenCastCleanup(); + +gboolean initXdgDesktopPortal(); + +void errHandle(GError *error, const gchar *functionName, int lineNum); + +struct XdgDesktopPortalApi { + GDBusConnection *connection; + GDBusProxy *screenCastProxy; + gchar *senderName; + char *screenCastSessionHandle; +}; + +struct DBusCallbackHelper { + guint id; + void *data; + gboolean isDone; +}; + +typedef enum { + RESULT_OK = 0, + RESULT_ERROR = -1, + RESULT_DENIED = -11, + RESULT_OUT_OF_BOUNDS = -12, +} ScreenCastResult; + +struct StartHelper { + const gchar *token; + ScreenCastResult result; +}; + +#endif //_SCREENCAST_PORTAL_H diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/context.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/context.h new file mode 100644 index 0000000000000..b02baf6c4c55f --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/context.h @@ -0,0 +1,175 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_CONTEXT_H +#define PIPEWIRE_CONTEXT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** \defgroup pw_context Context + * + * \brief The PipeWire context object manages all locally available + * resources. It is used by both clients and servers. + * + * The context is used to: + * + * - Load modules and extend the functionality. This includes + * extending the protocol with new object types or creating + * any of the available objects. + * + * - Create implementations of various objects like nodes, + * devices, factories, modules, etc.. This will usually also + * create pw_global objects that can then be shared with + * clients. + * + * - Connect to another PipeWire instance (the main daemon, for + * example) and interact with it (See \ref page_core_api). + * + * - Export a local implementation of an object to another + * instance. + */ + +/** + * \addtogroup pw_context + * @{ + */ +struct pw_context; + +struct pw_global; +struct pw_impl_client; + +#include +#include +#include + +/** context events emitted by the context object added with \ref pw_context_add_listener */ +struct pw_context_events { +#define PW_VERSION_CONTEXT_EVENTS 0 + uint32_t version; + + /** The context is being destroyed */ + void (*destroy) (void *data); + /** The context is being freed */ + void (*free) (void *data); + /** a new client object is added */ + void (*check_access) (void *data, struct pw_impl_client *client); + /** a new global object was added */ + void (*global_added) (void *data, struct pw_global *global); + /** a global object was removed */ + void (*global_removed) (void *data, struct pw_global *global); +}; + +/** Make a new context object for a given main_loop. Ownership of the properties is taken */ +struct pw_context * pw_context_new(struct pw_loop *main_loop, /**< a main loop to run in */ + struct pw_properties *props, /**< extra properties */ + size_t user_data_size /**< extra user data size */); + +/** destroy a context object, all resources except the main_loop will be destroyed */ +void pw_context_destroy(struct pw_context *context); + +/** Get the context user data */ +void *pw_context_get_user_data(struct pw_context *context); + +/** Add a new event listener to a context */ +void pw_context_add_listener(struct pw_context *context, + struct spa_hook *listener, + const struct pw_context_events *events, + void *data); + +/** Get the context properties */ +const struct pw_properties *pw_context_get_properties(struct pw_context *context); + +/** Update the context properties */ +int pw_context_update_properties(struct pw_context *context, const struct spa_dict *dict); + +/** Get a config section for this context. Since 0.3.22, deprecated, + * use pw_context_conf_section_for_each(). */ +const char *pw_context_get_conf_section(struct pw_context *context, const char *section); +/** Parse a standard config section for this context. Since 0.3.22 */ +int pw_context_parse_conf_section(struct pw_context *context, + struct pw_properties *conf, const char *section); + +/** update properties from a section into props. Since 0.3.45 */ +int pw_context_conf_update_props(struct pw_context *context, const char *section, + struct pw_properties *props); +/** emit callback for all config sections. Since 0.3.45 */ +int pw_context_conf_section_for_each(struct pw_context *context, const char *section, + int (*callback) (void *data, const char *location, const char *section, + const char *str, size_t len), + void *data); +/** emit callback for all matched properties. Since 0.3.46 */ +int pw_context_conf_section_match_rules(struct pw_context *context, const char *section, + const struct spa_dict *props, + int (*callback) (void *data, const char *location, const char *action, + const char *str, size_t len), + void *data); + +/** Get the context support objects */ +const struct spa_support *pw_context_get_support(struct pw_context *context, uint32_t *n_support); + +/** get the context main loop */ +struct pw_loop *pw_context_get_main_loop(struct pw_context *context); + +/** get the context data loop. Since 0.3.56 */ +struct pw_data_loop *pw_context_get_data_loop(struct pw_context *context); + +/** Get the work queue from the context: Since 0.3.26 */ +struct pw_work_queue *pw_context_get_work_queue(struct pw_context *context); + +/** Iterate the globals of the context. The callback should return + * 0 to fetch the next item, any other value stops the iteration and returns + * the value. When all callbacks return 0, this function returns 0 when all + * globals are iterated. */ +int pw_context_for_each_global(struct pw_context *context, + int (*callback) (void *data, struct pw_global *global), + void *data); + +/** Find a context global by id */ +struct pw_global *pw_context_find_global(struct pw_context *context, /**< the context */ + uint32_t id /**< the global id */); + +/** add a spa library for the given factory_name regex */ +int pw_context_add_spa_lib(struct pw_context *context, const char *factory_regex, const char *lib); + +/** find the library name for a spa factory */ +const char * pw_context_find_spa_lib(struct pw_context *context, const char *factory_name); + +struct spa_handle *pw_context_load_spa_handle(struct pw_context *context, + const char *factory_name, + const struct spa_dict *info); + + +/** data for registering export functions */ +struct pw_export_type { + struct spa_list link; + const char *type; + struct pw_proxy * (*func) (struct pw_core *core, + const char *type, const struct spa_dict *props, void *object, + size_t user_data_size); +}; + +/** register a type that can be exported on a context_proxy. This is usually used by + * extension modules */ +int pw_context_register_export_type(struct pw_context *context, struct pw_export_type *type); +/** find information about registered export type */ +const struct pw_export_type *pw_context_find_export_type(struct pw_context *context, const char *type); + +/** add an object to the context */ +int pw_context_set_object(struct pw_context *context, const char *type, void *value); +/** get an object from the context */ +void *pw_context_get_object(struct pw_context *context, const char *type); + +/** + * \} + */ +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_CONTEXT_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/core.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/core.h new file mode 100644 index 0000000000000..23a9e16dfbf86 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/core.h @@ -0,0 +1,612 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_CORE_H +#define PIPEWIRE_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +/** \defgroup pw_core Core + * + * \brief The core global object. + * + * This is a special singleton object. It is used for internal PipeWire + * protocol features. Connecting to a PipeWire instance returns one core + * object, the caller should then register event listeners + * using \ref pw_core_add_listener. + * + * Updates to the core object are then provided through the \ref + * pw_core_events interface. See \ref page_tutorial2 for an example. + */ + +/** + * \addtogroup pw_core + * \{ + */ +#define PW_TYPE_INTERFACE_Core PW_TYPE_INFO_INTERFACE_BASE "Core" +#define PW_TYPE_INTERFACE_Registry PW_TYPE_INFO_INTERFACE_BASE "Registry" + +#define PW_VERSION_CORE 4 +struct pw_core; +#define PW_VERSION_REGISTRY 3 +struct pw_registry; + +/** The default remote name to connect to */ +#define PW_DEFAULT_REMOTE "pipewire-0" + +/** default ID for the core object after connect */ +#define PW_ID_CORE 0 + +/* invalid ID that matches any object when used for permissions */ +#define PW_ID_ANY (uint32_t)(0xffffffff) + +/** The core information. Extra information may be added in later versions, + * clients must not assume a constant struct size */ +struct pw_core_info { + uint32_t id; /**< id of the global */ + uint32_t cookie; /**< a random cookie for identifying this instance of PipeWire */ + const char *user_name; /**< name of the user that started the core */ + const char *host_name; /**< name of the machine the core is running on */ + const char *version; /**< version of the core */ + const char *name; /**< name of the core */ +#define PW_CORE_CHANGE_MASK_PROPS (1 << 0) +#define PW_CORE_CHANGE_MASK_ALL ((1 << 1)-1) + uint64_t change_mask; /**< bitfield of changed fields since last call */ + struct spa_dict *props; /**< extra properties */ +}; + +#include +#include +#include + +/** Update an existing \ref pw_core_info with \a update with reset */ +struct pw_core_info * +pw_core_info_update(struct pw_core_info *info, + const struct pw_core_info *update); +/** Update an existing \ref pw_core_info with \a update */ +struct pw_core_info * +pw_core_info_merge(struct pw_core_info *info, + const struct pw_core_info *update, bool reset); +/** Free a \ref pw_core_info */ +void pw_core_info_free(struct pw_core_info *info); + +/** Core */ + +#define PW_CORE_EVENT_INFO 0 +#define PW_CORE_EVENT_DONE 1 +#define PW_CORE_EVENT_PING 2 +#define PW_CORE_EVENT_ERROR 3 +#define PW_CORE_EVENT_REMOVE_ID 4 +#define PW_CORE_EVENT_BOUND_ID 5 +#define PW_CORE_EVENT_ADD_MEM 6 +#define PW_CORE_EVENT_REMOVE_MEM 7 +#define PW_CORE_EVENT_BOUND_PROPS 8 +#define PW_CORE_EVENT_NUM 9 + +/** \struct pw_core_events + * \brief Core events + */ +struct pw_core_events { +#define PW_VERSION_CORE_EVENTS 1 + uint32_t version; + + /** + * Notify new core info + * + * This event is emitted when first bound to the core or when the + * hello method is called. + * + * \param info new core info + */ + void (*info) (void *data, const struct pw_core_info *info); + /** + * Emit a done event + * + * The done event is emitted as a result of a sync method with the + * same seq number. + * + * \param seq the seq number passed to the sync method call + */ + void (*done) (void *data, uint32_t id, int seq); + + /** Emit a ping event + * + * The client should reply with a pong reply with the same seq + * number. + */ + void (*ping) (void *data, uint32_t id, int seq); + + /** + * Fatal error event + * + * The error event is sent out when a fatal (non-recoverable) + * error has occurred. The id argument is the proxy object where + * the error occurred, most often in response to a request to that + * object. The message is a brief description of the error, + * for (debugging) convenience. + * + * This event is usually also emitted on the proxy object with + * \a id. + * + * \param id object where the error occurred + * \param seq the sequence number that generated the error + * \param res error code + * \param message error description + */ + void (*error) (void *data, uint32_t id, int seq, int res, const char *message); + /** + * Remove an object ID + * + * This event is used internally by the object ID management + * logic. When a client deletes an object, the server will send + * this event to acknowledge that it has seen the delete request. + * When the client receives this event, it will know that it can + * safely reuse the object ID. + * + * \param id deleted object ID + */ + void (*remove_id) (void *data, uint32_t id); + + /** + * Notify an object binding + * + * This event is emitted when a local object ID is bound to a + * global ID. It is emitted before the global becomes visible in the + * registry. + * + * \param id bound object ID + * \param global_id the global id bound to + */ + void (*bound_id) (void *data, uint32_t id, uint32_t global_id); + + /** + * Add memory for a client + * + * Memory is given to a client as \a fd of a certain + * memory \a type. + * + * Further references to this fd will be made with the per memory + * unique identifier \a id. + * + * \param id the unique id of the memory + * \param type the memory type, one of enum spa_data_type + * \param fd the file descriptor + * \param flags extra flags + */ + void (*add_mem) (void *data, uint32_t id, uint32_t type, int fd, uint32_t flags); + + /** + * Remove memory for a client + * + * \param id the memory id to remove + */ + void (*remove_mem) (void *data, uint32_t id); + + void (*bound_props) (void *data, uint32_t id, uint32_t global_id, const struct spa_dict *props); +}; + +#define PW_CORE_METHOD_ADD_LISTENER 0 +#define PW_CORE_METHOD_HELLO 1 +#define PW_CORE_METHOD_SYNC 2 +#define PW_CORE_METHOD_PONG 3 +#define PW_CORE_METHOD_ERROR 4 +#define PW_CORE_METHOD_GET_REGISTRY 5 +#define PW_CORE_METHOD_CREATE_OBJECT 6 +#define PW_CORE_METHOD_DESTROY 7 +#define PW_CORE_METHOD_NUM 8 + +/** + * \struct pw_core_methods + * \brief Core methods + * + * The core global object. This is a singleton object used for + * creating new objects in the remote PipeWire instance. It is + * also used for internal features. + */ +struct pw_core_methods { +#define PW_VERSION_CORE_METHODS 0 + uint32_t version; + + int (*add_listener) (void *object, + struct spa_hook *listener, + const struct pw_core_events *events, + void *data); + /** + * Start a conversation with the server. This will send + * the core info and will destroy all resources for the client + * (except the core and client resource). + */ + int (*hello) (void *object, uint32_t version); + /** + * Do server roundtrip + * + * Ask the server to emit the 'done' event with \a seq. + * + * Since methods are handled in-order and events are delivered + * in-order, this can be used as a barrier to ensure all previous + * methods and the resulting events have been handled. + * + * \param seq the seq number passed to the done event + */ + int (*sync) (void *object, uint32_t id, int seq); + /** + * Reply to a server ping event. + * + * Reply to the server ping event with the same seq. + * + * \param seq the seq number received in the ping event + */ + int (*pong) (void *object, uint32_t id, int seq); + /** + * Fatal error event + * + * The error method is sent out when a fatal (non-recoverable) + * error has occurred. The id argument is the proxy object where + * the error occurred, most often in response to an event on that + * object. The message is a brief description of the error, + * for (debugging) convenience. + * + * This method is usually also emitted on the resource object with + * \a id. + * + * \param id object where the error occurred + * \param res error code + * \param message error description + */ + int (*error) (void *object, uint32_t id, int seq, int res, const char *message); + /** + * Get the registry object + * + * Create a registry object that allows the client to list and bind + * the global objects available from the PipeWire server + * \param version the client version + * \param user_data_size extra size + */ + struct pw_registry * (*get_registry) (void *object, uint32_t version, + size_t user_data_size); + + /** + * Create a new object on the PipeWire server from a factory. + * + * \param factory_name the factory name to use + * \param type the interface to bind to + * \param version the version of the interface + * \param props extra properties + * \param user_data_size extra size + */ + void * (*create_object) (void *object, + const char *factory_name, + const char *type, + uint32_t version, + const struct spa_dict *props, + size_t user_data_size); + /** + * Destroy an resource + * + * Destroy the server resource for the given proxy. + * + * \param obj the proxy to destroy + */ + int (*destroy) (void *object, void *proxy); +}; + +#define pw_core_method(o,method,version,...) \ +({ \ + int _res = -ENOTSUP; \ + spa_interface_call_res((struct spa_interface*)o, \ + struct pw_core_methods, _res, \ + method, version, ##__VA_ARGS__); \ + _res; \ +}) + +#define pw_core_add_listener(c,...) pw_core_method(c,add_listener,0,__VA_ARGS__) +#define pw_core_hello(c,...) pw_core_method(c,hello,0,__VA_ARGS__) +#define pw_core_sync(c,...) pw_core_method(c,sync,0,__VA_ARGS__) +#define pw_core_pong(c,...) pw_core_method(c,pong,0,__VA_ARGS__) +#define pw_core_error(c,...) pw_core_method(c,error,0,__VA_ARGS__) + + +static inline +SPA_PRINTF_FUNC(5, 0) int +pw_core_errorv(struct pw_core *core, uint32_t id, int seq, + int res, const char *message, va_list args) +{ + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), message, args); + buffer[1023] = '\0'; + return pw_core_error(core, id, seq, res, buffer); +} + +static inline +SPA_PRINTF_FUNC(5, 6) int +pw_core_errorf(struct pw_core *core, uint32_t id, int seq, + int res, const char *message, ...) +{ + va_list args; + int r; + va_start(args, message); + r = pw_core_errorv(core, id, seq, res, message, args); + va_end(args); + return r; +} + +static inline struct pw_registry * +pw_core_get_registry(struct pw_core *core, uint32_t version, size_t user_data_size) +{ + struct pw_registry *res = NULL; + spa_interface_call_res((struct spa_interface*)core, + struct pw_core_methods, res, + get_registry, 0, version, user_data_size); + return res; +} + +static inline void * +pw_core_create_object(struct pw_core *core, + const char *factory_name, + const char *type, + uint32_t version, + const struct spa_dict *props, + size_t user_data_size) +{ + void *res = NULL; + spa_interface_call_res((struct spa_interface*)core, + struct pw_core_methods, res, + create_object, 0, factory_name, + type, version, props, user_data_size); + return res; +} + +#define pw_core_destroy(c,...) pw_core_method(c,destroy,0,__VA_ARGS__) + +/** + * \} + */ + +/** \defgroup pw_registry Registry + * + * The registry object is a singleton object that keeps track of + * global objects on the PipeWire instance. See also \ref pw_global. + * + * Global objects typically represent an actual object in PipeWire + * (for example, a module or node) or they are singleton + * objects such as the core. + * + * When a client creates a registry object, the registry object + * will emit a global event for each global currently in the + * registry. Globals come and go as a result of device hotplugs or + * reconfiguration or other events, and the registry will send out + * global and global_remove events to keep the client up to date + * with the changes. To mark the end of the initial burst of + * events, the client can use the pw_core.sync methosd immediately + * after calling pw_core.get_registry. + * + * A client can bind to a global object by using the bind + * request. This creates a client-side proxy that lets the object + * emit events to the client and lets the client invoke methods on + * the object. See \ref page_proxy + * + * Clients can also change the permissions of the global objects that + * it can see. This is interesting when you want to configure a + * pipewire session before handing it to another application. You + * can, for example, hide certain existing or new objects or limit + * the access permissions on an object. + */ + +/** + * \addtogroup pw_registry + * \{ + */ + +#define PW_REGISTRY_EVENT_GLOBAL 0 +#define PW_REGISTRY_EVENT_GLOBAL_REMOVE 1 +#define PW_REGISTRY_EVENT_NUM 2 + +/** Registry events */ +struct pw_registry_events { +#define PW_VERSION_REGISTRY_EVENTS 0 + uint32_t version; + /** + * Notify of a new global object + * + * The registry emits this event when a new global object is + * available. + * + * \param id the global object id + * \param permissions the permissions of the object + * \param type the type of the interface + * \param version the version of the interface + * \param props extra properties of the global + */ + void (*global) (void *data, uint32_t id, + uint32_t permissions, const char *type, uint32_t version, + const struct spa_dict *props); + /** + * Notify of a global object removal + * + * Emitted when a global object was removed from the registry. + * If the client has any bindings to the global, it should destroy + * those. + * + * \param id the id of the global that was removed + */ + void (*global_remove) (void *data, uint32_t id); +}; + +#define PW_REGISTRY_METHOD_ADD_LISTENER 0 +#define PW_REGISTRY_METHOD_BIND 1 +#define PW_REGISTRY_METHOD_DESTROY 2 +#define PW_REGISTRY_METHOD_NUM 3 + +/** Registry methods */ +struct pw_registry_methods { +#define PW_VERSION_REGISTRY_METHODS 0 + uint32_t version; + + int (*add_listener) (void *object, + struct spa_hook *listener, + const struct pw_registry_events *events, + void *data); + /** + * Bind to a global object + * + * Bind to the global object with \a id and use the client proxy + * with new_id as the proxy. After this call, methods can be + * send to the remote global object and events can be received + * + * \param id the global id to bind to + * \param type the interface type to bind to + * \param version the interface version to use + * \returns the new object + */ + void * (*bind) (void *object, uint32_t id, const char *type, uint32_t version, + size_t use_data_size); + + /** + * Attempt to destroy a global object + * + * Try to destroy the global object. + * + * \param id the global id to destroy + */ + int (*destroy) (void *object, uint32_t id); +}; + +#define pw_registry_method(o,method,version,...) \ +({ \ + int _res = -ENOTSUP; \ + spa_interface_call_res((struct spa_interface*)o, \ + struct pw_registry_methods, _res, \ + method, version, ##__VA_ARGS__); \ + _res; \ +}) + +/** Registry */ +#define pw_registry_add_listener(p,...) pw_registry_method(p,add_listener,0,__VA_ARGS__) + +static inline void * +pw_registry_bind(struct pw_registry *registry, + uint32_t id, const char *type, uint32_t version, + size_t user_data_size) +{ + void *res = NULL; + spa_interface_call_res((struct spa_interface*)registry, + struct pw_registry_methods, res, + bind, 0, id, type, version, user_data_size); + return res; +} + +#define pw_registry_destroy(p,...) pw_registry_method(p,destroy,0,__VA_ARGS__) + +/** + * \} + */ + +/** + * \addtogroup pw_core + * \{ + */ + +/** Connect to a PipeWire instance + * + * \param context a \ref pw_context + * \param properties optional properties, ownership of the properties is + * taken. + * \param user_data_size extra user data size + * + * \return a \ref pw_core on success or NULL with errno set on error. The core + * will have an id of \ref PW_ID_CORE (0) + */ +struct pw_core * +pw_context_connect(struct pw_context *context, + struct pw_properties *properties, + size_t user_data_size); + +/** Connect to a PipeWire instance on the given socket + * + * \param context a \ref pw_context + * \param fd the connected socket to use, the socket will be closed + * automatically on disconnect or error. + * \param properties optional properties, ownership of the properties is + * taken. + * \param user_data_size extra user data size + * + * \return a \ref pw_core on success or NULL with errno set on error */ +struct pw_core * +pw_context_connect_fd(struct pw_context *context, + int fd, + struct pw_properties *properties, + size_t user_data_size); + +/** Connect to a given PipeWire instance + * + * \param context a \ref pw_context to connect to + * \param properties optional properties, ownership of the properties is + * taken. + * \param user_data_size extra user data size + * + * \return a \ref pw_core on success or NULL with errno set on error */ +struct pw_core * +pw_context_connect_self(struct pw_context *context, + struct pw_properties *properties, + size_t user_data_size); + +/** Steal the fd of the core connection or < 0 on error. The core + * will be disconnected after this call. */ +int pw_core_steal_fd(struct pw_core *core); + +/** Pause or resume the core. When the core is paused, no new events + * will be dispatched until the core is resumed again. */ +int pw_core_set_paused(struct pw_core *core, bool paused); + +/** disconnect and destroy a core */ +int pw_core_disconnect(struct pw_core *core); + +/** Get the user_data. It is of the size specified when this object was + * constructed */ +void *pw_core_get_user_data(struct pw_core *core); + +/** Get the client proxy of the connected core. This will have the id + * of PW_ID_CLIENT (1) */ +struct pw_client * pw_core_get_client(struct pw_core *core); + +/** Get the context object used to created this core */ +struct pw_context * pw_core_get_context(struct pw_core *core); + +/** Get properties from the core */ +const struct pw_properties *pw_core_get_properties(struct pw_core *core); + +/** Update the core properties. This updates the properties + * of the associated client. + * \return the number of properties that were updated */ +int pw_core_update_properties(struct pw_core *core, const struct spa_dict *dict); + +/** Get the core mempool object */ +struct pw_mempool * pw_core_get_mempool(struct pw_core *core); + +/** Get the proxy with the given id */ +struct pw_proxy *pw_core_find_proxy(struct pw_core *core, uint32_t id); + +/** Export an object into the PipeWire instance associated with core */ +struct pw_proxy *pw_core_export(struct pw_core *core, /**< the core */ + const char *type, /**< the type of object */ + const struct spa_dict *props, /**< extra properties */ + void *object, /**< object to export */ + size_t user_data_size /**< extra user data */); + +/** + * \} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_CORE_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/keys.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/keys.h new file mode 100644 index 0000000000000..b7de764657a94 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/keys.h @@ -0,0 +1,343 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_KEYS_H +#define PIPEWIRE_KEYS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +/** + * \defgroup pw_keys Key Names + * + * A collection of keys that are used to add extra information on objects. + * + * Keys that start with "pipewire." are in general set-once and then + * read-only. They are usually used for security sensitive information that + * needs to be fixed. + * + * Properties from other objects can also appear. This usually suggests some + * sort of parent/child or owner/owned relationship. + * + * \addtogroup pw_keys + * \{ + */ +#define PW_KEY_PROTOCOL "pipewire.protocol" /**< protocol used for connection */ +#define PW_KEY_ACCESS "pipewire.access" /**< how the client access is controlled */ +#define PW_KEY_CLIENT_ACCESS "pipewire.client.access"/**< how the client wants to be access + * controlled */ + +/** Various keys related to the identity of a client process and its security. + * Must be obtained from trusted sources by the protocol and placed as + * read-only properties. */ +#define PW_KEY_SEC_PID "pipewire.sec.pid" /**< Client pid, set by protocol */ +#define PW_KEY_SEC_UID "pipewire.sec.uid" /**< Client uid, set by protocol*/ +#define PW_KEY_SEC_GID "pipewire.sec.gid" /**< client gid, set by protocol*/ +#define PW_KEY_SEC_LABEL "pipewire.sec.label" /**< client security label, set by protocol*/ + +#define PW_KEY_LIBRARY_NAME_SYSTEM "library.name.system" /**< name of the system library to use */ +#define PW_KEY_LIBRARY_NAME_LOOP "library.name.loop" /**< name of the loop library to use */ +#define PW_KEY_LIBRARY_NAME_DBUS "library.name.dbus" /**< name of the dbus library to use */ + +/** object properties */ +#define PW_KEY_OBJECT_PATH "object.path" /**< unique path to construct the object */ +#define PW_KEY_OBJECT_ID "object.id" /**< a global object id */ +#define PW_KEY_OBJECT_SERIAL "object.serial" /**< a 64 bit object serial number. This is a number + * incremented for each object that is created. + * The lower 32 bits are guaranteed to never be + * SPA_ID_INVALID. */ +#define PW_KEY_OBJECT_LINGER "object.linger" /**< the object lives on even after the client + * that created it has been destroyed */ +#define PW_KEY_OBJECT_REGISTER "object.register" /**< If the object should be registered. */ + + +/* config */ +#define PW_KEY_CONFIG_PREFIX "config.prefix" /**< a config prefix directory */ +#define PW_KEY_CONFIG_NAME "config.name" /**< a config file name */ +#define PW_KEY_CONFIG_OVERRIDE_PREFIX "config.override.prefix" /**< a config override prefix directory */ +#define PW_KEY_CONFIG_OVERRIDE_NAME "config.override.name" /**< a config override file name */ + +/* context */ +#define PW_KEY_CONTEXT_PROFILE_MODULES "context.profile.modules" /**< a context profile for modules, deprecated */ +#define PW_KEY_USER_NAME "context.user-name" /**< The user name that runs pipewire */ +#define PW_KEY_HOST_NAME "context.host-name" /**< The host name of the machine */ + +/* core */ +#define PW_KEY_CORE_NAME "core.name" /**< The name of the core. Default is + * `pipewire--`, overwritten + * by env(PIPEWIRE_CORE) */ +#define PW_KEY_CORE_VERSION "core.version" /**< The version of the core. */ +#define PW_KEY_CORE_DAEMON "core.daemon" /**< If the core is listening for connections. */ + +#define PW_KEY_CORE_ID "core.id" /**< the core id */ +#define PW_KEY_CORE_MONITORS "core.monitors" /**< the apis monitored by core. */ + +/* cpu */ +#define PW_KEY_CPU_MAX_ALIGN "cpu.max-align" /**< maximum alignment needed to support + * all CPU optimizations */ +#define PW_KEY_CPU_CORES "cpu.cores" /**< number of cores */ + +/* priorities */ +#define PW_KEY_PRIORITY_SESSION "priority.session" /**< priority in session manager */ +#define PW_KEY_PRIORITY_DRIVER "priority.driver" /**< priority to be a driver */ + +/* remote keys */ +#define PW_KEY_REMOTE_NAME "remote.name" /**< The name of the remote to connect to, + * default pipewire-0, overwritten by + * env(PIPEWIRE_REMOTE) */ +#define PW_KEY_REMOTE_INTENTION "remote.intention" /**< The intention of the remote connection, + * "generic", "screencast" */ + +/** application keys */ +#define PW_KEY_APP_NAME "application.name" /**< application name. Ex: "Totem Music Player" */ +#define PW_KEY_APP_ID "application.id" /**< a textual id for identifying an + * application logically. Ex: "org.gnome.Totem" */ +#define PW_KEY_APP_VERSION "application.version" /**< application version. Ex: "1.2.0" */ +#define PW_KEY_APP_ICON "application.icon" /**< aa base64 blob with PNG image data */ +#define PW_KEY_APP_ICON_NAME "application.icon-name" /**< an XDG icon name for the application. + * Ex: "totem" */ +#define PW_KEY_APP_LANGUAGE "application.language" /**< application language if applicable, in + * standard POSIX format. Ex: "en_GB" */ + +#define PW_KEY_APP_PROCESS_ID "application.process.id" /**< process id (pid)*/ +#define PW_KEY_APP_PROCESS_BINARY "application.process.binary" /**< binary name */ +#define PW_KEY_APP_PROCESS_USER "application.process.user" /**< user name */ +#define PW_KEY_APP_PROCESS_HOST "application.process.host" /**< host name */ +#define PW_KEY_APP_PROCESS_MACHINE_ID "application.process.machine-id" /**< the D-Bus host id the + * application runs on */ +#define PW_KEY_APP_PROCESS_SESSION_ID "application.process.session-id" /**< login session of the + * application, on Unix the + * value of $XDG_SESSION_ID. */ +/** window system */ +#define PW_KEY_WINDOW_X11_DISPLAY "window.x11.display" /**< the X11 display string. Ex. ":0.0" */ + +/** Client properties */ +#define PW_KEY_CLIENT_ID "client.id" /**< a client id */ +#define PW_KEY_CLIENT_NAME "client.name" /**< the client name */ +#define PW_KEY_CLIENT_API "client.api" /**< the client api used to access + * PipeWire */ + +/** Node keys */ +#define PW_KEY_NODE_ID "node.id" /**< node id */ +#define PW_KEY_NODE_NAME "node.name" /**< node name */ +#define PW_KEY_NODE_NICK "node.nick" /**< short node name */ +#define PW_KEY_NODE_DESCRIPTION "node.description" /**< localized human readable node one-line + * description. Ex. "Foobar USB Headset" */ +#define PW_KEY_NODE_PLUGGED "node.plugged" /**< when the node was created. As a uint64 in + * nanoseconds. */ + +#define PW_KEY_NODE_SESSION "node.session" /**< the session id this node is part of */ +#define PW_KEY_NODE_GROUP "node.group" /**< the group id this node is part of. Nodes + * in the same group are always scheduled + * with the same driver. */ +#define PW_KEY_NODE_EXCLUSIVE "node.exclusive" /**< node wants exclusive access to resources */ +#define PW_KEY_NODE_AUTOCONNECT "node.autoconnect" /**< node wants to be automatically connected + * to a compatible node */ +#define PW_KEY_NODE_LATENCY "node.latency" /**< the requested latency of the node as + * a fraction. Ex: 128/48000 */ +#define PW_KEY_NODE_MAX_LATENCY "node.max-latency" /**< the maximum supported latency of the + * node as a fraction. Ex: 1024/48000 */ +#define PW_KEY_NODE_LOCK_QUANTUM "node.lock-quantum" /**< don't change quantum when this node + * is active */ +#define PW_KEY_NODE_FORCE_QUANTUM "node.force-quantum" /**< force a quantum while the node is + * active */ +#define PW_KEY_NODE_RATE "node.rate" /**< the requested rate of the graph as + * a fraction. Ex: 1/48000 */ +#define PW_KEY_NODE_LOCK_RATE "node.lock-rate" /**< don't change rate when this node + * is active */ +#define PW_KEY_NODE_FORCE_RATE "node.force-rate" /**< force a rate while the node is + * active. A value of 0 takes the denominator + * of node.rate */ + +#define PW_KEY_NODE_DONT_RECONNECT "node.dont-reconnect" /**< don't reconnect this node. The node is + * initially linked to target.object or the + * default node. If the target is removed, + * the node is destroyed */ +#define PW_KEY_NODE_ALWAYS_PROCESS "node.always-process" /**< process even when unlinked */ +#define PW_KEY_NODE_WANT_DRIVER "node.want-driver" /**< the node wants to be grouped with a driver + * node in order to schedule the graph. */ +#define PW_KEY_NODE_PAUSE_ON_IDLE "node.pause-on-idle" /**< pause the node when idle */ +#define PW_KEY_NODE_SUSPEND_ON_IDLE "node.suspend-on-idle" /**< suspend the node when idle */ +#define PW_KEY_NODE_CACHE_PARAMS "node.cache-params" /**< cache the node params */ +#define PW_KEY_NODE_TRANSPORT_SYNC "node.transport.sync" /**< the node handles transport sync */ +#define PW_KEY_NODE_DRIVER "node.driver" /**< node can drive the graph */ +#define PW_KEY_NODE_STREAM "node.stream" /**< node is a stream, the server side should + * add a converter */ +#define PW_KEY_NODE_VIRTUAL "node.virtual" /**< the node is some sort of virtual + * object */ +#define PW_KEY_NODE_PASSIVE "node.passive" /**< indicate that a node wants passive links + * on output/input/all ports when the value is + * "out"/"in"/"true" respectively */ +#define PW_KEY_NODE_LINK_GROUP "node.link-group" /**< the node is internally linked to + * nodes with the same link-group */ +#define PW_KEY_NODE_NETWORK "node.network" /**< the node is on a network */ +#define PW_KEY_NODE_TRIGGER "node.trigger" /**< the node is not scheduled automatically + * based on the dependencies in the graph + * but it will be triggered explicitly. */ +#define PW_KEY_NODE_CHANNELNAMES "node.channel-names" /**< names of node's + * channels (unrelated to positions) */ +#define PW_KEY_NODE_DEVICE_PORT_NAME_PREFIX "node.device-port-name-prefix" /** override + * port name prefix for device ports, like capture and playback + * or disable the prefix completely if an empty string is provided */ + +/** Port keys */ +#define PW_KEY_PORT_ID "port.id" /**< port id */ +#define PW_KEY_PORT_NAME "port.name" /**< port name */ +#define PW_KEY_PORT_DIRECTION "port.direction" /**< the port direction, one of "in" or "out" + * or "control" and "notify" for control ports */ +#define PW_KEY_PORT_ALIAS "port.alias" /**< port alias */ +#define PW_KEY_PORT_PHYSICAL "port.physical" /**< if this is a physical port */ +#define PW_KEY_PORT_TERMINAL "port.terminal" /**< if this port consumes the data */ +#define PW_KEY_PORT_CONTROL "port.control" /**< if this port is a control port */ +#define PW_KEY_PORT_MONITOR "port.monitor" /**< if this port is a monitor port */ +#define PW_KEY_PORT_CACHE_PARAMS "port.cache-params" /**< cache the node port params */ +#define PW_KEY_PORT_EXTRA "port.extra" /**< api specific extra port info, API name + * should be prefixed. "jack:flags:56" */ +#define PW_KEY_PORT_PASSIVE "port.passive" /**< the ports wants passive links, since 0.3.67 */ + +/** link properties */ +#define PW_KEY_LINK_ID "link.id" /**< a link id */ +#define PW_KEY_LINK_INPUT_NODE "link.input.node" /**< input node id of a link */ +#define PW_KEY_LINK_INPUT_PORT "link.input.port" /**< input port id of a link */ +#define PW_KEY_LINK_OUTPUT_NODE "link.output.node" /**< output node id of a link */ +#define PW_KEY_LINK_OUTPUT_PORT "link.output.port" /**< output port id of a link */ +#define PW_KEY_LINK_PASSIVE "link.passive" /**< indicate that a link is passive and + * does not cause the graph to be + * runnable. */ +#define PW_KEY_LINK_FEEDBACK "link.feedback" /**< indicate that a link is a feedback + * link and the target will receive data + * in the next cycle */ + +/** device properties */ +#define PW_KEY_DEVICE_ID "device.id" /**< device id */ +#define PW_KEY_DEVICE_NAME "device.name" /**< device name */ +#define PW_KEY_DEVICE_PLUGGED "device.plugged" /**< when the device was created. As a uint64 in + * nanoseconds. */ +#define PW_KEY_DEVICE_NICK "device.nick" /**< a short device nickname */ +#define PW_KEY_DEVICE_STRING "device.string" /**< device string in the underlying layer's + * format. Ex. "surround51:0" */ +#define PW_KEY_DEVICE_API "device.api" /**< API this device is accessed with. + * Ex. "alsa", "v4l2" */ +#define PW_KEY_DEVICE_DESCRIPTION "device.description" /**< localized human readable device one-line + * description. Ex. "Foobar USB Headset" */ +#define PW_KEY_DEVICE_BUS_PATH "device.bus-path" /**< bus path to the device in the OS' + * format. Ex. "pci-0000:00:14.0-usb-0:3.2:1.0" */ +#define PW_KEY_DEVICE_SERIAL "device.serial" /**< Serial number if applicable */ +#define PW_KEY_DEVICE_VENDOR_ID "device.vendor.id" /**< vendor ID if applicable */ +#define PW_KEY_DEVICE_VENDOR_NAME "device.vendor.name" /**< vendor name if applicable */ +#define PW_KEY_DEVICE_PRODUCT_ID "device.product.id" /**< product ID if applicable */ +#define PW_KEY_DEVICE_PRODUCT_NAME "device.product.name" /**< product name if applicable */ +#define PW_KEY_DEVICE_CLASS "device.class" /**< device class */ +#define PW_KEY_DEVICE_FORM_FACTOR "device.form-factor" /**< form factor if applicable. One of + * "internal", "speaker", "handset", "tv", + * "webcam", "microphone", "headset", + * "headphone", "hands-free", "car", "hifi", + * "computer", "portable" */ +#define PW_KEY_DEVICE_BUS "device.bus" /**< bus of the device if applicable. One of + * "isa", "pci", "usb", "firewire", + * "bluetooth" */ +#define PW_KEY_DEVICE_SUBSYSTEM "device.subsystem" /**< device subsystem */ +#define PW_KEY_DEVICE_SYSFS_PATH "device.sysfs.path" /**< device sysfs path */ +#define PW_KEY_DEVICE_ICON "device.icon" /**< icon for the device. A base64 blob + * containing PNG image data */ +#define PW_KEY_DEVICE_ICON_NAME "device.icon-name" /**< an XDG icon name for the device. + * Ex. "sound-card-speakers-usb" */ +#define PW_KEY_DEVICE_INTENDED_ROLES "device.intended-roles" /**< intended use. A space separated list of + * roles (see PW_KEY_MEDIA_ROLE) this device + * is particularly well suited for, due to + * latency, quality or form factor. */ +#define PW_KEY_DEVICE_CACHE_PARAMS "device.cache-params" /**< cache the device spa params */ + +/** module properties */ +#define PW_KEY_MODULE_ID "module.id" /**< the module id */ +#define PW_KEY_MODULE_NAME "module.name" /**< the name of the module */ +#define PW_KEY_MODULE_AUTHOR "module.author" /**< the author's name */ +#define PW_KEY_MODULE_DESCRIPTION "module.description" /**< a human readable one-line description + * of the module's purpose.*/ +#define PW_KEY_MODULE_USAGE "module.usage" /**< a human readable usage description of + * the module's arguments. */ +#define PW_KEY_MODULE_VERSION "module.version" /**< a version string for the module. */ + +/** Factory properties */ +#define PW_KEY_FACTORY_ID "factory.id" /**< the factory id */ +#define PW_KEY_FACTORY_NAME "factory.name" /**< the name of the factory */ +#define PW_KEY_FACTORY_USAGE "factory.usage" /**< the usage of the factory */ +#define PW_KEY_FACTORY_TYPE_NAME "factory.type.name" /**< the name of the type created by a factory */ +#define PW_KEY_FACTORY_TYPE_VERSION "factory.type.version" /**< the version of the type created by a factory */ + +/** Stream properties */ +#define PW_KEY_STREAM_IS_LIVE "stream.is-live" /**< Indicates that the stream is live. */ +#define PW_KEY_STREAM_LATENCY_MIN "stream.latency.min" /**< The minimum latency of the stream. */ +#define PW_KEY_STREAM_LATENCY_MAX "stream.latency.max" /**< The maximum latency of the stream */ +#define PW_KEY_STREAM_MONITOR "stream.monitor" /**< Indicates that the stream is monitoring + * and might select a less accurate but faster + * conversion algorithm. */ +#define PW_KEY_STREAM_DONT_REMIX "stream.dont-remix" /**< don't remix channels */ +#define PW_KEY_STREAM_CAPTURE_SINK "stream.capture.sink" /**< Try to capture the sink output instead of + * source output */ + +/** Media */ +#define PW_KEY_MEDIA_TYPE "media.type" /**< Media type, one of + * Audio, Video, Midi */ +#define PW_KEY_MEDIA_CATEGORY "media.category" /**< Media Category: + * Playback, Capture, Duplex, Monitor, Manager */ +#define PW_KEY_MEDIA_ROLE "media.role" /**< Role: Movie, Music, Camera, + * Screen, Communication, Game, + * Notification, DSP, Production, + * Accessibility, Test */ +#define PW_KEY_MEDIA_CLASS "media.class" /**< class Ex: "Video/Source" */ +#define PW_KEY_MEDIA_NAME "media.name" /**< media name. Ex: "Pink Floyd: Time" */ +#define PW_KEY_MEDIA_TITLE "media.title" /**< title. Ex: "Time" */ +#define PW_KEY_MEDIA_ARTIST "media.artist" /**< artist. Ex: "Pink Floyd" */ +#define PW_KEY_MEDIA_COPYRIGHT "media.copyright" /**< copyright string */ +#define PW_KEY_MEDIA_SOFTWARE "media.software" /**< generator software */ +#define PW_KEY_MEDIA_LANGUAGE "media.language" /**< language in POSIX format. Ex: en_GB */ +#define PW_KEY_MEDIA_FILENAME "media.filename" /**< filename */ +#define PW_KEY_MEDIA_ICON "media.icon" /**< icon for the media, a base64 blob with + * PNG image data */ +#define PW_KEY_MEDIA_ICON_NAME "media.icon-name" /**< an XDG icon name for the media. + * Ex: "audio-x-mp3" */ +#define PW_KEY_MEDIA_COMMENT "media.comment" /**< extra comment */ +#define PW_KEY_MEDIA_DATE "media.date" /**< date of the media */ +#define PW_KEY_MEDIA_FORMAT "media.format" /**< format of the media */ + +/** format related properties */ +#define PW_KEY_FORMAT_DSP "format.dsp" /**< a dsp format. + * Ex: "32 bit float mono audio" */ +/** audio related properties */ +#define PW_KEY_AUDIO_CHANNEL "audio.channel" /**< an audio channel. Ex: "FL" */ +#define PW_KEY_AUDIO_RATE "audio.rate" /**< an audio samplerate */ +#define PW_KEY_AUDIO_CHANNELS "audio.channels" /**< number of audio channels */ +#define PW_KEY_AUDIO_FORMAT "audio.format" /**< an audio format. Ex: "S16LE" */ +#define PW_KEY_AUDIO_ALLOWED_RATES "audio.allowed-rates" /**< a list of allowed samplerates + * ex. "[ 44100 48000 ]" */ + +/** video related properties */ +#define PW_KEY_VIDEO_RATE "video.framerate" /**< a video framerate */ +#define PW_KEY_VIDEO_FORMAT "video.format" /**< a video format */ +#define PW_KEY_VIDEO_SIZE "video.size" /**< a video size as "x +#include + +/** \defgroup pw_loop Loop + * + * PipeWire loop object provides an implementation of + * the spa loop interfaces. It can be used to implement various + * event loops. + */ + +/** + * \addtogroup pw_loop + * \{ + */ + +struct pw_loop { + struct spa_system *system; /**< system utils */ + struct spa_loop *loop; /**< wrapped loop */ + struct spa_loop_control *control; /**< loop control */ + struct spa_loop_utils *utils; /**< loop utils */ +}; + +struct pw_loop * +pw_loop_new(const struct spa_dict *props); + +void +pw_loop_destroy(struct pw_loop *loop); + +#define pw_loop_add_source(l,...) spa_loop_add_source((l)->loop,__VA_ARGS__) +#define pw_loop_update_source(l,...) spa_loop_update_source((l)->loop,__VA_ARGS__) +#define pw_loop_remove_source(l,...) spa_loop_remove_source((l)->loop,__VA_ARGS__) +#define pw_loop_invoke(l,...) spa_loop_invoke((l)->loop,__VA_ARGS__) + +#define pw_loop_get_fd(l) spa_loop_control_get_fd((l)->control) +#define pw_loop_add_hook(l,...) spa_loop_control_add_hook((l)->control,__VA_ARGS__) +#define pw_loop_enter(l) spa_loop_control_enter((l)->control) +#define pw_loop_iterate(l,...) spa_loop_control_iterate((l)->control,__VA_ARGS__) +#define pw_loop_leave(l) spa_loop_control_leave((l)->control) + +#define pw_loop_add_io(l,...) spa_loop_utils_add_io((l)->utils,__VA_ARGS__) +#define pw_loop_update_io(l,...) spa_loop_utils_update_io((l)->utils,__VA_ARGS__) +#define pw_loop_add_idle(l,...) spa_loop_utils_add_idle((l)->utils,__VA_ARGS__) +#define pw_loop_enable_idle(l,...) spa_loop_utils_enable_idle((l)->utils,__VA_ARGS__) +#define pw_loop_add_event(l,...) spa_loop_utils_add_event((l)->utils,__VA_ARGS__) +#define pw_loop_signal_event(l,...) spa_loop_utils_signal_event((l)->utils,__VA_ARGS__) +#define pw_loop_add_timer(l,...) spa_loop_utils_add_timer((l)->utils,__VA_ARGS__) +#define pw_loop_update_timer(l,...) spa_loop_utils_update_timer((l)->utils,__VA_ARGS__) +#define pw_loop_add_signal(l,...) spa_loop_utils_add_signal((l)->utils,__VA_ARGS__) +#define pw_loop_destroy_source(l,...) spa_loop_utils_destroy_source((l)->utils,__VA_ARGS__) + +/** + * \} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_LOOP_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/port.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/port.h new file mode 100644 index 0000000000000..bb58cab97f53b --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/port.h @@ -0,0 +1,159 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_PORT_H +#define PIPEWIRE_PORT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include +#include + +#include + +/** \defgroup pw_port Port + * Port interface + */ + +/** + * \addtogroup pw_port + * \{ + */ + +#define PW_TYPE_INTERFACE_Port PW_TYPE_INFO_INTERFACE_BASE "Port" + +#define PW_VERSION_PORT 3 +struct pw_port; + +/** The direction of a port */ +#define pw_direction spa_direction +#define PW_DIRECTION_INPUT SPA_DIRECTION_INPUT +#define PW_DIRECTION_OUTPUT SPA_DIRECTION_OUTPUT + +/** Convert a \ref pw_direction to a readable string */ +const char * pw_direction_as_string(enum pw_direction direction); + +struct pw_port_info { + uint32_t id; /**< id of the global */ + enum pw_direction direction; /**< port direction */ +#define PW_PORT_CHANGE_MASK_PROPS (1 << 0) +#define PW_PORT_CHANGE_MASK_PARAMS (1 << 1) +#define PW_PORT_CHANGE_MASK_ALL ((1 << 2)-1) + uint64_t change_mask; /**< bitfield of changed fields since last call */ + struct spa_dict *props; /**< the properties of the port */ + struct spa_param_info *params; /**< parameters */ + uint32_t n_params; /**< number of items in \a params */ +}; + +struct pw_port_info * +pw_port_info_update(struct pw_port_info *info, + const struct pw_port_info *update); + +struct pw_port_info * +pw_port_info_merge(struct pw_port_info *info, + const struct pw_port_info *update, bool reset); + +void +pw_port_info_free(struct pw_port_info *info); + +#define PW_PORT_EVENT_INFO 0 +#define PW_PORT_EVENT_PARAM 1 +#define PW_PORT_EVENT_NUM 2 + +/** Port events */ +struct pw_port_events { +#define PW_VERSION_PORT_EVENTS 0 + uint32_t version; + /** + * Notify port info + * + * \param info info about the port + */ + void (*info) (void *data, const struct pw_port_info *info); + /** + * Notify a port param + * + * Event emitted as a result of the enum_params method. + * + * \param seq the sequence number of the request + * \param id the param id + * \param index the param index + * \param next the param index of the next param + * \param param the parameter + */ + void (*param) (void *data, int seq, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param); +}; + +#define PW_PORT_METHOD_ADD_LISTENER 0 +#define PW_PORT_METHOD_SUBSCRIBE_PARAMS 1 +#define PW_PORT_METHOD_ENUM_PARAMS 2 +#define PW_PORT_METHOD_NUM 3 + +/** Port methods */ +struct pw_port_methods { +#define PW_VERSION_PORT_METHODS 0 + uint32_t version; + + int (*add_listener) (void *object, + struct spa_hook *listener, + const struct pw_port_events *events, + void *data); + /** + * Subscribe to parameter changes + * + * Automatically emit param events for the given ids when + * they are changed. + * + * \param ids an array of param ids + * \param n_ids the number of ids in \a ids + */ + int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids); + + /** + * Enumerate port parameters + * + * Start enumeration of port parameters. For each param, a + * param event will be emitted. + * + * \param seq a sequence number returned in the reply + * \param id the parameter id to enumerate + * \param start the start index or 0 for the first param + * \param num the maximum number of params to retrieve + * \param filter a param filter or NULL + */ + int (*enum_params) (void *object, int seq, + uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter); +}; + +#define pw_port_method(o,method,version,...) \ +({ \ + int _res = -ENOTSUP; \ + spa_interface_call_res((struct spa_interface*)o, \ + struct pw_port_methods, _res, \ + method, version, ##__VA_ARGS__); \ + _res; \ +}) + +#define pw_port_add_listener(c,...) pw_port_method(c,add_listener,0,__VA_ARGS__) +#define pw_port_subscribe_params(c,...) pw_port_method(c,subscribe_params,0,__VA_ARGS__) +#define pw_port_enum_params(c,...) pw_port_method(c,enum_params,0,__VA_ARGS__) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* PIPEWIRE_PORT_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/properties.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/properties.h new file mode 100644 index 0000000000000..3966be417bcb7 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/properties.h @@ -0,0 +1,179 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_PROPERTIES_H +#define PIPEWIRE_PROPERTIES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +/** \defgroup pw_properties Properties + * + * Properties are used to pass around arbitrary key/value pairs. + * Both keys and values are strings which keeps things simple. + * Encoding of arbitrary values should be done by using a string + * serialization such as base64 for binary blobs. + */ + +/** + * \addtogroup pw_properties + * \{ + */ +struct pw_properties { + struct spa_dict dict; /**< dictionary of key/values */ + uint32_t flags; /**< extra flags */ +}; + +struct pw_properties * +pw_properties_new(const char *key, ...) SPA_SENTINEL; + +struct pw_properties * +pw_properties_new_dict(const struct spa_dict *dict); + +struct pw_properties * +pw_properties_new_string(const char *args); + +struct pw_properties * +pw_properties_copy(const struct pw_properties *properties); + +int pw_properties_update_keys(struct pw_properties *props, + const struct spa_dict *dict, const char * const keys[]); +int pw_properties_update_ignore(struct pw_properties *props, + const struct spa_dict *dict, const char * const ignore[]); + +/* Update props with all key/value pairs from dict */ +int pw_properties_update(struct pw_properties *props, + const struct spa_dict *dict); +/* Update props with all key/value pairs from str */ +int pw_properties_update_string(struct pw_properties *props, + const char *str, size_t size); + +int pw_properties_add(struct pw_properties *oldprops, + const struct spa_dict *dict); +int pw_properties_add_keys(struct pw_properties *oldprops, + const struct spa_dict *dict, const char * const keys[]); + +void pw_properties_clear(struct pw_properties *properties); + +void +pw_properties_free(struct pw_properties *properties); + +int +pw_properties_set(struct pw_properties *properties, const char *key, const char *value); + +int +pw_properties_setf(struct pw_properties *properties, + const char *key, const char *format, ...) SPA_PRINTF_FUNC(3, 4); +int +pw_properties_setva(struct pw_properties *properties, + const char *key, const char *format, va_list args) SPA_PRINTF_FUNC(3,0); +const char * +pw_properties_get(const struct pw_properties *properties, const char *key); + +int +pw_properties_fetch_uint32(const struct pw_properties *properties, const char *key, uint32_t *value); + +int +pw_properties_fetch_int32(const struct pw_properties *properties, const char *key, int32_t *value); + +int +pw_properties_fetch_uint64(const struct pw_properties *properties, const char *key, uint64_t *value); + +int +pw_properties_fetch_int64(const struct pw_properties *properties, const char *key, int64_t *value); + +int +pw_properties_fetch_bool(const struct pw_properties *properties, const char *key, bool *value); + +static inline uint32_t +pw_properties_get_uint32(const struct pw_properties *properties, const char *key, uint32_t deflt) +{ + uint32_t val = deflt; + pw_properties_fetch_uint32(properties, key, &val); + return val; +} + +static inline int32_t +pw_properties_get_int32(const struct pw_properties *properties, const char *key, int32_t deflt) +{ + int32_t val = deflt; + pw_properties_fetch_int32(properties, key, &val); + return val; +} + +static inline uint64_t +pw_properties_get_uint64(const struct pw_properties *properties, const char *key, uint64_t deflt) +{ + uint64_t val = deflt; + pw_properties_fetch_uint64(properties, key, &val); + return val; +} + +static inline int64_t +pw_properties_get_int64(const struct pw_properties *properties, const char *key, int64_t deflt) +{ + int64_t val = deflt; + pw_properties_fetch_int64(properties, key, &val); + return val; +} + + +static inline bool +pw_properties_get_bool(const struct pw_properties *properties, const char *key, bool deflt) +{ + bool val = deflt; + pw_properties_fetch_bool(properties, key, &val); + return val; +} + +const char * +pw_properties_iterate(const struct pw_properties *properties, void **state); + +#define PW_PROPERTIES_FLAG_NL (1<<0) +int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags); + +static inline bool pw_properties_parse_bool(const char *value) { + return spa_atob(value); +} + +static inline int pw_properties_parse_int(const char *value) { + int v; + return spa_atoi32(value, &v, 0) ? v: 0; +} + +static inline int64_t pw_properties_parse_int64(const char *value) { + int64_t v; + return spa_atoi64(value, &v, 0) ? v : 0; +} + +static inline uint64_t pw_properties_parse_uint64(const char *value) { + uint64_t v; + return spa_atou64(value, &v, 0) ? v : 0; +} + +static inline float pw_properties_parse_float(const char *value) { + float v; + return spa_atof(value, &v) ? v : 0.0f; +} + +static inline double pw_properties_parse_double(const char *value) { + double v; + return spa_atod(value, &v) ? v : 0.0; +} + +/** + * \} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_PROPERTIES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/protocol.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/protocol.h new file mode 100644 index 0000000000000..f119a31b5db46 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/protocol.h @@ -0,0 +1,142 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_PROTOCOL_H +#define PIPEWIRE_PROTOCOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** \defgroup pw_protocol Protocol + * + * \brief Manages protocols and their implementation + */ + +/** + * \addtogroup pw_protocol + * \{ + */ + +struct pw_protocol; + +#include +#include +#include + +#define PW_TYPE_INFO_Protocol "PipeWire:Protocol" +#define PW_TYPE_INFO_PROTOCOL_BASE PW_TYPE_INFO_Protocol ":" + +struct pw_protocol_client { + struct spa_list link; /**< link in protocol client_list */ + struct pw_protocol *protocol; /**< the owner protocol */ + + struct pw_core *core; + + int (*connect) (struct pw_protocol_client *client, + const struct spa_dict *props, + void (*done_callback) (void *data, int result), + void *data); + int (*connect_fd) (struct pw_protocol_client *client, int fd, bool close); + int (*steal_fd) (struct pw_protocol_client *client); + void (*disconnect) (struct pw_protocol_client *client); + void (*destroy) (struct pw_protocol_client *client); + int (*set_paused) (struct pw_protocol_client *client, bool paused); +}; + +#define pw_protocol_client_connect(c,p,cb,d) ((c)->connect(c,p,cb,d)) +#define pw_protocol_client_connect_fd(c,fd,cl) ((c)->connect_fd(c,fd,cl)) +#define pw_protocol_client_steal_fd(c) ((c)->steal_fd(c)) +#define pw_protocol_client_disconnect(c) ((c)->disconnect(c)) +#define pw_protocol_client_destroy(c) ((c)->destroy(c)) +#define pw_protocol_client_set_paused(c,p) ((c)->set_paused(c,p)) + +struct pw_protocol_server { + struct spa_list link; /**< link in protocol server_list */ + struct pw_protocol *protocol; /**< the owner protocol */ + + struct pw_impl_core *core; + + struct spa_list client_list; /**< list of clients of this protocol */ + + void (*destroy) (struct pw_protocol_server *listen); +}; + +#define pw_protocol_server_destroy(l) ((l)->destroy(l)) + +struct pw_protocol_marshal { + const char *type; /**< interface type */ + uint32_t version; /**< version */ +#define PW_PROTOCOL_MARSHAL_FLAG_IMPL (1 << 0) /**< marshal for implementations */ + uint32_t flags; /**< version */ + uint32_t n_client_methods; /**< number of client methods */ + uint32_t n_server_methods; /**< number of server methods */ + const void *client_marshal; + const void *server_demarshal; + const void *server_marshal; + const void *client_demarshal; +}; + +struct pw_protocol_implementation { +#define PW_VERSION_PROTOCOL_IMPLEMENTATION 0 + uint32_t version; + + struct pw_protocol_client * (*new_client) (struct pw_protocol *protocol, + struct pw_core *core, + const struct spa_dict *props); + struct pw_protocol_server * (*add_server) (struct pw_protocol *protocol, + struct pw_impl_core *core, + const struct spa_dict *props); +}; + +struct pw_protocol_events { +#define PW_VERSION_PROTOCOL_EVENTS 0 + uint32_t version; + + void (*destroy) (void *data); +}; + +#define pw_protocol_new_client(p,...) (pw_protocol_get_implementation(p)->new_client(p,__VA_ARGS__)) +#define pw_protocol_add_server(p,...) (pw_protocol_get_implementation(p)->add_server(p,__VA_ARGS__)) +#define pw_protocol_ext(p,type,method,...) (((type*)pw_protocol_get_extension(p))->method( __VA_ARGS__)) + +struct pw_protocol *pw_protocol_new(struct pw_context *context, const char *name, size_t user_data_size); + +void pw_protocol_destroy(struct pw_protocol *protocol); + +struct pw_context *pw_protocol_get_context(struct pw_protocol *protocol); + +void *pw_protocol_get_user_data(struct pw_protocol *protocol); + +const struct pw_protocol_implementation * +pw_protocol_get_implementation(struct pw_protocol *protocol); + +const void * +pw_protocol_get_extension(struct pw_protocol *protocol); + + +void pw_protocol_add_listener(struct pw_protocol *protocol, + struct spa_hook *listener, + const struct pw_protocol_events *events, + void *data); + +int pw_protocol_add_marshal(struct pw_protocol *protocol, + const struct pw_protocol_marshal *marshal); + +const struct pw_protocol_marshal * +pw_protocol_get_marshal(struct pw_protocol *protocol, const char *type, uint32_t version, uint32_t flags); + +struct pw_protocol * pw_context_find_protocol(struct pw_context *context, const char *name); + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* PIPEWIRE_PROTOCOL_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/proxy.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/proxy.h new file mode 100644 index 0000000000000..ecd8038a2c0f6 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/proxy.h @@ -0,0 +1,201 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_PROXY_H +#define PIPEWIRE_PROXY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** \page page_proxy Proxy + * + * \section sec_page_proxy_overview Overview + * + * The proxy object is a client side representation of a resource + * that lives on a remote PipeWire instance. + * + * It is used to communicate with the remote object. + * + * \section sec_page_proxy_core Core proxy + * + * A proxy for a remote core object can be obtained by making + * a remote connection with \ref pw_context_connect. + * See \ref pw_proxy + * + * Some methods on proxy object allow creation of more proxy objects or + * create a binding between a local proxy and global resource. + * + * \section sec_page_proxy_create Create + * + * A client first creates a new proxy object with pw_proxy_new(). A + * type must be provided for this object. + * + * The protocol of the context will usually install an interface to + * translate method calls and events to the wire format. + * + * The creator of the proxy will usually also install an event + * implementation of the particular object type. + * + * \section sec_page_proxy_bind Bind + * + * To actually use the proxy object, one needs to create a server + * side resource for it. This can be done by, for example, binding + * to a global object or by calling a method that creates and binds + * to a new remote object. In all cases, the local id is passed to + * the server and is used to create a resource with the same id. + * + * \section sec_page_proxy_methods Methods + * + * To call a method on the proxy use the interface methods. Calling + * any interface method will result in a request to the server to + * perform the requested action on the corresponding resource. + * + * \section sec_page_proxy_events Events + * + * Events send from the server to the proxy will be demarshalled by + * the protocol and will then result in a call to the installed + * implementation of the proxy. + * + * \section sec_page_proxy_destroy Destroy + * + * Use pw_proxy_destroy() to destroy the client side object. This + * is usually done automatically when the server removes the resource + * associated to the proxy. + */ + +/** \defgroup pw_proxy Proxy + * + * \brief Represents an object on the client side. + * + * A pw_proxy acts as a client side proxy to an object existing in a remote + * pipewire instance. The proxy is responsible for converting interface functions + * invoked by the client to PipeWire messages. Events will call the handlers + * set in listener. + * + * See \ref page_proxy + */ + +/** + * \addtogroup pw_proxy + * \{ + */ +struct pw_proxy; + +#include + +/** Proxy events, use \ref pw_proxy_add_listener */ +struct pw_proxy_events { +#define PW_VERSION_PROXY_EVENTS 1 + uint32_t version; + + /** The proxy is destroyed */ + void (*destroy) (void *data); + + /** a proxy is bound to a global id */ + void (*bound) (void *data, uint32_t global_id); + + /** a proxy is removed from the server. Use pw_proxy_destroy to + * free the proxy. */ + void (*removed) (void *data); + + /** a reply to a sync method completed */ + void (*done) (void *data, int seq); + + /** an error occurred on the proxy */ + void (*error) (void *data, int seq, int res, const char *message); + + void (*bound_props) (void *data, uint32_t global_id, const struct spa_dict *props); +}; + +/* Make a new proxy object. The id can be used to bind to a remote object and + * can be retrieved with \ref pw_proxy_get_id . */ +struct pw_proxy * +pw_proxy_new(struct pw_proxy *factory, + const char *type, /* interface type */ + uint32_t version, /* interface version */ + size_t user_data_size /* size of user data */); + +/** Add an event listener to proxy */ +void pw_proxy_add_listener(struct pw_proxy *proxy, + struct spa_hook *listener, + const struct pw_proxy_events *events, + void *data); + +/** Add a listener for the events received from the remote object. The + * events depend on the type of the remote object type. */ +void pw_proxy_add_object_listener(struct pw_proxy *proxy, /**< the proxy */ + struct spa_hook *listener, /**< listener */ + const void *funcs, /**< proxied functions */ + void *data /**< data passed to events */); + +/** destroy a proxy */ +void pw_proxy_destroy(struct pw_proxy *proxy); + +void pw_proxy_ref(struct pw_proxy *proxy); +void pw_proxy_unref(struct pw_proxy *proxy); + +/** Get the user_data. The size was given in \ref pw_proxy_new */ +void *pw_proxy_get_user_data(struct pw_proxy *proxy); + +/** Get the local id of the proxy */ +uint32_t pw_proxy_get_id(struct pw_proxy *proxy); + +/** Get the type and version of the proxy */ +const char *pw_proxy_get_type(struct pw_proxy *proxy, uint32_t *version); + +/** Get the protocol used for the proxy */ +struct pw_protocol *pw_proxy_get_protocol(struct pw_proxy *proxy); + +/** Generate an sync method for a proxy. This will generate a done event + * with the same seq number of the reply. */ +int pw_proxy_sync(struct pw_proxy *proxy, int seq); + +/** Set the global id this proxy is bound to. This is usually used internally + * and will also emit the bound event */ +int pw_proxy_set_bound_id(struct pw_proxy *proxy, uint32_t global_id); +/** Get the global id bound to this proxy of SPA_ID_INVALID when not bound + * to a global */ +uint32_t pw_proxy_get_bound_id(struct pw_proxy *proxy); + +/** Generate an error for a proxy */ +int pw_proxy_error(struct pw_proxy *proxy, int res, const char *error); +int pw_proxy_errorf(struct pw_proxy *proxy, int res, const char *error, ...) SPA_PRINTF_FUNC(3, 4); + +/** Get the listener of proxy */ +struct spa_hook_list *pw_proxy_get_object_listeners(struct pw_proxy *proxy); + +/** Get the marshal functions for the proxy */ +const struct pw_protocol_marshal *pw_proxy_get_marshal(struct pw_proxy *proxy); + +/** Install a marshal function on a proxy */ +int pw_proxy_install_marshal(struct pw_proxy *proxy, bool implementor); + +#define pw_proxy_notify(p,type,event,version,...) \ + spa_hook_list_call(pw_proxy_get_object_listeners(p), \ + type, event, version, ## __VA_ARGS__) + +#define pw_proxy_call(p,type,method,version,...) \ + spa_interface_call((struct spa_interface*)p, \ + type, method, version, ##__VA_ARGS__) + +#define pw_proxy_call_res(p,type,method,version,...) \ +({ \ + int _res = -ENOTSUP; \ + spa_interface_call_res((struct spa_interface*)p, \ + type, _res, method, version, ##__VA_ARGS__); \ + _res; \ +}) + +/** + * \} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_PROXY_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/stream.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/stream.h new file mode 100644 index 0000000000000..90096056a0c57 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/stream.h @@ -0,0 +1,509 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_STREAM_H +#define PIPEWIRE_STREAM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \page page_streams Streams + * + * \section sec_overview Overview + * + * \ref pw_stream "Streams" are used to exchange data with the + * PipeWire server. A stream is a wrapper around a proxy for a pw_client_node + * with an adapter. This means the stream will automatically do conversion + * to the type required by the server. + * + * Streams can be used to: + * + * \li Consume a stream from PipeWire. This is a PW_DIRECTION_INPUT stream. + * \li Produce a stream to PipeWire. This is a PW_DIRECTION_OUTPUT stream + * + * You can connect the stream port to a specific server port or let PipeWire + * choose a port for you. + * + * For more complicated nodes such as filters or ports with multiple + * inputs and/or outputs you will need to use the pw_filter or make + * a pw_node yourself and export it with \ref pw_core_export. + * + * Streams can also be used to: + * + * \li Implement a Sink in PipeWire. This is a PW_DIRECTION_INPUT stream. + * \li Implement a Source in PipeWire. This is a PW_DIRECTION_OUTPUT stream + * + * In this case, the PW_KEY_MEDIA_CLASS property needs to be set to + * "Audio/Sink" or "Audio/Source" respectively. + * + * \section sec_create Create + * + * Make a new stream with \ref pw_stream_new(). You will need to specify + * a name for the stream and extra properties. The basic set of properties + * each stream must provide is filled in automatically. + * + * Once the stream is created, the state_changed event should be used to + * track the state of the stream. + * + * \section sec_connect Connect + * + * The stream is initially unconnected. To connect the stream, use + * \ref pw_stream_connect(). Pass the desired direction as an argument. + * + * The direction is: + + * \li PW_DIRECTION_INPUT for a stream that *consumes* data. This can be a + * stream that captures from a Source or a when the stream is used to + * implement a Sink. + * + * \li PW_DIRECTION_OUTPUT for a stream that *produces* data. This can be a + * stream that plays to a Sink or when the stream is used to implement + * a Source. + * + * \subsection ssec_stream_target Stream target + * + * To make the newly connected stream automatically connect to an existing + * PipeWire node, use the \ref PW_STREAM_FLAG_AUTOCONNECT and set the + * PW_KEY_OBJECT_SERIAL or the PW_KEY_NODE_NAME value of the target node + * in the PW_KEY_TARGET_OBJECT property before connecting. + * + * \subsection ssec_stream_formats Stream formats + * + * An array of possible formats that this stream can consume or provide + * must be specified. + * + * \section sec_format Format negotiation + * + * After connecting the stream, the server will want to configure some + * parameters on the stream. You will be notified of these changes + * with the param_changed event. + * + * When a format param change is emitted, the client should now prepare + * itself to deal with the format and complete the negotiation procedure + * with a call to \ref pw_stream_update_params(). + * + * As arguments to \ref pw_stream_update_params() an array of spa_param + * structures must be given. They contain parameters such as buffer size, + * number of buffers, required metadata and other parameters for the + * media buffers. + * + * \section sec_buffers Buffer negotiation + * + * After completing the format negotiation, PipeWire will allocate and + * notify the stream of the buffers that will be used to exchange data + * between client and server. + * + * With the add_buffer event, a stream will be notified of a new buffer + * that can be used for data transport. You can attach user_data to these + * buffers. The buffers can only be used with the stream that emitted + * the add_buffer event. + * + * After the buffers are negotiated, the stream will transition to the + * \ref PW_STREAM_STATE_PAUSED state. + * + * \section sec_streaming Streaming + * + * From the \ref PW_STREAM_STATE_PAUSED state, the stream can be set to + * the \ref PW_STREAM_STATE_STREAMING state by the PipeWire server when + * data transport is started. + * + * Depending on how the stream was connected it will need to Produce or + * Consume data for/from PipeWire as explained in the following + * subsections. + * + * \subsection ssec_consume Consume data + * + * The process event is emitted for each new buffer that can be + * consumed. + * + * \ref pw_stream_dequeue_buffer() should be used to get the data and + * metadata of the buffer. + * + * The buffer is owned by the stream and stays alive until the + * remove_buffer callback has returned or the stream is destroyed. + * + * When the buffer has been processed, call \ref pw_stream_queue_buffer() + * to let PipeWire reuse the buffer. + * + * \subsection ssec_produce Produce data + * + * \ref pw_stream_dequeue_buffer() gives an empty buffer that can be filled. + * + * The buffer is owned by the stream and stays alive until the + * remove_buffer event is emitted or the stream is destroyed. + * + * Filled buffers should be queued with \ref pw_stream_queue_buffer(). + * + * The process event is emitted when PipeWire has emptied a buffer that + * can now be refilled. + * + * \section sec_stream_disconnect Disconnect + * + * Use \ref pw_stream_disconnect() to disconnect a stream after use. + * + * \section sec_stream_configuration Configuration + * + * \subsection ssec_config_properties Stream Properties + * + * \subsection ssec_config_rules Stream Rules + * + * \section sec_stream_environment Environment Variables + * + */ +/** \defgroup pw_stream Stream + * + * \brief PipeWire stream objects + * + * The stream object provides a convenient way to send and + * receive data streams from/to PipeWire. + * + * See also \ref page_streams and \ref api_pw_core + */ + +/** + * \addtogroup pw_stream + * \{ + */ +struct pw_stream; + +#include +#include +#include + +/** \enum pw_stream_state The state of a stream */ +enum pw_stream_state { + PW_STREAM_STATE_ERROR = -1, /**< the stream is in error */ + PW_STREAM_STATE_UNCONNECTED = 0, /**< unconnected */ + PW_STREAM_STATE_CONNECTING = 1, /**< connection is in progress */ + PW_STREAM_STATE_PAUSED = 2, /**< paused */ + PW_STREAM_STATE_STREAMING = 3 /**< streaming */ +}; + +/** a buffer structure obtained from pw_stream_dequeue_buffer(). The size of this + * structure can grow as more field are added in the future */ +struct pw_buffer { + struct spa_buffer *buffer; /**< the spa buffer */ + void *user_data; /**< user data attached to the buffer */ + uint64_t size; /**< This field is set by the user and the sum of + * all queued buffer is returned in the time info. + * For audio, it is advised to use the number of + * samples in the buffer for this field. */ + uint64_t requested; /**< For playback streams, this field contains the + * suggested amount of data to provide. For audio + * streams this will be the amount of samples + * required by the resampler. This field is 0 + * when no suggestion is provided. Since 0.3.49 */ +}; + +struct pw_stream_control { + const char *name; /**< name of the control */ + uint32_t flags; /**< extra flags (unused) */ + float def; /**< default value */ + float min; /**< min value */ + float max; /**< max value */ + float *values; /**< array of values */ + uint32_t n_values; /**< number of values in array */ + uint32_t max_values; /**< max values that can be set on this control */ +}; + +/** A time structure. + * + * Use pw_stream_get_time_n() to get an updated time snapshot of the stream. + * The time snapshot can give information about the time in the driver of the + * graph, the delay to the edge of the graph and the internal queuing in the + * stream. + * + * pw_time.ticks gives a monotonic increasing counter of the time in the graph + * driver. I can be used to generate a timetime to schedule samples as well + * as detect discontinuities in the timeline caused by xruns. + * + * pw_time.delay is expressed as pw_time.rate, the time domain of the graph. This + * value, and pw_time.ticks, were captured at pw_time.now and can be extrapolated + * to the current time like this: + * + * struct timespec ts; + * clock_gettime(CLOCK_MONOTONIC, &ts); + * int64_t diff = SPA_TIMESPEC_TO_NSEC(&ts) - pw_time.now; + * int64_t elapsed = (pw_time.rate.denom * diff) / (pw_time.rate.num * SPA_NSEC_PER_SEC); + * + * pw_time.delay contains the total delay that a signal will travel through the + * graph. This includes the delay caused by filters in the graph as well as delays + * caused by the hardware. The delay is usually quite stable and should only change when + * the topology, quantum or samplerate of the graph changes. + * + * pw_time.queued and pw_time.buffered is expressed in the time domain of the stream, + * or the format that is used for the buffers of this stream. + * + * pw_time.queued is the sum of all the pw_buffer.size fields of the buffers that are + * currently queued in the stream but not yet processed. The application can choose + * the units of this value, for example, time, samples or bytes (below expressed + * as app.rate). + * + * pw_time.buffered is format dependent, for audio/raw it contains the number of samples + * that are buffered inside the resampler/converter. + * + * The total delay of data in a stream is the sum of the queued and buffered data + * (not yet processed data) and the delay to the edge of the graph, usually a + * playback or capture device. + * + * For an audio playback stream, if you were to queue a buffer, the total delay + * in milliseconds for the first sample in the newly queued buffer to be played + * by the hardware can be calculated as: + * + * (pw_time.buffered * 1000 / stream.samplerate) + + * (pw_time.queued * 1000 / app.rate) + + * ((pw_time.delay - elapsed) * 1000 * pw_time.rate.num / pw_time.rate.denom) + * + * The current extrapolated time (in ms) in the source or sink can be calculated as: + * + * (pw_time.ticks + elapsed) * 1000 * pw_time.rate.num / pw_time.rate.denom + * + * + * stream time domain graph time domain + * /-----------------------\/-----------------------------\ + * + * queue +-+ +-+ +-----------+ +--------+ + * ----> | | | |->| converter | -> graph -> | kernel | -> speaker + * <---- +-+ +-+ +-----------+ +--------+ + * dequeue buffers \-------------------/\--------/ + * graph internal + * latency latency + * \--------/\-------------/\-----------------------------/ + * queued buffered delay + */ +struct pw_time { + int64_t now; /**< the monotonic time in nanoseconds. This is the time + * when this time report was updated. It is usually + * updated every graph cycle. You can use the current + * monotonic time to calculate the elapsed time between + * this report and the current state and calculate + * updated ticks and delay values. */ + struct spa_fraction rate; /**< the rate of \a ticks and delay. This is usually + * expressed in 1/. */ + uint64_t ticks; /**< the ticks at \a now. This is the current time that + * the remote end is reading/writing. This is monotonicaly + * increasing. */ + int64_t delay; /**< delay to device. This is the time it will take for + * the next output sample of the stream to be presented by + * the playback device or the time a sample traveled + * from the capture device. This delay includes the + * delay introduced by all filters on the path between + * the stream and the device. The delay is normally + * constant in a graph and can change when the topology + * of the graph or the quantum changes. This delay does + * not include the delay caused by queued buffers. */ + uint64_t queued; /**< data queued in the stream, this is the sum + * of the size fields in the pw_buffer that are + * currently queued */ + uint64_t buffered; /**< for audio/raw streams, this contains the extra + * number of samples buffered in the resampler. + * Since 0.3.50. */ + uint32_t queued_buffers; /**< The number of buffers that are queued. Since 0.3.50 */ + uint32_t avail_buffers; /**< The number of buffers that can be dequeued. Since 0.3.50 */ +}; + +#include + +/** Events for a stream. These events are always called from the mainloop + * unless explicitly documented otherwise. */ +struct pw_stream_events { +#define PW_VERSION_STREAM_EVENTS 2 + uint32_t version; + + void (*destroy) (void *data); + /** when the stream state changes */ + void (*state_changed) (void *data, enum pw_stream_state old, + enum pw_stream_state state, const char *error); + + /** Notify information about a control. */ + void (*control_info) (void *data, uint32_t id, const struct pw_stream_control *control); + + /** when io changed on the stream. */ + void (*io_changed) (void *data, uint32_t id, void *area, uint32_t size); + /** when a parameter changed */ + void (*param_changed) (void *data, uint32_t id, const struct spa_pod *param); + + /** when a new buffer was created for this stream */ + void (*add_buffer) (void *data, struct pw_buffer *buffer); + /** when a buffer was destroyed for this stream */ + void (*remove_buffer) (void *data, struct pw_buffer *buffer); + + /** when a buffer can be queued (for playback streams) or + * dequeued (for capture streams). This is normally called from the + * mainloop but can also be called directly from the realtime data + * thread if the user is prepared to deal with this. */ + void (*process) (void *data); + + /** The stream is drained */ + void (*drained) (void *data); + + /** A command notify, Since 0.3.39:1 */ + void (*command) (void *data, const struct spa_command *command); + + /** a trigger_process completed. Since version 0.3.40:2 */ + void (*trigger_done) (void *data); +}; + +/** Convert a stream state to a readable string */ +const char * pw_stream_state_as_string(enum pw_stream_state state); + +/** \enum pw_stream_flags Extra flags that can be used in \ref pw_stream_connect() */ +enum pw_stream_flags { + PW_STREAM_FLAG_NONE = 0, /**< no flags */ + PW_STREAM_FLAG_AUTOCONNECT = (1 << 0), /**< try to automatically connect + * this stream */ + PW_STREAM_FLAG_INACTIVE = (1 << 1), /**< start the stream inactive, + * pw_stream_set_active() needs to be + * called explicitly */ + PW_STREAM_FLAG_MAP_BUFFERS = (1 << 2), /**< mmap the buffers except DmaBuf */ + PW_STREAM_FLAG_DRIVER = (1 << 3), /**< be a driver */ + PW_STREAM_FLAG_RT_PROCESS = (1 << 4), /**< call process from the realtime + * thread. You MUST use RT safe functions + * in the process callback. */ + PW_STREAM_FLAG_NO_CONVERT = (1 << 5), /**< don't convert format */ + PW_STREAM_FLAG_EXCLUSIVE = (1 << 6), /**< require exclusive access to the + * device */ + PW_STREAM_FLAG_DONT_RECONNECT = (1 << 7), /**< don't try to reconnect this stream + * when the sink/source is removed */ + PW_STREAM_FLAG_ALLOC_BUFFERS = (1 << 8), /**< the application will allocate buffer + * memory. In the add_buffer event, the + * data of the buffer should be set */ + PW_STREAM_FLAG_TRIGGER = (1 << 9), /**< the output stream will not be scheduled + * automatically but _trigger_process() + * needs to be called. This can be used + * when the output of the stream depends + * on input from other streams. */ +}; + +/** Create a new unconneced \ref pw_stream + * \return a newly allocated \ref pw_stream */ +struct pw_stream * +pw_stream_new(struct pw_core *core, /**< a \ref pw_core */ + const char *name, /**< a stream media name */ + struct pw_properties *props /**< stream properties, ownership is taken */); + +struct pw_stream * +pw_stream_new_simple(struct pw_loop *loop, /**< a \ref pw_loop to use */ + const char *name, /**< a stream media name */ + struct pw_properties *props,/**< stream properties, ownership is taken */ + const struct pw_stream_events *events, /**< stream events */ + void *data /**< data passed to events */); + +/** Destroy a stream */ +void pw_stream_destroy(struct pw_stream *stream); + +void pw_stream_add_listener(struct pw_stream *stream, + struct spa_hook *listener, + const struct pw_stream_events *events, + void *data); + +enum pw_stream_state pw_stream_get_state(struct pw_stream *stream, const char **error); + +const char *pw_stream_get_name(struct pw_stream *stream); + +struct pw_core *pw_stream_get_core(struct pw_stream *stream); + +const struct pw_properties *pw_stream_get_properties(struct pw_stream *stream); + +int pw_stream_update_properties(struct pw_stream *stream, const struct spa_dict *dict); + +/** Connect a stream for input or output on \a port_path. + * \return 0 on success < 0 on error. + * + * You should connect to the process event and use pw_stream_dequeue_buffer() + * to get the latest metadata and data. */ +int +pw_stream_connect(struct pw_stream *stream, /**< a \ref pw_stream */ + enum pw_direction direction, /**< the stream direction */ + uint32_t target_id, /**< should have the value PW_ID_ANY. + * To select a specific target + * node, specify the + * PW_KEY_OBJECT_SERIAL or the + * PW_KEY_NODE_NAME value of the target + * node in the PW_KEY_TARGET_OBJECT + * property of the stream. + * Specifying target nodes by + * their id is deprecated. + */ + enum pw_stream_flags flags, /**< stream flags */ + const struct spa_pod **params, /**< an array with params. The params + * should ideally contain supported + * formats. */ + uint32_t n_params /**< number of items in \a params */); + +/** Get the node ID of the stream. + * \return node ID. */ +uint32_t +pw_stream_get_node_id(struct pw_stream *stream); + +/** Disconnect \a stream */ +int pw_stream_disconnect(struct pw_stream *stream); + +/** Set the stream in error state */ +int pw_stream_set_error(struct pw_stream *stream, /**< a \ref pw_stream */ + int res, /**< a result code */ + const char *error, /**< an error message */ + ...) SPA_PRINTF_FUNC(3, 4); + +/** Complete the negotiation process with result code \a res + * + * This function should be called after notification of the format. + + * When \a res indicates success, \a params contain the parameters for the + * allocation state. */ +int +pw_stream_update_params(struct pw_stream *stream, /**< a \ref pw_stream */ + const struct spa_pod **params, /**< an array of params. The params should + * ideally contain parameters for doing + * buffer allocation. */ + uint32_t n_params /**< number of elements in \a params */); + +/** Get control values */ +const struct pw_stream_control *pw_stream_get_control(struct pw_stream *stream, uint32_t id); + +/** Set control values */ +int pw_stream_set_control(struct pw_stream *stream, uint32_t id, uint32_t n_values, float *values, ...); + +/** Query the time on the stream */ +int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time *time, size_t size); + +/** Query the time on the stream, deprecated since 0.3.50, + * use pw_stream_get_time_n() to get the fields added since 0.3.50. */ +SPA_DEPRECATED +int pw_stream_get_time(struct pw_stream *stream, struct pw_time *time); + +/** Get a buffer that can be filled for playback streams or consumed + * for capture streams. */ +struct pw_buffer *pw_stream_dequeue_buffer(struct pw_stream *stream); + +/** Submit a buffer for playback or recycle a buffer for capture. */ +int pw_stream_queue_buffer(struct pw_stream *stream, struct pw_buffer *buffer); + +/** Activate or deactivate the stream */ +int pw_stream_set_active(struct pw_stream *stream, bool active); + +/** Flush a stream. When \a drain is true, the drained callback will + * be called when all data is played or recorded */ +int pw_stream_flush(struct pw_stream *stream, bool drain); + +/** Check if the stream is driving. The stream needs to have the + * PW_STREAM_FLAG_DRIVER set. When the stream is driving, + * pw_stream_trigger_process() needs to be called when data is + * available (output) or needed (input). Since 0.3.34 */ +bool pw_stream_is_driving(struct pw_stream *stream); + +/** Trigger a push/pull on the stream. One iteration of the graph will + * scheduled and process() will be called. Since 0.3.34 */ +int pw_stream_trigger_process(struct pw_stream *stream); + +/** + * \} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_STREAM_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/pipewire/utils.h b/src/java.desktop/unix/native/libpipewire/include/pipewire/utils.h new file mode 100644 index 0000000000000..19110db81a4a8 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/pipewire/utils.h @@ -0,0 +1,99 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_UTILS_H +#define PIPEWIRE_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#ifndef _POSIX_C_SOURCE +# include +#endif + +#ifndef ENODATA +#define ENODATA 9919 +#endif + +#include +#include + +/** \defgroup pw_utils Utilities + * + * Various utility functions + */ + +/** + * \addtogroup pw_utils + * \{ + */ + +/** a function to destroy an item */ +typedef void (*pw_destroy_t) (void *object); + +const char * +pw_split_walk(const char *str, const char *delimiter, size_t *len, const char **state); + +char ** +pw_split_strv(const char *str, const char *delimiter, int max_tokens, int *n_tokens); + +int +pw_split_ip(char *str, const char *delimiter, int max_tokens, char *tokens[]); + +void +pw_free_strv(char **str); + +char * +pw_strip(char *str, const char *whitespace); + +#if !defined(strndupa) +# define strndupa(s, n) \ + ({ \ + const char *__old = (s); \ + size_t __len = strnlen(__old, (n)); \ + char *__new = (char *) __builtin_alloca(__len + 1); \ + memcpy(__new, __old, __len); \ + __new[__len] = '\0'; \ + __new; \ + }) +#endif + +#if !defined(strdupa) +# define strdupa(s) \ + ({ \ + const char *__old = (s); \ + size_t __len = strlen(__old) + 1; \ + char *__new = (char *) alloca(__len); \ + (char *) memcpy(__new, __old, __len); \ + }) +#endif + +SPA_WARN_UNUSED_RESULT +ssize_t pw_getrandom(void *buf, size_t buflen, unsigned int flags); + +void pw_random(void *buf, size_t buflen); + +#define pw_rand32() ({ uint32_t val; pw_random(&val, sizeof(val)); val; }) + +void* pw_reallocarray(void *ptr, size_t nmemb, size_t size); + +#ifdef PW_ENABLE_DEPRECATED +#define PW_DEPRECATED(v) (v) +#else +#define PW_DEPRECATED(v) ({ __typeof__(v) _v SPA_DEPRECATED = (v); (void)_v; (v); }) +#endif /* PW_ENABLE_DEPRECATED */ + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* PIPEWIRE_UTILS_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/buffer/buffer.h b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/buffer.h new file mode 100644 index 0000000000000..a5e2ac7ffff40 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/buffer.h @@ -0,0 +1,112 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_BUFFER_H +#define SPA_BUFFER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** \defgroup spa_buffer Buffers + * + * Buffers describe the data and metadata that is exchanged between + * ports of a node. + */ + +/** + * \addtogroup spa_buffer + * \{ + */ + +enum spa_data_type { + SPA_DATA_Invalid, + SPA_DATA_MemPtr, /**< pointer to memory, the data field in + * struct spa_data is set. */ + SPA_DATA_MemFd, /**< generic fd, mmap to get to memory */ + SPA_DATA_DmaBuf, /**< fd to dmabuf memory */ + SPA_DATA_MemId, /**< memory is identified with an id */ + + _SPA_DATA_LAST, /**< not part of ABI */ +}; + +/** Chunk of memory, can change for each buffer */ +struct spa_chunk { + uint32_t offset; /**< offset of valid data. Should be taken + * modulo the data maxsize to get the offset + * in the data memory. */ + uint32_t size; /**< size of valid data. Should be clamped to + * maxsize. */ + int32_t stride; /**< stride of valid data */ +#define SPA_CHUNK_FLAG_NONE 0 +#define SPA_CHUNK_FLAG_CORRUPTED (1u<<0) /**< chunk data is corrupted in some way */ +#define SPA_CHUNK_FLAG_EMPTY (1u<<1) /**< chunk data is empty with media specific + * neutral data such as silence or black. This + * could be used to optimize processing. */ + int32_t flags; /**< chunk flags */ +}; + +/** Data for a buffer this stays constant for a buffer */ +struct spa_data { + uint32_t type; /**< memory type, one of enum spa_data_type, when + * allocating memory, the type contains a bitmask + * of allowed types. SPA_ID_INVALID is a special + * value for the allocator to indicate that the + * other side did not explicitly specify any + * supported data types. It should probably use + * a memory type that does not require special + * handling in addition to simple mmap/munmap. */ +#define SPA_DATA_FLAG_NONE 0 +#define SPA_DATA_FLAG_READABLE (1u<<0) /**< data is readable */ +#define SPA_DATA_FLAG_WRITABLE (1u<<1) /**< data is writable */ +#define SPA_DATA_FLAG_DYNAMIC (1u<<2) /**< data pointer can be changed */ +#define SPA_DATA_FLAG_READWRITE (SPA_DATA_FLAG_READABLE|SPA_DATA_FLAG_WRITABLE) + uint32_t flags; /**< data flags */ + int64_t fd; /**< optional fd for data */ + uint32_t mapoffset; /**< offset to map fd at */ + uint32_t maxsize; /**< max size of data */ + void *data; /**< optional data pointer */ + struct spa_chunk *chunk; /**< valid chunk of memory */ +}; + +/** A Buffer */ +struct spa_buffer { + uint32_t n_metas; /**< number of metadata */ + uint32_t n_datas; /**< number of data members */ + struct spa_meta *metas; /**< array of metadata */ + struct spa_data *datas; /**< array of data members */ +}; + +/** Find metadata in a buffer */ +static inline struct spa_meta *spa_buffer_find_meta(const struct spa_buffer *b, uint32_t type) +{ + uint32_t i; + + for (i = 0; i < b->n_metas; i++) + if (b->metas[i].type == type) + return &b->metas[i]; + + return NULL; +} + +static inline void *spa_buffer_find_meta_data(const struct spa_buffer *b, uint32_t type, size_t size) +{ + struct spa_meta *m; + if ((m = spa_buffer_find_meta(b, type)) && m->size >= size) + return m->data; + return NULL; +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_BUFFER_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/buffer/meta.h b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/meta.h new file mode 100644 index 0000000000000..bf67f12b7f156 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/meta.h @@ -0,0 +1,172 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_META_H +#define SPA_META_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * \addtogroup spa_buffer + * \{ + */ + +enum spa_meta_type { + SPA_META_Invalid, + SPA_META_Header, /**< struct spa_meta_header */ + SPA_META_VideoCrop, /**< struct spa_meta_region with cropping data */ + SPA_META_VideoDamage, /**< array of struct spa_meta_region with damage, where an invalid entry or end-of-array marks the end. */ + SPA_META_Bitmap, /**< struct spa_meta_bitmap */ + SPA_META_Cursor, /**< struct spa_meta_cursor */ + SPA_META_Control, /**< metadata contains a spa_meta_control + * associated with the data */ + SPA_META_Busy, /**< don't write to buffer when count > 0 */ + SPA_META_VideoTransform, /**< struct spa_meta_transform */ + + _SPA_META_LAST, /**< not part of ABI/API */ +}; + +/** + * A metadata element. + * + * This structure is available on the buffer structure and contains + * the type of the metadata and a pointer/size to the actual metadata + * itself. + */ +struct spa_meta { + uint32_t type; /**< metadata type, one of enum spa_meta_type */ + uint32_t size; /**< size of metadata */ + void *data; /**< pointer to metadata */ +}; + +static inline void *spa_meta_first(const struct spa_meta *m) { + return m->data; +} +#define spa_meta_first spa_meta_first +static inline void *spa_meta_end(const struct spa_meta *m) { + return SPA_PTROFF(m->data,m->size,void); +} +#define spa_meta_end spa_meta_end +#define spa_meta_check(p,m) (SPA_PTROFF(p,sizeof(*(p)),void) <= spa_meta_end(m)) + +/** + * Describes essential buffer header metadata such as flags and + * timestamps. + */ +struct spa_meta_header { +#define SPA_META_HEADER_FLAG_DISCONT (1 << 0) /**< data is not continuous with previous buffer */ +#define SPA_META_HEADER_FLAG_CORRUPTED (1 << 1) /**< data might be corrupted */ +#define SPA_META_HEADER_FLAG_MARKER (1 << 2) /**< media specific marker */ +#define SPA_META_HEADER_FLAG_HEADER (1 << 3) /**< data contains a codec specific header */ +#define SPA_META_HEADER_FLAG_GAP (1 << 4) /**< data contains media neutral data */ +#define SPA_META_HEADER_FLAG_DELTA_UNIT (1 << 5) /**< cannot be decoded independently */ + uint32_t flags; /**< flags */ + uint32_t offset; /**< offset in current cycle */ + int64_t pts; /**< presentation timestamp in nanoseconds */ + int64_t dts_offset; /**< decoding timestamp as a difference with pts */ + uint64_t seq; /**< sequence number, increments with a + * media specific frequency */ +}; + +/** metadata structure for Region or an array of these for RegionArray */ +struct spa_meta_region { + struct spa_region region; +}; + +static inline bool spa_meta_region_is_valid(const struct spa_meta_region *m) { + return m->region.size.width != 0 && m->region.size.height != 0; +} +#define spa_meta_region_is_valid spa_meta_region_is_valid + +/** iterate all the items in a metadata */ +#define spa_meta_for_each(pos,meta) \ + for ((pos) = (__typeof(pos))spa_meta_first(meta); \ + spa_meta_check(pos, meta); \ + (pos)++) + +#define spa_meta_bitmap_is_valid(m) ((m)->format != 0) + +/** + * Bitmap information + * + * This metadata contains a bitmap image in the given format and size. + * It is typically used for cursor images or other small images that are + * better transferred inline. + */ +struct spa_meta_bitmap { + uint32_t format; /**< bitmap video format, one of enum spa_video_format. 0 is + * and invalid format and should be handled as if there is + * no new bitmap information. */ + struct spa_rectangle size; /**< width and height of bitmap */ + int32_t stride; /**< stride of bitmap data */ + uint32_t offset; /**< offset of bitmap data in this structure. An offset of + * 0 means no image data (invisible), an offset >= + * sizeof(struct spa_meta_bitmap) contains valid bitmap + * info. */ +}; + +#define spa_meta_cursor_is_valid(m) ((m)->id != 0) + +/** + * Cursor information + * + * Metadata to describe the position and appearance of a pointing device. + */ +struct spa_meta_cursor { + uint32_t id; /**< cursor id. an id of 0 is an invalid id and means that + * there is no new cursor data */ + uint32_t flags; /**< extra flags */ + struct spa_point position; /**< position on screen */ + struct spa_point hotspot; /**< offsets for hotspot in bitmap, this field has no meaning + * when there is no valid bitmap (see below) */ + uint32_t bitmap_offset; /**< offset of bitmap meta in this structure. When the offset + * is 0, there is no new bitmap information. When the offset is + * >= sizeof(struct spa_meta_cursor) there is a + * struct spa_meta_bitmap at the offset. */ +}; + +/** a timed set of events associated with the buffer */ +struct spa_meta_control { + struct spa_pod_sequence sequence; +}; + +/** a busy counter for the buffer */ +struct spa_meta_busy { + uint32_t flags; + uint32_t count; /**< number of users busy with the buffer */ +}; + +enum spa_meta_videotransform_value { + SPA_META_TRANSFORMATION_None = 0, /**< no transform */ + SPA_META_TRANSFORMATION_90, /**< 90 degree counter-clockwise */ + SPA_META_TRANSFORMATION_180, /**< 180 degree counter-clockwise */ + SPA_META_TRANSFORMATION_270, /**< 270 degree counter-clockwise */ + SPA_META_TRANSFORMATION_Flipped, /**< 180 degree flipped around the vertical axis. Equivalent + * to a reflexion through the vertical line splitting the + * bufffer in two equal sized parts */ + SPA_META_TRANSFORMATION_Flipped90, /**< flip then rotate around 90 degree counter-clockwise */ + SPA_META_TRANSFORMATION_Flipped180, /**< flip then rotate around 180 degree counter-clockwise */ + SPA_META_TRANSFORMATION_Flipped270, /**< flip then rotate around 270 degree counter-clockwise */ +}; + +/** a transformation of the buffer */ +struct spa_meta_videotransform { + uint32_t transform; /**< orientation transformation that was applied to the buffer, + * one of enum spa_meta_videotransform_value */ +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_META_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/buffer/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/type-info.h new file mode 100644 index 0000000000000..a5208c0b944da --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/buffer/type-info.h @@ -0,0 +1,74 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_BUFFER_TYPES_H +#define SPA_BUFFER_TYPES_H + +/** + * \addtogroup spa_buffer + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define SPA_TYPE_INFO_Buffer SPA_TYPE_INFO_POINTER_BASE "Buffer" +#define SPA_TYPE_INFO_BUFFER_BASE SPA_TYPE_INFO_Buffer ":" + +/** Buffers contain data of a certain type */ +#define SPA_TYPE_INFO_Data SPA_TYPE_INFO_ENUM_BASE "Data" +#define SPA_TYPE_INFO_DATA_BASE SPA_TYPE_INFO_Data ":" + +/** base type for fd based memory */ +#define SPA_TYPE_INFO_DATA_Fd SPA_TYPE_INFO_DATA_BASE "Fd" +#define SPA_TYPE_INFO_DATA_FD_BASE SPA_TYPE_INFO_DATA_Fd ":" + +static const struct spa_type_info spa_type_data_type[] = { + { SPA_DATA_Invalid, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "Invalid", NULL }, + { SPA_DATA_MemPtr, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "MemPtr", NULL }, + { SPA_DATA_MemFd, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_FD_BASE "MemFd", NULL }, + { SPA_DATA_DmaBuf, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_FD_BASE "DmaBuf", NULL }, + { SPA_DATA_MemId, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "MemId", NULL }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_Meta SPA_TYPE_INFO_POINTER_BASE "Meta" +#define SPA_TYPE_INFO_META_BASE SPA_TYPE_INFO_Meta ":" + +#define SPA_TYPE_INFO_META_Array SPA_TYPE_INFO_META_BASE "Array" +#define SPA_TYPE_INFO_META_ARRAY_BASE SPA_TYPE_INFO_META_Array ":" + +#define SPA_TYPE_INFO_META_Region SPA_TYPE_INFO_META_BASE "Region" +#define SPA_TYPE_INFO_META_REGION_BASE SPA_TYPE_INFO_META_Region ":" + +#define SPA_TYPE_INFO_META_ARRAY_Region SPA_TYPE_INFO_META_ARRAY_BASE "Region" +#define SPA_TYPE_INFO_META_ARRAY_REGION_BASE SPA_TYPE_INFO_META_ARRAY_Region ":" + +static const struct spa_type_info spa_type_meta_type[] = { + { SPA_META_Invalid, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Invalid", NULL }, + { SPA_META_Header, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Header", NULL }, + { SPA_META_VideoCrop, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_REGION_BASE "VideoCrop", NULL }, + { SPA_META_VideoDamage, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_ARRAY_REGION_BASE "VideoDamage", NULL }, + { SPA_META_Bitmap, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Bitmap", NULL }, + { SPA_META_Cursor, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Cursor", NULL }, + { SPA_META_Control, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Control", NULL }, + { SPA_META_Busy, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Busy", NULL }, + { SPA_META_VideoTransform, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "VideoTransform", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_BUFFER_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/control/control.h b/src/java.desktop/unix/native/libpipewire/include/spa/control/control.h new file mode 100644 index 0000000000000..ed04c7e5b9683 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/control/control.h @@ -0,0 +1,42 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_CONTROL_H +#define SPA_CONTROL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** \defgroup spa_control Control + * Control type declarations + */ + +/** + * \addtogroup spa_control + * \{ + */ + +/** Different Control types */ +enum spa_control_type { + SPA_CONTROL_Invalid, + SPA_CONTROL_Properties, /**< data contains a SPA_TYPE_OBJECT_Props */ + SPA_CONTROL_Midi, /**< data contains a spa_pod_bytes with raw midi data */ + SPA_CONTROL_OSC, /**< data contains a spa_pod_bytes with an OSC packet */ + + _SPA_CONTROL_LAST, /**< not part of ABI */ +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_CONTROL_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/control/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/control/type-info.h new file mode 100644 index 0000000000000..a3a61a153c7b2 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/control/type-info.h @@ -0,0 +1,41 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_CONTROL_TYPES_H +#define SPA_CONTROL_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_control + * \{ + */ + +#include +#include +#include + +/* base for parameter object enumerations */ +#define SPA_TYPE_INFO_Control SPA_TYPE_INFO_ENUM_BASE "Control" +#define SPA_TYPE_INFO_CONTROL_BASE SPA_TYPE_INFO_Control ":" + +static const struct spa_type_info spa_type_control[] = { + { SPA_CONTROL_Invalid, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Invalid", NULL }, + { SPA_CONTROL_Properties, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Properties", NULL }, + { SPA_CONTROL_Midi, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Midi", NULL }, + { SPA_CONTROL_OSC, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "OSC", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_CONTROL_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/debug/types.h b/src/java.desktop/unix/native/libpipewire/include/spa/debug/types.h new file mode 100644 index 0000000000000..dda912e7c04e0 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/debug/types.h @@ -0,0 +1,107 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_DEBUG_TYPES_H +#define SPA_DEBUG_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_debug + * \{ + */ + +#include + +#include + +static inline const struct spa_type_info *spa_debug_type_find(const struct spa_type_info *info, uint32_t type) +{ + const struct spa_type_info *res; + + if (info == NULL) + info = SPA_TYPE_ROOT; + + while (info && info->name) { + if (info->type == SPA_ID_INVALID) { + if (info->values && (res = spa_debug_type_find(info->values, type))) + return res; + } + else if (info->type == type) + return info; + info++; + } + return NULL; +} + +static inline const char *spa_debug_type_short_name(const char *name) +{ + const char *h; + if ((h = strrchr(name, ':')) != NULL) + name = h + 1; + return name; +} + +static inline const char *spa_debug_type_find_name(const struct spa_type_info *info, uint32_t type) +{ + if ((info = spa_debug_type_find(info, type)) == NULL) + return NULL; + return info->name; +} + +static inline const char *spa_debug_type_find_short_name(const struct spa_type_info *info, uint32_t type) +{ + const char *str; + if ((str = spa_debug_type_find_name(info, type)) == NULL) + return NULL; + return spa_debug_type_short_name(str); +} + +static inline uint32_t spa_debug_type_find_type(const struct spa_type_info *info, const char *name) +{ + if (info == NULL) + info = SPA_TYPE_ROOT; + + while (info && info->name) { + uint32_t res; + if (strcmp(info->name, name) == 0) + return info->type; + if (info->values && (res = spa_debug_type_find_type(info->values, name)) != SPA_ID_INVALID) + return res; + info++; + } + return SPA_ID_INVALID; +} + +static inline const struct spa_type_info *spa_debug_type_find_short(const struct spa_type_info *info, const char *name) +{ + while (info && info->name) { + if (strcmp(spa_debug_type_short_name(info->name), name) == 0) + return info; + if (strcmp(info->name, name) == 0) + return info; + if (info->type != 0 && info->type == (uint32_t)atoi(name)) + return info; + info++; + } + return NULL; +} + +static inline uint32_t spa_debug_type_find_type_short(const struct spa_type_info *info, const char *name) +{ + if ((info = spa_debug_type_find_short(info, name)) == NULL) + return SPA_ID_INVALID; + return info->type; +} +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_DEBUG_NODE_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/monitor/event.h b/src/java.desktop/unix/native/libpipewire/include/spa/monitor/event.h new file mode 100644 index 0000000000000..423127e61206e --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/monitor/event.h @@ -0,0 +1,43 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_EVENT_DEVICE_H +#define SPA_EVENT_DEVICE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * \addtogroup spa_device + * \{ + */ + +/* object id of SPA_TYPE_EVENT_Device */ +enum spa_device_event { + SPA_DEVICE_EVENT_ObjectConfig, +}; + +#define SPA_DEVICE_EVENT_ID(ev) SPA_EVENT_ID(ev, SPA_TYPE_EVENT_Device) +#define SPA_DEVICE_EVENT_INIT(id) SPA_EVENT_INIT(SPA_TYPE_EVENT_Device, id) + +/* properties for SPA_TYPE_EVENT_Device */ +enum spa_event_device { + SPA_EVENT_DEVICE_START, + + SPA_EVENT_DEVICE_Object, /* an object id (Int) */ + SPA_EVENT_DEVICE_Props, /* properties for an object (SPA_TYPE_OBJECT_Props) */ +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_EVENT_DEVICE */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/monitor/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/monitor/type-info.h new file mode 100644 index 0000000000000..f5920c206004e --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/monitor/type-info.h @@ -0,0 +1,47 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2021 Collabora Ltd. */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_DEVICE_TYPE_INFO_H +#define SPA_DEVICE_TYPE_INFO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/** + * \addtogroup spa_device + * \{ + */ + +#define SPA_TYPE_INFO_DeviceEvent SPA_TYPE_INFO_EVENT_BASE "Device" +#define SPA_TYPE_INFO_DEVICE_EVENT_BASE SPA_TYPE_INFO_DeviceEvent ":" + +#define SPA_TYPE_INFO_DeviceEventId SPA_TYPE_INFO_ENUM_BASE "DeviceEventId" +#define SPA_TYPE_INFO_DEVICE_EVENT_ID_BASE SPA_TYPE_INFO_DeviceEventId ":" + +static const struct spa_type_info spa_type_device_event_id[] = { + { SPA_DEVICE_EVENT_ObjectConfig, SPA_TYPE_EVENT_Device, SPA_TYPE_INFO_DEVICE_EVENT_ID_BASE "ObjectConfig", NULL }, + { 0, 0, NULL, NULL }, +}; + +static const struct spa_type_info spa_type_device_event[] = { + { SPA_EVENT_DEVICE_START, SPA_TYPE_Id, SPA_TYPE_INFO_DEVICE_EVENT_BASE, spa_type_device_event_id }, + { SPA_EVENT_DEVICE_Object, SPA_TYPE_Int, SPA_TYPE_INFO_DEVICE_EVENT_BASE "Object", NULL }, + { SPA_EVENT_DEVICE_Props, SPA_TYPE_OBJECT_Props, SPA_TYPE_INFO_DEVICE_EVENT_BASE "Props", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_DEVICE_TYPE_INFO_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/node/command.h b/src/java.desktop/unix/native/libpipewire/include/spa/node/command.h new file mode 100644 index 0000000000000..0915fca6ceeb7 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/node/command.h @@ -0,0 +1,53 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_COMMAND_NODE_H +#define SPA_COMMAND_NODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_node + * \{ + */ + +#include + +/* object id of SPA_TYPE_COMMAND_Node */ +enum spa_node_command { + SPA_NODE_COMMAND_Suspend, /**< suspend a node, this removes all configured + * formats and closes any devices */ + SPA_NODE_COMMAND_Pause, /**< pause a node. this makes it stop emitting + * scheduling events */ + SPA_NODE_COMMAND_Start, /**< start a node, this makes it start emitting + * scheduling events */ + SPA_NODE_COMMAND_Enable, + SPA_NODE_COMMAND_Disable, + SPA_NODE_COMMAND_Flush, + SPA_NODE_COMMAND_Drain, + SPA_NODE_COMMAND_Marker, + SPA_NODE_COMMAND_ParamBegin, /**< begin a set of parameter enumerations or + * configuration that require the device to + * remain opened, like query formats and then + * set a format */ + SPA_NODE_COMMAND_ParamEnd, /**< end a transaction */ + SPA_NODE_COMMAND_RequestProcess,/**< Sent to a driver when some other node emitted + * the RequestProcess event. */ +}; + +#define SPA_NODE_COMMAND_ID(cmd) SPA_COMMAND_ID(cmd, SPA_TYPE_COMMAND_Node) +#define SPA_NODE_COMMAND_INIT(id) SPA_COMMAND_INIT(SPA_TYPE_COMMAND_Node, id) + + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_COMMAND_NODE_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/node/event.h b/src/java.desktop/unix/native/libpipewire/include/spa/node/event.h new file mode 100644 index 0000000000000..94c2ef1316a45 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/node/event.h @@ -0,0 +1,44 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_EVENT_NODE_H +#define SPA_EVENT_NODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_node + * \{ + */ + +#include + +/* object id of SPA_TYPE_EVENT_Node */ +enum spa_node_event { + SPA_NODE_EVENT_Error, + SPA_NODE_EVENT_Buffering, + SPA_NODE_EVENT_RequestRefresh, + SPA_NODE_EVENT_RequestProcess, /*< Ask the driver to start processing + * the graph */ +}; + +#define SPA_NODE_EVENT_ID(ev) SPA_EVENT_ID(ev, SPA_TYPE_EVENT_Node) +#define SPA_NODE_EVENT_INIT(id) SPA_EVENT_INIT(SPA_TYPE_EVENT_Node, id) + +/* properties for SPA_TYPE_EVENT_Node */ +enum spa_event_node { + SPA_EVENT_NODE_START, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_EVENT_NODE_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/node/io.h b/src/java.desktop/unix/native/libpipewire/include/spa/node/io.h new file mode 100644 index 0000000000000..a25c8fd11c5a5 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/node/io.h @@ -0,0 +1,290 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_IO_H +#define SPA_IO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_node + * \{ + */ + +#include +#include + +/** IO areas + * + * IO information for a port on a node. This is allocated + * by the host and configured on a node or all ports for which + * IO is requested. + * + * The plugin will communicate with the host through the IO + * areas. + */ + +/** Different IO area types */ +enum spa_io_type { + SPA_IO_Invalid, + SPA_IO_Buffers, /**< area to exchange buffers, struct spa_io_buffers */ + SPA_IO_Range, /**< expected byte range, struct spa_io_range */ + SPA_IO_Clock, /**< area to update clock information, struct spa_io_clock */ + SPA_IO_Latency, /**< latency reporting, struct spa_io_latency */ + SPA_IO_Control, /**< area for control messages, struct spa_io_sequence */ + SPA_IO_Notify, /**< area for notify messages, struct spa_io_sequence */ + SPA_IO_Position, /**< position information in the graph, struct spa_io_position */ + SPA_IO_RateMatch, /**< rate matching between nodes, struct spa_io_rate_match */ + SPA_IO_Memory, /**< memory pointer, struct spa_io_memory */ +}; + +/** + * IO area to exchange buffers. + * + * A set of buffers should first be configured on the node/port. + * Further references to those buffers will be made by using the + * id of the buffer. + * + * If status is SPA_STATUS_OK, the host should ignore + * the io area. + * + * If status is SPA_STATUS_NEED_DATA, the host should: + * 1) recycle the buffer in buffer_id, if possible + * 2) prepare a new buffer and place the id in buffer_id. + * + * If status is SPA_STATUS_HAVE_DATA, the host should consume + * the buffer in buffer_id and set the state to + * SPA_STATUS_NEED_DATA when new data is requested. + * + * If status is SPA_STATUS_STOPPED, some error occurred on the + * port. + * + * If status is SPA_STATUS_DRAINED, data from the io area was + * used to drain. + * + * Status can also be a negative errno value to indicate errors. + * such as: + * -EINVAL: buffer_id is invalid + * -EPIPE: no more buffers available + */ +struct spa_io_buffers { +#define SPA_STATUS_OK 0 +#define SPA_STATUS_NEED_DATA (1<<0) +#define SPA_STATUS_HAVE_DATA (1<<1) +#define SPA_STATUS_STOPPED (1<<2) +#define SPA_STATUS_DRAINED (1<<3) + int32_t status; /**< the status code */ + uint32_t buffer_id; /**< a buffer id */ +}; + +#define SPA_IO_BUFFERS_INIT ((struct spa_io_buffers) { SPA_STATUS_OK, SPA_ID_INVALID, }) + +/** + * IO area to exchange a memory region + */ +struct spa_io_memory { + int32_t status; /**< the status code */ + uint32_t size; /**< the size of \a data */ + void *data; /**< a memory pointer */ +}; +#define SPA_IO_MEMORY_INIT ((struct spa_io_memory) { SPA_STATUS_OK, 0, NULL, }) + +/** A range, suitable for input ports that can suggest a range to output ports */ +struct spa_io_range { + uint64_t offset; /**< offset in range */ + uint32_t min_size; /**< minimum size of data */ + uint32_t max_size; /**< maximum size of data */ +}; + +/** + * Absolute time reporting. + * + * Nodes that can report clocking information will receive this io block. + * The application sets the id. This is usually set as part of the + * position information but can also be set separately. + * + * The clock counts the elapsed time according to the clock provider + * since the provider was last started. + */ +struct spa_io_clock { +#define SPA_IO_CLOCK_FLAG_FREEWHEEL (1u<<0) + uint32_t flags; /**< clock flags */ + uint32_t id; /**< unique clock id, set by application */ + char name[64]; /**< clock name prefixed with API, set by node. The clock name + * is unique per clock and can be used to check if nodes + * share the same clock. */ + uint64_t nsec; /**< time in nanoseconds against monotonic clock */ + struct spa_fraction rate; /**< rate for position/duration/delay */ + uint64_t position; /**< current position */ + uint64_t duration; /**< duration of current cycle */ + int64_t delay; /**< delay between position and hardware, + * positive for capture, negative for playback */ + double rate_diff; /**< rate difference between clock and monotonic time */ + uint64_t next_nsec; /**< estimated next wakeup time in nanoseconds */ + + struct spa_fraction target_rate; /**< target rate of next cycle */ + uint64_t target_duration; /**< target duration of next cycle */ + uint32_t target_seq; /**< seq counter. must be equal at start and + * end of read and lower bit must be 0 */ + + uint32_t padding[3]; +}; + +/* the size of the video in this cycle */ +struct spa_io_video_size { +#define SPA_IO_VIDEO_SIZE_VALID (1<<0) + uint32_t flags; /**< optional flags */ + uint32_t stride; /**< video stride in bytes */ + struct spa_rectangle size; /**< the video size */ + struct spa_fraction framerate; /**< the minimum framerate, the cycle duration is + * always smaller to ensure there is only one + * video frame per cycle. */ + uint32_t padding[4]; +}; + +/** latency reporting */ +struct spa_io_latency { + struct spa_fraction rate; /**< rate for min/max */ + uint64_t min; /**< min latency */ + uint64_t max; /**< max latency */ +}; + +/** control stream, io area for SPA_IO_Control and SPA_IO_Notify */ +struct spa_io_sequence { + struct spa_pod_sequence sequence; /**< sequence of timed events */ +}; + +/** bar and beat segment */ +struct spa_io_segment_bar { +#define SPA_IO_SEGMENT_BAR_FLAG_VALID (1<<0) + uint32_t flags; /**< extra flags */ + uint32_t offset; /**< offset in segment of this beat */ + float signature_num; /**< time signature numerator */ + float signature_denom; /**< time signature denominator */ + double bpm; /**< beats per minute */ + double beat; /**< current beat in segment */ + uint32_t padding[8]; +}; + +/** video frame segment */ +struct spa_io_segment_video { +#define SPA_IO_SEGMENT_VIDEO_FLAG_VALID (1<<0) +#define SPA_IO_SEGMENT_VIDEO_FLAG_DROP_FRAME (1<<1) +#define SPA_IO_SEGMENT_VIDEO_FLAG_PULL_DOWN (1<<2) +#define SPA_IO_SEGMENT_VIDEO_FLAG_INTERLACED (1<<3) + uint32_t flags; /**< flags */ + uint32_t offset; /**< offset in segment */ + struct spa_fraction framerate; + uint32_t hours; + uint32_t minutes; + uint32_t seconds; + uint32_t frames; + uint32_t field_count; /**< 0 for progressive, 1 and 2 for interlaced */ + uint32_t padding[11]; +}; + +/** + * A segment converts a running time to a segment (stream) position. + * + * The segment position is valid when the current running time is between + * start and start + duration. The position is then + * calculated as: + * + * (running time - start) * rate + position; + * + * Support for looping is done by specifying the LOOPING flags with a + * non-zero duration. When the running time reaches start + duration, + * duration is added to start and the loop repeats. + * + * Care has to be taken when the running time + clock.duration extends + * past the start + duration from the segment; the user should correctly + * wrap around and partially repeat the loop in the current cycle. + * + * Extra information can be placed in the segment by setting the valid flags + * and filling up the corresponding structures. + */ +struct spa_io_segment { + uint32_t version; +#define SPA_IO_SEGMENT_FLAG_LOOPING (1<<0) /**< after the duration, the segment repeats */ +#define SPA_IO_SEGMENT_FLAG_NO_POSITION (1<<1) /**< position is invalid. The position can be invalid + * after a seek, for example, when the exact mapping + * of the extra segment info (bar, video, ...) to + * position has not been determined yet */ + uint32_t flags; /**< extra flags */ + uint64_t start; /**< value of running time when this + * info is active. Can be in the future for + * pending changes. It does not have to be in + * exact multiples of the clock duration. */ + uint64_t duration; /**< duration when this info becomes invalid expressed + * in running time. If the duration is 0, this + * segment extends to the next segment. If the + * segment becomes invalid and the looping flag is + * set, the segment repeats. */ + double rate; /**< overall rate of the segment, can be negative for + * backwards time reporting. */ + uint64_t position; /**< The position when the running time == start. + * can be invalid when the owner of the extra segment + * information has not yet made the mapping. */ + + struct spa_io_segment_bar bar; + struct spa_io_segment_video video; +}; + +enum spa_io_position_state { + SPA_IO_POSITION_STATE_STOPPED, + SPA_IO_POSITION_STATE_STARTING, + SPA_IO_POSITION_STATE_RUNNING, +}; + +/** the maximum number of segments visible in the future */ +#define SPA_IO_POSITION_MAX_SEGMENTS 8 + +/** + * The position information adds extra meaning to the raw clock times. + * + * It is set on all nodes and the clock id will contain the clock of the + * driving node in the graph. + * + * The position information contains 1 or more segments that convert the + * raw clock times to a stream time. They are sorted based on their + * start times, and thus the order in which they will activate in + * the future. This makes it possible to look ahead in the scheduled + * segments and anticipate the changes in the timeline. + */ +struct spa_io_position { + struct spa_io_clock clock; /**< clock position of driver, always valid and + * read only */ + struct spa_io_video_size video; /**< size of the video in the current cycle */ + int64_t offset; /**< an offset to subtract from the clock position + * to get a running time. This is the time that + * the state has been in the RUNNING state and the + * time that should be used to compare the segment + * start values against. */ + uint32_t state; /**< one of enum spa_io_position_state */ + + uint32_t n_segments; /**< number of segments */ + struct spa_io_segment segments[SPA_IO_POSITION_MAX_SEGMENTS]; /**< segments */ +}; + +/** rate matching */ +struct spa_io_rate_match { + uint32_t delay; /**< extra delay in samples for resampler */ + uint32_t size; /**< requested input size for resampler */ + double rate; /**< rate for resampler */ +#define SPA_IO_RATE_MATCH_FLAG_ACTIVE (1 << 0) + uint32_t flags; /**< extra flags */ + uint32_t padding[7]; +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_IO_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/node/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/node/type-info.h new file mode 100644 index 0000000000000..3d3d7908519c0 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/node/type-info.h @@ -0,0 +1,87 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_NODE_TYPES_H +#define SPA_NODE_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_node + * \{ + */ + +#include + +#include +#include +#include + +#define SPA_TYPE_INFO_IO SPA_TYPE_INFO_ENUM_BASE "IO" +#define SPA_TYPE_INFO_IO_BASE SPA_TYPE_INFO_IO ":" + +static const struct spa_type_info spa_type_io[] = { + { SPA_IO_Invalid, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Invalid", NULL }, + { SPA_IO_Buffers, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Buffers", NULL }, + { SPA_IO_Range, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Range", NULL }, + { SPA_IO_Clock, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Clock", NULL }, + { SPA_IO_Latency, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Latency", NULL }, + { SPA_IO_Control, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Control", NULL }, + { SPA_IO_Notify, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Notify", NULL }, + { SPA_IO_Position, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Position", NULL }, + { SPA_IO_RateMatch, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "RateMatch", NULL }, + { SPA_IO_Memory, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Memory", NULL }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_NodeEvent SPA_TYPE_INFO_EVENT_BASE "Node" +#define SPA_TYPE_INFO_NODE_EVENT_BASE SPA_TYPE_INFO_NodeEvent ":" + +static const struct spa_type_info spa_type_node_event_id[] = { + { SPA_NODE_EVENT_Error, SPA_TYPE_EVENT_Node, SPA_TYPE_INFO_NODE_EVENT_BASE "Error", NULL }, + { SPA_NODE_EVENT_Buffering, SPA_TYPE_EVENT_Node, SPA_TYPE_INFO_NODE_EVENT_BASE "Buffering", NULL }, + { SPA_NODE_EVENT_RequestRefresh, SPA_TYPE_EVENT_Node, SPA_TYPE_INFO_NODE_EVENT_BASE "RequestRefresh", NULL }, + { SPA_NODE_EVENT_RequestProcess, SPA_TYPE_EVENT_Node, SPA_TYPE_INFO_NODE_EVENT_BASE "RequestProcess", NULL }, + { 0, 0, NULL, NULL }, +}; + +static const struct spa_type_info spa_type_node_event[] = { + { SPA_EVENT_NODE_START, SPA_TYPE_Id, SPA_TYPE_INFO_NODE_EVENT_BASE, spa_type_node_event_id }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_NodeCommand SPA_TYPE_INFO_COMMAND_BASE "Node" +#define SPA_TYPE_INFO_NODE_COMMAND_BASE SPA_TYPE_INFO_NodeCommand ":" + +static const struct spa_type_info spa_type_node_command_id[] = { + { SPA_NODE_COMMAND_Suspend, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Suspend", NULL }, + { SPA_NODE_COMMAND_Pause, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Pause", NULL }, + { SPA_NODE_COMMAND_Start, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Start", NULL }, + { SPA_NODE_COMMAND_Enable, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Enable", NULL }, + { SPA_NODE_COMMAND_Disable, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Disable", NULL }, + { SPA_NODE_COMMAND_Flush, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Flush", NULL }, + { SPA_NODE_COMMAND_Drain, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Drain", NULL }, + { SPA_NODE_COMMAND_Marker, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Marker", NULL }, + { SPA_NODE_COMMAND_ParamBegin, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "ParamBegin", NULL }, + { SPA_NODE_COMMAND_ParamEnd, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "ParamEnd", NULL }, + { SPA_NODE_COMMAND_RequestProcess, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "RequestProcess", NULL }, + { 0, 0, NULL, NULL }, +}; + +static const struct spa_type_info spa_type_node_command[] = { + { 0, SPA_TYPE_Id, SPA_TYPE_INFO_NODE_COMMAND_BASE, spa_type_node_command_id }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_NODE_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac-types.h new file mode 100644 index 0000000000000..26c80bfa9db1c --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac-types.h @@ -0,0 +1,38 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_AAC_TYPES_H +#define SPA_AUDIO_AAC_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define SPA_TYPE_INFO_AudioAACStreamFormat SPA_TYPE_INFO_ENUM_BASE "AudioAACStreamFormat" +#define SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE SPA_TYPE_INFO_AudioAACStreamFormat ":" + +static const struct spa_type_info spa_type_audio_aac_stream_format[] = { + { SPA_AUDIO_AAC_STREAM_FORMAT_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE "UNKNOWN", NULL }, + { SPA_AUDIO_AAC_STREAM_FORMAT_RAW, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE "RAW", NULL }, + { SPA_AUDIO_AAC_STREAM_FORMAT_MP2ADTS, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE "MP2ADTS", NULL }, + { SPA_AUDIO_AAC_STREAM_FORMAT_MP4ADTS, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE "MP4ADTS", NULL }, + { SPA_AUDIO_AAC_STREAM_FORMAT_MP4LOAS, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE "MP4LOAS", NULL }, + { SPA_AUDIO_AAC_STREAM_FORMAT_MP4LATM, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE "MP4LATM", NULL }, + { SPA_AUDIO_AAC_STREAM_FORMAT_ADIF, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE "ADIF", NULL }, + { SPA_AUDIO_AAC_STREAM_FORMAT_MP4FF, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AAC_STREAM_FORMAT_BASE "MP4FF", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_AAC_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac.h new file mode 100644 index 0000000000000..dc5257c2dfd3d --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/aac.h @@ -0,0 +1,51 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_AAC_H +#define SPA_AUDIO_AAC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +enum spa_audio_aac_stream_format { + SPA_AUDIO_AAC_STREAM_FORMAT_UNKNOWN, + /* Raw AAC frames */ + SPA_AUDIO_AAC_STREAM_FORMAT_RAW, + /* ISO/IEC 13818-7 MPEG-2 Audio Data Transport Stream (ADTS) */ + SPA_AUDIO_AAC_STREAM_FORMAT_MP2ADTS, + /* ISO/IEC 14496-3 MPEG-4 Audio Data Transport Stream (ADTS) */ + SPA_AUDIO_AAC_STREAM_FORMAT_MP4ADTS, + /* ISO/IEC 14496-3 Low Overhead Audio Stream (LOAS) */ + SPA_AUDIO_AAC_STREAM_FORMAT_MP4LOAS, + /* ISO/IEC 14496-3 Low Overhead Audio Transport Multiplex (LATM) */ + SPA_AUDIO_AAC_STREAM_FORMAT_MP4LATM, + /* ISO/IEC 14496-3 Audio Data Interchange Format (ADIF) */ + SPA_AUDIO_AAC_STREAM_FORMAT_ADIF, + /* ISO/IEC 14496-12 MPEG-4 file format */ + SPA_AUDIO_AAC_STREAM_FORMAT_MP4FF, + + SPA_AUDIO_AAC_STREAM_FORMAT_CUSTOM = 0x10000, +}; + +struct spa_audio_info_aac { + uint32_t rate; /*< sample rate */ + uint32_t channels; /*< number of channels */ + uint32_t bitrate; /*< stream bitrate */ + enum spa_audio_aac_stream_format stream_format; /*< AAC audio stream format */ +}; + +#define SPA_AUDIO_INFO_AAC_INIT(...) ((struct spa_audio_info_aac) { __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_AAC_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr-types.h new file mode 100644 index 0000000000000..5e07a784524c0 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr-types.h @@ -0,0 +1,32 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_AMR_TYPES_H +#define SPA_AUDIO_AMR_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define SPA_TYPE_INFO_AudioAMRBandMode SPA_TYPE_INFO_ENUM_BASE "AudioAMRBandMode" +#define SPA_TYPE_INFO_AUDIO_AMR_BAND_MODE_BASE SPA_TYPE_INFO_AudioAMRBandMode ":" + +static const struct spa_type_info spa_type_audio_amr_band_mode[] = { + { SPA_AUDIO_AMR_BAND_MODE_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AMR_BAND_MODE_BASE "UNKNOWN", NULL }, + { SPA_AUDIO_AMR_BAND_MODE_NB, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AMR_BAND_MODE_BASE "NB", NULL }, + { SPA_AUDIO_AMR_BAND_MODE_WB, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_AMR_BAND_MODE_BASE "WB", NULL }, + { 0, 0, NULL, NULL }, +}; +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_AMR_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr.h new file mode 100644 index 0000000000000..88b2c4cbcf3ef --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/amr.h @@ -0,0 +1,36 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_AMR_H +#define SPA_AUDIO_AMR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +enum spa_audio_amr_band_mode { + SPA_AUDIO_AMR_BAND_MODE_UNKNOWN, + SPA_AUDIO_AMR_BAND_MODE_NB, + SPA_AUDIO_AMR_BAND_MODE_WB, +}; + +struct spa_audio_info_amr { + uint32_t rate; /*< sample rate */ + uint32_t channels; /*< number of channels */ + enum spa_audio_amr_band_mode band_mode; +}; + +#define SPA_AUDIO_INFO_AMR_INIT(...) ((struct spa_audio_info_amr) { __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_AMR_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/iec958-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/iec958-types.h new file mode 100644 index 0000000000000..fc8243a769b78 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/iec958-types.h @@ -0,0 +1,39 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_IEC958_TYPES_H +#define SPA_AUDIO_IEC958_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define SPA_TYPE_INFO_AudioIEC958Codec SPA_TYPE_INFO_ENUM_BASE "AudioIEC958Codec" +#define SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE SPA_TYPE_INFO_AudioIEC958Codec ":" + +static const struct spa_type_info spa_type_audio_iec958_codec[] = { + { SPA_AUDIO_IEC958_CODEC_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "UNKNOWN", NULL }, + { SPA_AUDIO_IEC958_CODEC_PCM, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "PCM", NULL }, + { SPA_AUDIO_IEC958_CODEC_DTS, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "DTS", NULL }, + { SPA_AUDIO_IEC958_CODEC_AC3, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "AC3", NULL }, + { SPA_AUDIO_IEC958_CODEC_MPEG, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "MPEG", NULL }, + { SPA_AUDIO_IEC958_CODEC_MPEG2_AAC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "MPEG2-AAC", NULL }, + { SPA_AUDIO_IEC958_CODEC_EAC3, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "EAC3", NULL }, + { SPA_AUDIO_IEC958_CODEC_TRUEHD, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "TrueHD", NULL }, + { SPA_AUDIO_IEC958_CODEC_DTSHD, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "DTS-HD", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_RAW_IEC958_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/iec958.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/iec958.h new file mode 100644 index 0000000000000..103f235cb6698 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/iec958.h @@ -0,0 +1,49 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_IEC958_H +#define SPA_AUDIO_IEC958_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ +enum spa_audio_iec958_codec { + SPA_AUDIO_IEC958_CODEC_UNKNOWN, + + SPA_AUDIO_IEC958_CODEC_PCM, + SPA_AUDIO_IEC958_CODEC_DTS, + SPA_AUDIO_IEC958_CODEC_AC3, + SPA_AUDIO_IEC958_CODEC_MPEG, /**< MPEG-1 or MPEG-2 (Part 3, not AAC) */ + SPA_AUDIO_IEC958_CODEC_MPEG2_AAC, /**< MPEG-2 AAC */ + + SPA_AUDIO_IEC958_CODEC_EAC3, + + SPA_AUDIO_IEC958_CODEC_TRUEHD, /**< Dolby TrueHD */ + SPA_AUDIO_IEC958_CODEC_DTSHD, /**< DTS-HD Master Audio */ +}; + +struct spa_audio_info_iec958 { + enum spa_audio_iec958_codec codec; /*< format, one of the DSP formats in enum spa_audio_format_dsp */ + uint32_t flags; /*< extra flags */ + uint32_t rate; /*< sample rate */ +}; + +#define SPA_AUDIO_INFO_IEC958_INIT(...) ((struct spa_audio_info_iec958) { __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_IEC958_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3-types.h new file mode 100644 index 0000000000000..6907090faaf6a --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3-types.h @@ -0,0 +1,34 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_MP3_TYPES_H +#define SPA_AUDIO_MP3_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define SPA_TYPE_INFO_AudioMP3ChannelMode SPA_TYPE_INFO_ENUM_BASE "AudioMP3ChannelMode" +#define SPA_TYPE_INFO_AUDIO_MP3_CHANNEL_MODE_BASE SPA_TYPE_INFO_AudioMP3ChannelMode ":" + +static const struct spa_type_info spa_type_audio_mp3_channel_mode[] = { + { SPA_AUDIO_MP3_CHANNEL_MODE_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_MP3_CHANNEL_MODE_BASE "UNKNOWN", NULL }, + { SPA_AUDIO_MP3_CHANNEL_MODE_MONO, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_MP3_CHANNEL_MODE_BASE "Mono", NULL }, + { SPA_AUDIO_MP3_CHANNEL_MODE_STEREO, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_MP3_CHANNEL_MODE_BASE "Stereo", NULL }, + { SPA_AUDIO_MP3_CHANNEL_MODE_JOINTSTEREO, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_MP3_CHANNEL_MODE_BASE "Joint-stereo", NULL }, + { SPA_AUDIO_MP3_CHANNEL_MODE_DUAL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_MP3_CHANNEL_MODE_BASE "Dual", NULL }, + { 0, 0, NULL, NULL }, +}; +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_MP3_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3.h new file mode 100644 index 0000000000000..51f4c2eaf75e5 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/mp3.h @@ -0,0 +1,37 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_MP3_H +#define SPA_AUDIO_MP3_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +enum spa_audio_mp3_channel_mode { + SPA_AUDIO_MP3_CHANNEL_MODE_UNKNOWN, + SPA_AUDIO_MP3_CHANNEL_MODE_MONO, + SPA_AUDIO_MP3_CHANNEL_MODE_STEREO, + SPA_AUDIO_MP3_CHANNEL_MODE_JOINTSTEREO, + SPA_AUDIO_MP3_CHANNEL_MODE_DUAL, +}; + +struct spa_audio_info_mp3 { + uint32_t rate; /*< sample rate */ + uint32_t channels; /*< number of channels */ +}; + +#define SPA_AUDIO_INFO_MP3_INIT(...) ((struct spa_audio_info_mp3) { __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_MP3_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw-types.h new file mode 100644 index 0000000000000..50a42157602d9 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw-types.h @@ -0,0 +1,258 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_RAW_TYPES_H +#define SPA_AUDIO_RAW_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include + +#define SPA_TYPE_INFO_AudioFormat SPA_TYPE_INFO_ENUM_BASE "AudioFormat" +#define SPA_TYPE_INFO_AUDIO_FORMAT_BASE SPA_TYPE_INFO_AudioFormat ":" + +static const struct spa_type_info spa_type_audio_format[] = { + { SPA_AUDIO_FORMAT_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "UNKNOWN", NULL }, + { SPA_AUDIO_FORMAT_ENCODED, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "ENCODED", NULL }, + { SPA_AUDIO_FORMAT_S8, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S8", NULL }, + { SPA_AUDIO_FORMAT_U8, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U8", NULL }, + { SPA_AUDIO_FORMAT_S16_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16LE", NULL }, + { SPA_AUDIO_FORMAT_S16_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16BE", NULL }, + { SPA_AUDIO_FORMAT_U16_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16LE", NULL }, + { SPA_AUDIO_FORMAT_U16_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16BE", NULL }, + { SPA_AUDIO_FORMAT_S24_32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32LE", NULL }, + { SPA_AUDIO_FORMAT_S24_32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32BE", NULL }, + { SPA_AUDIO_FORMAT_U24_32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32LE", NULL }, + { SPA_AUDIO_FORMAT_U24_32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32BE", NULL }, + { SPA_AUDIO_FORMAT_S32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32LE", NULL }, + { SPA_AUDIO_FORMAT_S32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32BE", NULL }, + { SPA_AUDIO_FORMAT_U32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32LE", NULL }, + { SPA_AUDIO_FORMAT_U32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32BE", NULL }, + { SPA_AUDIO_FORMAT_S24_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24LE", NULL }, + { SPA_AUDIO_FORMAT_S24_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24BE", NULL }, + { SPA_AUDIO_FORMAT_U24_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24LE", NULL }, + { SPA_AUDIO_FORMAT_U24_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24BE", NULL }, + { SPA_AUDIO_FORMAT_S20_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20LE", NULL }, + { SPA_AUDIO_FORMAT_S20_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20BE", NULL }, + { SPA_AUDIO_FORMAT_U20_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20LE", NULL }, + { SPA_AUDIO_FORMAT_U20_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20BE", NULL }, + { SPA_AUDIO_FORMAT_S18_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18LE", NULL }, + { SPA_AUDIO_FORMAT_S18_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18BE", NULL }, + { SPA_AUDIO_FORMAT_U18_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18LE", NULL }, + { SPA_AUDIO_FORMAT_U18_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18BE", NULL }, + { SPA_AUDIO_FORMAT_F32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32LE", NULL }, + { SPA_AUDIO_FORMAT_F32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32BE", NULL }, + { SPA_AUDIO_FORMAT_F64_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64LE", NULL }, + { SPA_AUDIO_FORMAT_F64_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64BE", NULL }, + + { SPA_AUDIO_FORMAT_ULAW, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "ULAW", NULL }, + { SPA_AUDIO_FORMAT_ALAW, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "ALAW", NULL }, + + { SPA_AUDIO_FORMAT_U8P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U8P", NULL }, + { SPA_AUDIO_FORMAT_S16P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16P", NULL }, + { SPA_AUDIO_FORMAT_S24_32P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32P", NULL }, + { SPA_AUDIO_FORMAT_S32P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32P", NULL }, + { SPA_AUDIO_FORMAT_S24P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24P", NULL }, + { SPA_AUDIO_FORMAT_F32P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32P", NULL }, + { SPA_AUDIO_FORMAT_F64P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64P", NULL }, + { SPA_AUDIO_FORMAT_S8P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S8P", NULL }, + +#if __BYTE_ORDER == __BIG_ENDIAN + { SPA_AUDIO_FORMAT_S16_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16OE", NULL }, + { SPA_AUDIO_FORMAT_S16, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16", NULL }, + { SPA_AUDIO_FORMAT_U16_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16OE", NULL }, + { SPA_AUDIO_FORMAT_U16, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16", NULL }, + { SPA_AUDIO_FORMAT_S24_32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32OE", NULL }, + { SPA_AUDIO_FORMAT_S24_32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32", NULL }, + { SPA_AUDIO_FORMAT_U24_32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32OE", NULL }, + { SPA_AUDIO_FORMAT_U24_32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32", NULL }, + { SPA_AUDIO_FORMAT_S32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32OE", NULL }, + { SPA_AUDIO_FORMAT_S32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32", NULL }, + { SPA_AUDIO_FORMAT_U32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32OE", NULL }, + { SPA_AUDIO_FORMAT_U32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32", NULL }, + { SPA_AUDIO_FORMAT_S24_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24OE", NULL }, + { SPA_AUDIO_FORMAT_S24, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24", NULL }, + { SPA_AUDIO_FORMAT_U24_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24OE", NULL }, + { SPA_AUDIO_FORMAT_U24, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24", NULL }, + { SPA_AUDIO_FORMAT_S20_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20OE", NULL }, + { SPA_AUDIO_FORMAT_S20, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20", NULL }, + { SPA_AUDIO_FORMAT_U20_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20OE", NULL }, + { SPA_AUDIO_FORMAT_U20, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20", NULL }, + { SPA_AUDIO_FORMAT_S18_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18OE", NULL }, + { SPA_AUDIO_FORMAT_S18, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18", NULL }, + { SPA_AUDIO_FORMAT_U18_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18OE", NULL }, + { SPA_AUDIO_FORMAT_U18, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18", NULL }, + { SPA_AUDIO_FORMAT_F32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32OE", NULL }, + { SPA_AUDIO_FORMAT_F32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32", NULL }, + { SPA_AUDIO_FORMAT_F64_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64OE", NULL }, + { SPA_AUDIO_FORMAT_F64, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64", NULL }, +#elif __BYTE_ORDER == __LITTLE_ENDIAN + { SPA_AUDIO_FORMAT_S16, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16", NULL }, + { SPA_AUDIO_FORMAT_S16_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16OE", NULL }, + { SPA_AUDIO_FORMAT_U16, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16", NULL }, + { SPA_AUDIO_FORMAT_U16_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16OE", NULL }, + { SPA_AUDIO_FORMAT_S24_32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32", NULL }, + { SPA_AUDIO_FORMAT_S24_32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32OE", NULL }, + { SPA_AUDIO_FORMAT_U24_32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32", NULL }, + { SPA_AUDIO_FORMAT_U24_32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32OE", NULL }, + { SPA_AUDIO_FORMAT_S32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32", NULL }, + { SPA_AUDIO_FORMAT_S32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32OE", NULL }, + { SPA_AUDIO_FORMAT_U32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32", NULL }, + { SPA_AUDIO_FORMAT_U32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32OE", NULL }, + { SPA_AUDIO_FORMAT_S24, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24", NULL }, + { SPA_AUDIO_FORMAT_S24_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24OE", NULL }, + { SPA_AUDIO_FORMAT_U24, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24", NULL }, + { SPA_AUDIO_FORMAT_U24_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24OE", NULL }, + { SPA_AUDIO_FORMAT_S20, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20", NULL }, + { SPA_AUDIO_FORMAT_S20_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20OE", NULL }, + { SPA_AUDIO_FORMAT_U20, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20", NULL }, + { SPA_AUDIO_FORMAT_U20_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20OE", NULL }, + { SPA_AUDIO_FORMAT_S18, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18", NULL }, + { SPA_AUDIO_FORMAT_S18_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18OE", NULL }, + { SPA_AUDIO_FORMAT_U18, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18", NULL }, + { SPA_AUDIO_FORMAT_U18_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18OE", NULL }, + { SPA_AUDIO_FORMAT_F32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32", NULL }, + { SPA_AUDIO_FORMAT_F32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32OE", NULL }, + { SPA_AUDIO_FORMAT_F64, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64", NULL }, + { SPA_AUDIO_FORMAT_F64_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64OE", NULL }, +#endif + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_AudioFlags SPA_TYPE_INFO_FLAGS_BASE "AudioFlags" +#define SPA_TYPE_INFO_AUDIO_FLAGS_BASE SPA_TYPE_INFO_AudioFlags ":" + +static const struct spa_type_info spa_type_audio_flags[] = { + { SPA_AUDIO_FLAG_NONE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FLAGS_BASE "none", NULL }, + { SPA_AUDIO_FLAG_UNPOSITIONED, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FLAGS_BASE "unpositioned", NULL }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_AudioChannel SPA_TYPE_INFO_ENUM_BASE "AudioChannel" +#define SPA_TYPE_INFO_AUDIO_CHANNEL_BASE SPA_TYPE_INFO_AudioChannel ":" + +static const struct spa_type_info spa_type_audio_channel[] = { + { SPA_AUDIO_CHANNEL_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "UNK", NULL }, + { SPA_AUDIO_CHANNEL_NA, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "NA", NULL }, + { SPA_AUDIO_CHANNEL_MONO, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "MONO", NULL }, + { SPA_AUDIO_CHANNEL_FL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FL", NULL }, + { SPA_AUDIO_CHANNEL_FR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FR", NULL }, + { SPA_AUDIO_CHANNEL_FC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FC", NULL }, + { SPA_AUDIO_CHANNEL_LFE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "LFE", NULL }, + { SPA_AUDIO_CHANNEL_SL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "SL", NULL }, + { SPA_AUDIO_CHANNEL_SR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "SR", NULL }, + { SPA_AUDIO_CHANNEL_FLC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FLC", NULL }, + { SPA_AUDIO_CHANNEL_FRC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FRC", NULL }, + { SPA_AUDIO_CHANNEL_RC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RC", NULL }, + { SPA_AUDIO_CHANNEL_RL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RL", NULL }, + { SPA_AUDIO_CHANNEL_RR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RR", NULL }, + { SPA_AUDIO_CHANNEL_TC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TC", NULL }, + { SPA_AUDIO_CHANNEL_TFL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TFL", NULL }, + { SPA_AUDIO_CHANNEL_TFC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TFC", NULL }, + { SPA_AUDIO_CHANNEL_TFR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TFR", NULL }, + { SPA_AUDIO_CHANNEL_TRL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TRL", NULL }, + { SPA_AUDIO_CHANNEL_TRC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TRC", NULL }, + { SPA_AUDIO_CHANNEL_TRR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TRR", NULL }, + { SPA_AUDIO_CHANNEL_RLC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RLC", NULL }, + { SPA_AUDIO_CHANNEL_RRC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RRC", NULL }, + { SPA_AUDIO_CHANNEL_FLW, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FLW", NULL }, + { SPA_AUDIO_CHANNEL_FRW, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FRW", NULL }, + { SPA_AUDIO_CHANNEL_LFE2, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "LFE2", NULL }, + { SPA_AUDIO_CHANNEL_FLH, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FLH", NULL }, + { SPA_AUDIO_CHANNEL_FCH, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FCH", NULL }, + { SPA_AUDIO_CHANNEL_FRH, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FRH", NULL }, + { SPA_AUDIO_CHANNEL_TFLC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TFLC", NULL }, + { SPA_AUDIO_CHANNEL_TFRC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TFRC", NULL }, + { SPA_AUDIO_CHANNEL_TSL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TSL", NULL }, + { SPA_AUDIO_CHANNEL_TSR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TSR", NULL }, + { SPA_AUDIO_CHANNEL_LLFE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "LLFR", NULL }, + { SPA_AUDIO_CHANNEL_RLFE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RLFE", NULL }, + { SPA_AUDIO_CHANNEL_BC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "BC", NULL }, + { SPA_AUDIO_CHANNEL_BLC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "BLC", NULL }, + { SPA_AUDIO_CHANNEL_BRC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "BRC", NULL }, + + { SPA_AUDIO_CHANNEL_AUX0, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX0", NULL }, + { SPA_AUDIO_CHANNEL_AUX1, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX1", NULL }, + { SPA_AUDIO_CHANNEL_AUX2, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX2", NULL }, + { SPA_AUDIO_CHANNEL_AUX3, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX3", NULL }, + { SPA_AUDIO_CHANNEL_AUX4, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX4", NULL }, + { SPA_AUDIO_CHANNEL_AUX5, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX5", NULL }, + { SPA_AUDIO_CHANNEL_AUX6, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX6", NULL }, + { SPA_AUDIO_CHANNEL_AUX7, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX7", NULL }, + { SPA_AUDIO_CHANNEL_AUX8, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX8", NULL }, + { SPA_AUDIO_CHANNEL_AUX9, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX9", NULL }, + { SPA_AUDIO_CHANNEL_AUX10, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX10", NULL }, + { SPA_AUDIO_CHANNEL_AUX11, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX11", NULL }, + { SPA_AUDIO_CHANNEL_AUX12, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX12", NULL }, + { SPA_AUDIO_CHANNEL_AUX13, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX13", NULL }, + { SPA_AUDIO_CHANNEL_AUX14, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX14", NULL }, + { SPA_AUDIO_CHANNEL_AUX15, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX15", NULL }, + { SPA_AUDIO_CHANNEL_AUX16, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX16", NULL }, + { SPA_AUDIO_CHANNEL_AUX17, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX17", NULL }, + { SPA_AUDIO_CHANNEL_AUX18, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX18", NULL }, + { SPA_AUDIO_CHANNEL_AUX19, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX19", NULL }, + { SPA_AUDIO_CHANNEL_AUX20, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX20", NULL }, + { SPA_AUDIO_CHANNEL_AUX21, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX21", NULL }, + { SPA_AUDIO_CHANNEL_AUX22, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX22", NULL }, + { SPA_AUDIO_CHANNEL_AUX23, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX23", NULL }, + { SPA_AUDIO_CHANNEL_AUX24, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX24", NULL }, + { SPA_AUDIO_CHANNEL_AUX25, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX25", NULL }, + { SPA_AUDIO_CHANNEL_AUX26, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX26", NULL }, + { SPA_AUDIO_CHANNEL_AUX27, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX27", NULL }, + { SPA_AUDIO_CHANNEL_AUX28, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX28", NULL }, + { SPA_AUDIO_CHANNEL_AUX29, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX29", NULL }, + { SPA_AUDIO_CHANNEL_AUX30, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX30", NULL }, + { SPA_AUDIO_CHANNEL_AUX31, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX31", NULL }, + { SPA_AUDIO_CHANNEL_AUX32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX32", NULL }, + { SPA_AUDIO_CHANNEL_AUX33, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX33", NULL }, + { SPA_AUDIO_CHANNEL_AUX34, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX34", NULL }, + { SPA_AUDIO_CHANNEL_AUX35, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX35", NULL }, + { SPA_AUDIO_CHANNEL_AUX36, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX36", NULL }, + { SPA_AUDIO_CHANNEL_AUX37, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX37", NULL }, + { SPA_AUDIO_CHANNEL_AUX38, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX38", NULL }, + { SPA_AUDIO_CHANNEL_AUX39, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX39", NULL }, + { SPA_AUDIO_CHANNEL_AUX40, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX40", NULL }, + { SPA_AUDIO_CHANNEL_AUX41, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX41", NULL }, + { SPA_AUDIO_CHANNEL_AUX42, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX42", NULL }, + { SPA_AUDIO_CHANNEL_AUX43, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX43", NULL }, + { SPA_AUDIO_CHANNEL_AUX44, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX44", NULL }, + { SPA_AUDIO_CHANNEL_AUX45, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX45", NULL }, + { SPA_AUDIO_CHANNEL_AUX46, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX46", NULL }, + { SPA_AUDIO_CHANNEL_AUX47, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX47", NULL }, + { SPA_AUDIO_CHANNEL_AUX48, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX48", NULL }, + { SPA_AUDIO_CHANNEL_AUX49, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX49", NULL }, + { SPA_AUDIO_CHANNEL_AUX50, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX50", NULL }, + { SPA_AUDIO_CHANNEL_AUX51, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX51", NULL }, + { SPA_AUDIO_CHANNEL_AUX52, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX52", NULL }, + { SPA_AUDIO_CHANNEL_AUX53, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX53", NULL }, + { SPA_AUDIO_CHANNEL_AUX54, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX54", NULL }, + { SPA_AUDIO_CHANNEL_AUX55, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX55", NULL }, + { SPA_AUDIO_CHANNEL_AUX56, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX56", NULL }, + { SPA_AUDIO_CHANNEL_AUX57, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX57", NULL }, + { SPA_AUDIO_CHANNEL_AUX58, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX58", NULL }, + { SPA_AUDIO_CHANNEL_AUX59, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX59", NULL }, + { SPA_AUDIO_CHANNEL_AUX60, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX60", NULL }, + { SPA_AUDIO_CHANNEL_AUX61, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX61", NULL }, + { SPA_AUDIO_CHANNEL_AUX62, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX62", NULL }, + { SPA_AUDIO_CHANNEL_AUX63, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX63", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_RAW_RAW_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw.h new file mode 100644 index 0000000000000..54052f75311a9 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/raw.h @@ -0,0 +1,309 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_RAW_H +#define SPA_AUDIO_RAW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) && !defined(AIX) +#include +#endif + +#if defined(AIX) +#include +#define __BIG_ENDIAN BIG_ENDIAN +#define __BYTE_ORDER BIG_ENDIAN +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#define SPA_AUDIO_MAX_CHANNELS 64u + +enum spa_audio_format { + SPA_AUDIO_FORMAT_UNKNOWN, + SPA_AUDIO_FORMAT_ENCODED, + + /* interleaved formats */ + SPA_AUDIO_FORMAT_START_Interleaved = 0x100, + SPA_AUDIO_FORMAT_S8, + SPA_AUDIO_FORMAT_U8, + SPA_AUDIO_FORMAT_S16_LE, + SPA_AUDIO_FORMAT_S16_BE, + SPA_AUDIO_FORMAT_U16_LE, + SPA_AUDIO_FORMAT_U16_BE, + SPA_AUDIO_FORMAT_S24_32_LE, + SPA_AUDIO_FORMAT_S24_32_BE, + SPA_AUDIO_FORMAT_U24_32_LE, + SPA_AUDIO_FORMAT_U24_32_BE, + SPA_AUDIO_FORMAT_S32_LE, + SPA_AUDIO_FORMAT_S32_BE, + SPA_AUDIO_FORMAT_U32_LE, + SPA_AUDIO_FORMAT_U32_BE, + SPA_AUDIO_FORMAT_S24_LE, + SPA_AUDIO_FORMAT_S24_BE, + SPA_AUDIO_FORMAT_U24_LE, + SPA_AUDIO_FORMAT_U24_BE, + SPA_AUDIO_FORMAT_S20_LE, + SPA_AUDIO_FORMAT_S20_BE, + SPA_AUDIO_FORMAT_U20_LE, + SPA_AUDIO_FORMAT_U20_BE, + SPA_AUDIO_FORMAT_S18_LE, + SPA_AUDIO_FORMAT_S18_BE, + SPA_AUDIO_FORMAT_U18_LE, + SPA_AUDIO_FORMAT_U18_BE, + SPA_AUDIO_FORMAT_F32_LE, + SPA_AUDIO_FORMAT_F32_BE, + SPA_AUDIO_FORMAT_F64_LE, + SPA_AUDIO_FORMAT_F64_BE, + + SPA_AUDIO_FORMAT_ULAW, + SPA_AUDIO_FORMAT_ALAW, + + /* planar formats */ + SPA_AUDIO_FORMAT_START_Planar = 0x200, + SPA_AUDIO_FORMAT_U8P, + SPA_AUDIO_FORMAT_S16P, + SPA_AUDIO_FORMAT_S24_32P, + SPA_AUDIO_FORMAT_S32P, + SPA_AUDIO_FORMAT_S24P, + SPA_AUDIO_FORMAT_F32P, + SPA_AUDIO_FORMAT_F64P, + SPA_AUDIO_FORMAT_S8P, + + /* other formats start here */ + SPA_AUDIO_FORMAT_START_Other = 0x400, + + /* Aliases */ + + /* DSP formats */ + SPA_AUDIO_FORMAT_DSP_S32 = SPA_AUDIO_FORMAT_S24_32P, + SPA_AUDIO_FORMAT_DSP_F32 = SPA_AUDIO_FORMAT_F32P, + SPA_AUDIO_FORMAT_DSP_F64 = SPA_AUDIO_FORMAT_F64P, + + /* native endian */ +#if __BYTE_ORDER == __BIG_ENDIAN + SPA_AUDIO_FORMAT_S16 = SPA_AUDIO_FORMAT_S16_BE, + SPA_AUDIO_FORMAT_U16 = SPA_AUDIO_FORMAT_U16_BE, + SPA_AUDIO_FORMAT_S24_32 = SPA_AUDIO_FORMAT_S24_32_BE, + SPA_AUDIO_FORMAT_U24_32 = SPA_AUDIO_FORMAT_U24_32_BE, + SPA_AUDIO_FORMAT_S32 = SPA_AUDIO_FORMAT_S32_BE, + SPA_AUDIO_FORMAT_U32 = SPA_AUDIO_FORMAT_U32_BE, + SPA_AUDIO_FORMAT_S24 = SPA_AUDIO_FORMAT_S24_BE, + SPA_AUDIO_FORMAT_U24 = SPA_AUDIO_FORMAT_U24_BE, + SPA_AUDIO_FORMAT_S20 = SPA_AUDIO_FORMAT_S20_BE, + SPA_AUDIO_FORMAT_U20 = SPA_AUDIO_FORMAT_U20_BE, + SPA_AUDIO_FORMAT_S18 = SPA_AUDIO_FORMAT_S18_BE, + SPA_AUDIO_FORMAT_U18 = SPA_AUDIO_FORMAT_U18_BE, + SPA_AUDIO_FORMAT_F32 = SPA_AUDIO_FORMAT_F32_BE, + SPA_AUDIO_FORMAT_F64 = SPA_AUDIO_FORMAT_F64_BE, + SPA_AUDIO_FORMAT_S16_OE = SPA_AUDIO_FORMAT_S16_LE, + SPA_AUDIO_FORMAT_U16_OE = SPA_AUDIO_FORMAT_U16_LE, + SPA_AUDIO_FORMAT_S24_32_OE = SPA_AUDIO_FORMAT_S24_32_LE, + SPA_AUDIO_FORMAT_U24_32_OE = SPA_AUDIO_FORMAT_U24_32_LE, + SPA_AUDIO_FORMAT_S32_OE = SPA_AUDIO_FORMAT_S32_LE, + SPA_AUDIO_FORMAT_U32_OE = SPA_AUDIO_FORMAT_U32_LE, + SPA_AUDIO_FORMAT_S24_OE = SPA_AUDIO_FORMAT_S24_LE, + SPA_AUDIO_FORMAT_U24_OE = SPA_AUDIO_FORMAT_U24_LE, + SPA_AUDIO_FORMAT_S20_OE = SPA_AUDIO_FORMAT_S20_LE, + SPA_AUDIO_FORMAT_U20_OE = SPA_AUDIO_FORMAT_U20_LE, + SPA_AUDIO_FORMAT_S18_OE = SPA_AUDIO_FORMAT_S18_LE, + SPA_AUDIO_FORMAT_U18_OE = SPA_AUDIO_FORMAT_U18_LE, + SPA_AUDIO_FORMAT_F32_OE = SPA_AUDIO_FORMAT_F32_LE, + SPA_AUDIO_FORMAT_F64_OE = SPA_AUDIO_FORMAT_F64_LE, +#elif __BYTE_ORDER == __LITTLE_ENDIAN + SPA_AUDIO_FORMAT_S16 = SPA_AUDIO_FORMAT_S16_LE, + SPA_AUDIO_FORMAT_U16 = SPA_AUDIO_FORMAT_U16_LE, + SPA_AUDIO_FORMAT_S24_32 = SPA_AUDIO_FORMAT_S24_32_LE, + SPA_AUDIO_FORMAT_U24_32 = SPA_AUDIO_FORMAT_U24_32_LE, + SPA_AUDIO_FORMAT_S32 = SPA_AUDIO_FORMAT_S32_LE, + SPA_AUDIO_FORMAT_U32 = SPA_AUDIO_FORMAT_U32_LE, + SPA_AUDIO_FORMAT_S24 = SPA_AUDIO_FORMAT_S24_LE, + SPA_AUDIO_FORMAT_U24 = SPA_AUDIO_FORMAT_U24_LE, + SPA_AUDIO_FORMAT_S20 = SPA_AUDIO_FORMAT_S20_LE, + SPA_AUDIO_FORMAT_U20 = SPA_AUDIO_FORMAT_U20_LE, + SPA_AUDIO_FORMAT_S18 = SPA_AUDIO_FORMAT_S18_LE, + SPA_AUDIO_FORMAT_U18 = SPA_AUDIO_FORMAT_U18_LE, + SPA_AUDIO_FORMAT_F32 = SPA_AUDIO_FORMAT_F32_LE, + SPA_AUDIO_FORMAT_F64 = SPA_AUDIO_FORMAT_F64_LE, + SPA_AUDIO_FORMAT_S16_OE = SPA_AUDIO_FORMAT_S16_BE, + SPA_AUDIO_FORMAT_U16_OE = SPA_AUDIO_FORMAT_U16_BE, + SPA_AUDIO_FORMAT_S24_32_OE = SPA_AUDIO_FORMAT_S24_32_BE, + SPA_AUDIO_FORMAT_U24_32_OE = SPA_AUDIO_FORMAT_U24_32_BE, + SPA_AUDIO_FORMAT_S32_OE = SPA_AUDIO_FORMAT_S32_BE, + SPA_AUDIO_FORMAT_U32_OE = SPA_AUDIO_FORMAT_U32_BE, + SPA_AUDIO_FORMAT_S24_OE = SPA_AUDIO_FORMAT_S24_BE, + SPA_AUDIO_FORMAT_U24_OE = SPA_AUDIO_FORMAT_U24_BE, + SPA_AUDIO_FORMAT_S20_OE = SPA_AUDIO_FORMAT_S20_BE, + SPA_AUDIO_FORMAT_U20_OE = SPA_AUDIO_FORMAT_U20_BE, + SPA_AUDIO_FORMAT_S18_OE = SPA_AUDIO_FORMAT_S18_BE, + SPA_AUDIO_FORMAT_U18_OE = SPA_AUDIO_FORMAT_U18_BE, + SPA_AUDIO_FORMAT_F32_OE = SPA_AUDIO_FORMAT_F32_BE, + SPA_AUDIO_FORMAT_F64_OE = SPA_AUDIO_FORMAT_F64_BE, +#endif +}; + +#define SPA_AUDIO_FORMAT_IS_INTERLEAVED(fmt) ((fmt) > SPA_AUDIO_FORMAT_START_Interleaved && (fmt) < SPA_AUDIO_FORMAT_START_Planar) +#define SPA_AUDIO_FORMAT_IS_PLANAR(fmt) ((fmt) > SPA_AUDIO_FORMAT_START_Planar && (fmt) < SPA_AUDIO_FORMAT_START_Other) + +enum spa_audio_channel { + SPA_AUDIO_CHANNEL_UNKNOWN, /**< unspecified */ + SPA_AUDIO_CHANNEL_NA, /**< N/A, silent */ + + SPA_AUDIO_CHANNEL_MONO, /**< mono stream */ + + SPA_AUDIO_CHANNEL_FL, /**< front left */ + SPA_AUDIO_CHANNEL_FR, /**< front right */ + SPA_AUDIO_CHANNEL_FC, /**< front center */ + SPA_AUDIO_CHANNEL_LFE, /**< LFE */ + SPA_AUDIO_CHANNEL_SL, /**< side left */ + SPA_AUDIO_CHANNEL_SR, /**< side right */ + SPA_AUDIO_CHANNEL_FLC, /**< front left center */ + SPA_AUDIO_CHANNEL_FRC, /**< front right center */ + SPA_AUDIO_CHANNEL_RC, /**< rear center */ + SPA_AUDIO_CHANNEL_RL, /**< rear left */ + SPA_AUDIO_CHANNEL_RR, /**< rear right */ + SPA_AUDIO_CHANNEL_TC, /**< top center */ + SPA_AUDIO_CHANNEL_TFL, /**< top front left */ + SPA_AUDIO_CHANNEL_TFC, /**< top front center */ + SPA_AUDIO_CHANNEL_TFR, /**< top front right */ + SPA_AUDIO_CHANNEL_TRL, /**< top rear left */ + SPA_AUDIO_CHANNEL_TRC, /**< top rear center */ + SPA_AUDIO_CHANNEL_TRR, /**< top rear right */ + SPA_AUDIO_CHANNEL_RLC, /**< rear left center */ + SPA_AUDIO_CHANNEL_RRC, /**< rear right center */ + SPA_AUDIO_CHANNEL_FLW, /**< front left wide */ + SPA_AUDIO_CHANNEL_FRW, /**< front right wide */ + SPA_AUDIO_CHANNEL_LFE2, /**< LFE 2 */ + SPA_AUDIO_CHANNEL_FLH, /**< front left high */ + SPA_AUDIO_CHANNEL_FCH, /**< front center high */ + SPA_AUDIO_CHANNEL_FRH, /**< front right high */ + SPA_AUDIO_CHANNEL_TFLC, /**< top front left center */ + SPA_AUDIO_CHANNEL_TFRC, /**< top front right center */ + SPA_AUDIO_CHANNEL_TSL, /**< top side left */ + SPA_AUDIO_CHANNEL_TSR, /**< top side right */ + SPA_AUDIO_CHANNEL_LLFE, /**< left LFE */ + SPA_AUDIO_CHANNEL_RLFE, /**< right LFE */ + SPA_AUDIO_CHANNEL_BC, /**< bottom center */ + SPA_AUDIO_CHANNEL_BLC, /**< bottom left center */ + SPA_AUDIO_CHANNEL_BRC, /**< bottom right center */ + + SPA_AUDIO_CHANNEL_START_Aux = 0x1000, /**< aux channels */ + SPA_AUDIO_CHANNEL_AUX0 = SPA_AUDIO_CHANNEL_START_Aux, + SPA_AUDIO_CHANNEL_AUX1, + SPA_AUDIO_CHANNEL_AUX2, + SPA_AUDIO_CHANNEL_AUX3, + SPA_AUDIO_CHANNEL_AUX4, + SPA_AUDIO_CHANNEL_AUX5, + SPA_AUDIO_CHANNEL_AUX6, + SPA_AUDIO_CHANNEL_AUX7, + SPA_AUDIO_CHANNEL_AUX8, + SPA_AUDIO_CHANNEL_AUX9, + SPA_AUDIO_CHANNEL_AUX10, + SPA_AUDIO_CHANNEL_AUX11, + SPA_AUDIO_CHANNEL_AUX12, + SPA_AUDIO_CHANNEL_AUX13, + SPA_AUDIO_CHANNEL_AUX14, + SPA_AUDIO_CHANNEL_AUX15, + SPA_AUDIO_CHANNEL_AUX16, + SPA_AUDIO_CHANNEL_AUX17, + SPA_AUDIO_CHANNEL_AUX18, + SPA_AUDIO_CHANNEL_AUX19, + SPA_AUDIO_CHANNEL_AUX20, + SPA_AUDIO_CHANNEL_AUX21, + SPA_AUDIO_CHANNEL_AUX22, + SPA_AUDIO_CHANNEL_AUX23, + SPA_AUDIO_CHANNEL_AUX24, + SPA_AUDIO_CHANNEL_AUX25, + SPA_AUDIO_CHANNEL_AUX26, + SPA_AUDIO_CHANNEL_AUX27, + SPA_AUDIO_CHANNEL_AUX28, + SPA_AUDIO_CHANNEL_AUX29, + SPA_AUDIO_CHANNEL_AUX30, + SPA_AUDIO_CHANNEL_AUX31, + SPA_AUDIO_CHANNEL_AUX32, + SPA_AUDIO_CHANNEL_AUX33, + SPA_AUDIO_CHANNEL_AUX34, + SPA_AUDIO_CHANNEL_AUX35, + SPA_AUDIO_CHANNEL_AUX36, + SPA_AUDIO_CHANNEL_AUX37, + SPA_AUDIO_CHANNEL_AUX38, + SPA_AUDIO_CHANNEL_AUX39, + SPA_AUDIO_CHANNEL_AUX40, + SPA_AUDIO_CHANNEL_AUX41, + SPA_AUDIO_CHANNEL_AUX42, + SPA_AUDIO_CHANNEL_AUX43, + SPA_AUDIO_CHANNEL_AUX44, + SPA_AUDIO_CHANNEL_AUX45, + SPA_AUDIO_CHANNEL_AUX46, + SPA_AUDIO_CHANNEL_AUX47, + SPA_AUDIO_CHANNEL_AUX48, + SPA_AUDIO_CHANNEL_AUX49, + SPA_AUDIO_CHANNEL_AUX50, + SPA_AUDIO_CHANNEL_AUX51, + SPA_AUDIO_CHANNEL_AUX52, + SPA_AUDIO_CHANNEL_AUX53, + SPA_AUDIO_CHANNEL_AUX54, + SPA_AUDIO_CHANNEL_AUX55, + SPA_AUDIO_CHANNEL_AUX56, + SPA_AUDIO_CHANNEL_AUX57, + SPA_AUDIO_CHANNEL_AUX58, + SPA_AUDIO_CHANNEL_AUX59, + SPA_AUDIO_CHANNEL_AUX60, + SPA_AUDIO_CHANNEL_AUX61, + SPA_AUDIO_CHANNEL_AUX62, + SPA_AUDIO_CHANNEL_AUX63, + + SPA_AUDIO_CHANNEL_LAST_Aux = 0x1fff, /**< aux channels */ + + SPA_AUDIO_CHANNEL_START_Custom = 0x10000, +}; + +enum spa_audio_volume_ramp_scale { + SPA_AUDIO_VOLUME_RAMP_INVALID, + SPA_AUDIO_VOLUME_RAMP_LINEAR, + SPA_AUDIO_VOLUME_RAMP_CUBIC, +}; + +/** Extra audio flags */ +#define SPA_AUDIO_FLAG_NONE (0) /*< no valid flag */ +#define SPA_AUDIO_FLAG_UNPOSITIONED (1 << 0) /*< the position array explicitly + * contains unpositioned channels. */ +/** Audio information description */ +struct spa_audio_info_raw { + enum spa_audio_format format; /*< format, one of enum spa_audio_format */ + uint32_t flags; /*< extra flags */ + uint32_t rate; /*< sample rate */ + uint32_t channels; /*< number of channels */ + uint32_t position[SPA_AUDIO_MAX_CHANNELS]; /*< channel position from enum spa_audio_channel */ +}; + +#define SPA_AUDIO_INFO_RAW_INIT(...) ((struct spa_audio_info_raw) { __VA_ARGS__ }) + +#define SPA_KEY_AUDIO_FORMAT "audio.format" /**< an audio format as string, + * Ex. "S16LE" */ +#define SPA_KEY_AUDIO_CHANNEL "audio.channel" /**< an audio channel as string, + * Ex. "FL" */ +#define SPA_KEY_AUDIO_CHANNELS "audio.channels" /**< an audio channel count as int */ +#define SPA_KEY_AUDIO_RATE "audio.rate" /**< an audio sample rate as int */ +#define SPA_KEY_AUDIO_POSITION "audio.position" /**< channel positions as comma separated list + * of channels ex. "FL,FR" */ +#define SPA_KEY_AUDIO_ALLOWED_RATES "audio.allowed-rates" /**< a list of allowed samplerates + * ex. "[ 44100 48000 ]" */ +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_RAW_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/type-info.h new file mode 100644 index 0000000000000..8a3aa49aabfd8 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/type-info.h @@ -0,0 +1,15 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_TYPES_H +#define SPA_AUDIO_TYPES_H + +#include +#include +#include +#include +#include +#include + +#endif /* SPA_AUDIO_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma-types.h new file mode 100644 index 0000000000000..0309223ac7008 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma-types.h @@ -0,0 +1,37 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_WMA_TYPES_H +#define SPA_AUDIO_WMA_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define SPA_TYPE_INFO_AudioWMAProfile SPA_TYPE_INFO_ENUM_BASE "AudioWMAProfile" +#define SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE SPA_TYPE_INFO_AudioWMAProfile ":" + +static const struct spa_type_info spa_type_audio_wma_profile[] = { + { SPA_AUDIO_WMA_PROFILE_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE "UNKNOWN", NULL }, + { SPA_AUDIO_WMA_PROFILE_WMA7, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE "WMA7", NULL }, + { SPA_AUDIO_WMA_PROFILE_WMA8, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE "WMA8", NULL }, + { SPA_AUDIO_WMA_PROFILE_WMA9, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE "WMA9", NULL }, + { SPA_AUDIO_WMA_PROFILE_WMA10, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE "WMA10", NULL }, + { SPA_AUDIO_WMA_PROFILE_WMA9_PRO, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE "WMA9-Pro", NULL }, + { SPA_AUDIO_WMA_PROFILE_WMA9_LOSSLESS, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE "WMA9-Lossless", NULL }, + { SPA_AUDIO_WMA_PROFILE_WMA10_LOSSLESS, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_WMA_PROFILE_BASE "WMA10-Lossless", NULL }, + { 0, 0, NULL, NULL }, +}; +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_WMA_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma.h new file mode 100644 index 0000000000000..84a78a7e12d05 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/audio/wma.h @@ -0,0 +1,47 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_AUDIO_WMA_H +#define SPA_AUDIO_WMA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +enum spa_audio_wma_profile { + SPA_AUDIO_WMA_PROFILE_UNKNOWN, + + SPA_AUDIO_WMA_PROFILE_WMA7, + SPA_AUDIO_WMA_PROFILE_WMA8, + SPA_AUDIO_WMA_PROFILE_WMA9, + SPA_AUDIO_WMA_PROFILE_WMA10, + SPA_AUDIO_WMA_PROFILE_WMA9_PRO, + SPA_AUDIO_WMA_PROFILE_WMA9_LOSSLESS, + SPA_AUDIO_WMA_PROFILE_WMA10_LOSSLESS, + + SPA_AUDIO_WMA_PROFILE_CUSTOM = 0x10000, +}; + +struct spa_audio_info_wma { + uint32_t rate; /*< sample rate */ + uint32_t channels; /*< number of channels */ + uint32_t bitrate; /*< stream bitrate */ + uint32_t block_align; /*< block alignment */ + enum spa_audio_wma_profile profile; /*< WMA profile */ + +}; + +#define SPA_AUDIO_INFO_WMA_INIT(...) ((struct spa_audio_info_wma) { __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_AUDIO_WMA_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/audio.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/audio.h new file mode 100644 index 0000000000000..8561a00aebdbe --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/audio.h @@ -0,0 +1,54 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_BLUETOOTH_AUDIO_H +#define SPA_BLUETOOTH_AUDIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ +enum spa_bluetooth_audio_codec { + SPA_BLUETOOTH_AUDIO_CODEC_START, + + /* A2DP */ + SPA_BLUETOOTH_AUDIO_CODEC_SBC, + SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, + SPA_BLUETOOTH_AUDIO_CODEC_MPEG, + SPA_BLUETOOTH_AUDIO_CODEC_AAC, + SPA_BLUETOOTH_AUDIO_CODEC_APTX, + SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD, + SPA_BLUETOOTH_AUDIO_CODEC_LDAC, + SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL, + SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX, + SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM, + SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM_DUPLEX, + SPA_BLUETOOTH_AUDIO_CODEC_LC3PLUS_HR, + SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05, + SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_51, + SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_71, + SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_DUPLEX, + SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_PRO, + + /* HFP */ + SPA_BLUETOOTH_AUDIO_CODEC_CVSD = 0x100, + SPA_BLUETOOTH_AUDIO_CODEC_MSBC, + + /* BAP */ + SPA_BLUETOOTH_AUDIO_CODEC_LC3 = 0x200, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_BLUETOOTH_AUDIO_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/type-info.h new file mode 100644 index 0000000000000..a7ce08246c27b --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/bluetooth/type-info.h @@ -0,0 +1,58 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_BLUETOOTH_TYPES_H +#define SPA_BLUETOOTH_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +#define SPA_TYPE_INFO_BluetoothAudioCodec SPA_TYPE_INFO_ENUM_BASE "BluetoothAudioCodec" +#define SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE SPA_TYPE_INFO_BluetoothAudioCodec ":" + +static const struct spa_type_info spa_type_bluetooth_audio_codec[] = { + /* A2DP */ + { SPA_BLUETOOTH_AUDIO_CODEC_SBC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "sbc", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "sbc_xq", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_MPEG, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "mpeg", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_AAC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aac", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_APTX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_hd", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_LDAC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "ldac", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_ll", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_ll_duplex", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "faststream", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM_DUPLEX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "faststream_duplex", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_LC3PLUS_HR, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lc3plus_hr", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_51, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05_51", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_71, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05_71", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_DUPLEX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05_duplex", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_PRO, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "opus_05_pro", NULL }, + + { SPA_BLUETOOTH_AUDIO_CODEC_CVSD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "cvsd", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_MSBC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "msbc", NULL }, + + { SPA_BLUETOOTH_AUDIO_CODEC_LC3, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lc3", NULL }, + + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_BLUETOOTH_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers-types.h new file mode 100644 index 0000000000000..987d75a1669f6 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers-types.h @@ -0,0 +1,70 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_BUFFERS_TYPES_H +#define SPA_PARAM_BUFFERS_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include + +#include + +#define SPA_TYPE_INFO_PARAM_Meta SPA_TYPE_INFO_PARAM_BASE "Meta" +#define SPA_TYPE_INFO_PARAM_META_BASE SPA_TYPE_INFO_PARAM_Meta ":" + +static const struct spa_type_info spa_type_param_meta[] = { + { SPA_PARAM_META_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_META_BASE, spa_type_param }, + { SPA_PARAM_META_type, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_META_BASE "type", spa_type_meta_type }, + { SPA_PARAM_META_size, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_META_BASE "size", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** Base for parameters that describe IO areas to exchange data, + * control and properties with a node. + */ +#define SPA_TYPE_INFO_PARAM_IO SPA_TYPE_INFO_PARAM_BASE "IO" +#define SPA_TYPE_INFO_PARAM_IO_BASE SPA_TYPE_INFO_PARAM_IO ":" + +static const struct spa_type_info spa_type_param_io[] = { + { SPA_PARAM_IO_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_IO_BASE, spa_type_param, }, + { SPA_PARAM_IO_id, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_IO_BASE "id", spa_type_io }, + { SPA_PARAM_IO_size, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_IO_BASE "size", NULL }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_PARAM_Buffers SPA_TYPE_INFO_PARAM_BASE "Buffers" +#define SPA_TYPE_INFO_PARAM_BUFFERS_BASE SPA_TYPE_INFO_PARAM_Buffers ":" + +#define SPA_TYPE_INFO_PARAM_BlockInfo SPA_TYPE_INFO_PARAM_BUFFERS_BASE "BlockInfo" +#define SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE SPA_TYPE_INFO_PARAM_BlockInfo ":" + +static const struct spa_type_info spa_type_param_buffers[] = { + { SPA_PARAM_BUFFERS_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_BUFFERS_BASE, spa_type_param, }, + { SPA_PARAM_BUFFERS_buffers, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BUFFERS_BASE "buffers", NULL }, + { SPA_PARAM_BUFFERS_blocks, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BUFFERS_BASE "blocks", NULL }, + { SPA_PARAM_BUFFERS_size, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "size", NULL }, + { SPA_PARAM_BUFFERS_stride, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "stride", NULL }, + { SPA_PARAM_BUFFERS_align, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "align", NULL }, + { SPA_PARAM_BUFFERS_dataType, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "dataType", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_BUFFERS_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers.h new file mode 100644 index 0000000000000..6834c6e5e390a --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/buffers.h @@ -0,0 +1,52 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_BUFFERS_H +#define SPA_PARAM_BUFFERS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +/** properties for SPA_TYPE_OBJECT_ParamBuffers */ +enum spa_param_buffers { + SPA_PARAM_BUFFERS_START, + SPA_PARAM_BUFFERS_buffers, /**< number of buffers (Int) */ + SPA_PARAM_BUFFERS_blocks, /**< number of data blocks per buffer (Int) */ + SPA_PARAM_BUFFERS_size, /**< size of a data block memory (Int)*/ + SPA_PARAM_BUFFERS_stride, /**< stride of data block memory (Int) */ + SPA_PARAM_BUFFERS_align, /**< alignment of data block memory (Int) */ + SPA_PARAM_BUFFERS_dataType, /**< possible memory types (Int, mask of enum spa_data_type) */ +}; + +/** properties for SPA_TYPE_OBJECT_ParamMeta */ +enum spa_param_meta { + SPA_PARAM_META_START, + SPA_PARAM_META_type, /**< the metadata, one of enum spa_meta_type (Id enum spa_meta_type) */ + SPA_PARAM_META_size, /**< the expected maximum size the meta (Int) */ +}; + +/** properties for SPA_TYPE_OBJECT_ParamIO */ +enum spa_param_io { + SPA_PARAM_IO_START, + SPA_PARAM_IO_id, /**< type ID, uniquely identifies the io area (Id enum spa_io_type) */ + SPA_PARAM_IO_size, /**< size of the io area (Int) */ +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_BUFFERS_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/format-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/format-types.h new file mode 100644 index 0000000000000..a02aa3c4c3e87 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/format-types.h @@ -0,0 +1,172 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_FORMAT_TYPES_H +#define SPA_PARAM_FORMAT_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include + +#include +#include + +#define SPA_TYPE_INFO_Format SPA_TYPE_INFO_PARAM_BASE "Format" +#define SPA_TYPE_INFO_FORMAT_BASE SPA_TYPE_INFO_Format ":" + +#define SPA_TYPE_INFO_MediaType SPA_TYPE_INFO_ENUM_BASE "MediaType" +#define SPA_TYPE_INFO_MEDIA_TYPE_BASE SPA_TYPE_INFO_MediaType ":" + +static const struct spa_type_info spa_type_media_type[] = { + { SPA_MEDIA_TYPE_unknown, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "unknown", NULL }, + { SPA_MEDIA_TYPE_audio, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "audio", NULL }, + { SPA_MEDIA_TYPE_video, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "video", NULL }, + { SPA_MEDIA_TYPE_image, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "image", NULL }, + { SPA_MEDIA_TYPE_binary, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "binary", NULL }, + { SPA_MEDIA_TYPE_stream, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "stream", NULL }, + { SPA_MEDIA_TYPE_application, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "application", NULL }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_MediaSubtype SPA_TYPE_INFO_ENUM_BASE "MediaSubtype" +#define SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE SPA_TYPE_INFO_MediaSubtype ":" + +static const struct spa_type_info spa_type_media_subtype[] = { + { SPA_MEDIA_SUBTYPE_unknown, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "unknown", NULL }, + /* generic subtypes */ + { SPA_MEDIA_SUBTYPE_raw, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", NULL }, + { SPA_MEDIA_SUBTYPE_dsp, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "dsp", NULL }, + { SPA_MEDIA_SUBTYPE_iec958, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "iec958", NULL }, + { SPA_MEDIA_SUBTYPE_dsd, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "dsd", NULL }, + /* audio subtypes */ + { SPA_MEDIA_SUBTYPE_mp3, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mp3", NULL }, + { SPA_MEDIA_SUBTYPE_aac, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "aac", NULL }, + { SPA_MEDIA_SUBTYPE_vorbis, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "vorbis", NULL }, + { SPA_MEDIA_SUBTYPE_wma, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "wma", NULL }, + { SPA_MEDIA_SUBTYPE_ra, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "ra", NULL }, + { SPA_MEDIA_SUBTYPE_sbc, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "sbc", NULL }, + { SPA_MEDIA_SUBTYPE_adpcm, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "adpcm", NULL }, + { SPA_MEDIA_SUBTYPE_g723, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "g723", NULL }, + { SPA_MEDIA_SUBTYPE_g726, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "g726", NULL }, + { SPA_MEDIA_SUBTYPE_g729, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "g729", NULL }, + { SPA_MEDIA_SUBTYPE_amr, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "amr", NULL }, + { SPA_MEDIA_SUBTYPE_gsm, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "gsm", NULL }, + { SPA_MEDIA_SUBTYPE_alac, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "alac", NULL }, + { SPA_MEDIA_SUBTYPE_flac, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "flac", NULL }, + { SPA_MEDIA_SUBTYPE_ape, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "ape", NULL }, + { SPA_MEDIA_SUBTYPE_opus, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "opus", NULL }, + /* video subtypes */ + { SPA_MEDIA_SUBTYPE_h264, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "h264", NULL }, + { SPA_MEDIA_SUBTYPE_mjpg, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mjpg", NULL }, + { SPA_MEDIA_SUBTYPE_dv, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "dv", NULL }, + { SPA_MEDIA_SUBTYPE_mpegts, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpegts", NULL }, + { SPA_MEDIA_SUBTYPE_h263, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "h263", NULL }, + { SPA_MEDIA_SUBTYPE_mpeg1, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpeg1", NULL }, + { SPA_MEDIA_SUBTYPE_mpeg2, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpeg2", NULL }, + { SPA_MEDIA_SUBTYPE_mpeg4, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpeg4", NULL }, + { SPA_MEDIA_SUBTYPE_xvid, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "xvid", NULL }, + { SPA_MEDIA_SUBTYPE_vc1, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "vc1", NULL }, + { SPA_MEDIA_SUBTYPE_vp8, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "vp8", NULL }, + { SPA_MEDIA_SUBTYPE_vp9, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "vp9", NULL }, + { SPA_MEDIA_SUBTYPE_bayer, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "bayer", NULL }, + /* image subtypes */ + { SPA_MEDIA_SUBTYPE_jpeg, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "jpeg", NULL }, + /* stream subtypes */ + { SPA_MEDIA_SUBTYPE_midi, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "midi", NULL }, + /* application subtypes */ + { SPA_MEDIA_SUBTYPE_control, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "control", NULL }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_FormatAudio SPA_TYPE_INFO_FORMAT_BASE "Audio" +#define SPA_TYPE_INFO_FORMAT_AUDIO_BASE SPA_TYPE_INFO_FormatAudio ":" + +#define SPA_TYPE_INFO_FORMAT_AUDIO_AAC SPA_TYPE_INFO_FORMAT_AUDIO_BASE "AAC" +#define SPA_TYPE_INFO_FORMAT_AUDIO_AAC_BASE SPA_TYPE_INFO_FORMAT_AUDIO_AAC ":" +#define SPA_TYPE_INFO_FORMAT_AUDIO_WMA SPA_TYPE_INFO_FORMAT_AUDIO_BASE "WMA" +#define SPA_TYPE_INFO_FORMAT_AUDIO_WMA_BASE SPA_TYPE_INFO_FORMAT_AUDIO_WMA ":" +#define SPA_TYPE_INFO_FORMAT_AUDIO_AMR SPA_TYPE_INFO_FORMAT_AUDIO_BASE "AMR" +#define SPA_TYPE_INFO_FORMAT_AUDIO_AMR_BASE SPA_TYPE_INFO_FORMAT_AUDIO_AMR ":" + +#define SPA_TYPE_INFO_FormatVideo SPA_TYPE_INFO_FORMAT_BASE "Video" +#define SPA_TYPE_INFO_FORMAT_VIDEO_BASE SPA_TYPE_INFO_FormatVideo ":" + +#define SPA_TYPE_INFO_FORMAT_VIDEO_H264 SPA_TYPE_INFO_FORMAT_VIDEO_BASE "H264" +#define SPA_TYPE_INFO_FORMAT_VIDEO_H264_BASE SPA_TYPE_INFO_FORMAT_VIDEO_H264 ":" + +static const struct spa_type_info spa_type_format[] = { + { SPA_FORMAT_START, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_BASE, spa_type_param, }, + + { SPA_FORMAT_mediaType, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_BASE "mediaType", + spa_type_media_type, }, + { SPA_FORMAT_mediaSubtype, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_BASE "mediaSubtype", + spa_type_media_subtype, }, + + { SPA_FORMAT_AUDIO_format, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "format", + spa_type_audio_format }, + { SPA_FORMAT_AUDIO_flags, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "flags", + spa_type_audio_flags }, + { SPA_FORMAT_AUDIO_rate, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "rate", NULL }, + { SPA_FORMAT_AUDIO_channels, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "channels", NULL }, + { SPA_FORMAT_AUDIO_position, SPA_TYPE_Array, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "position", + spa_type_prop_channel_map }, + + { SPA_FORMAT_AUDIO_iec958Codec, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "iec958Codec", + spa_type_audio_iec958_codec }, + + { SPA_FORMAT_AUDIO_bitorder, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "bitorder", + spa_type_param_bitorder }, + { SPA_FORMAT_AUDIO_interleave, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "interleave", NULL }, + { SPA_FORMAT_AUDIO_bitrate, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "bitrate", NULL }, + { SPA_FORMAT_AUDIO_blockAlign, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "blockAlign", NULL }, + + { SPA_FORMAT_AUDIO_AAC_streamFormat, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_AAC_BASE "streamFormat", + spa_type_audio_aac_stream_format }, + { SPA_FORMAT_AUDIO_WMA_profile, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_WMA_BASE "profile", + spa_type_audio_wma_profile }, + { SPA_FORMAT_AUDIO_AMR_bandMode, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_AMR_BASE "bandMode", + spa_type_audio_amr_band_mode }, + + { SPA_FORMAT_VIDEO_format, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "format", + spa_type_video_format, }, + { SPA_FORMAT_VIDEO_modifier, SPA_TYPE_Long, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "modifier", NULL }, + { SPA_FORMAT_VIDEO_size, SPA_TYPE_Rectangle, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "size", NULL }, + { SPA_FORMAT_VIDEO_framerate, SPA_TYPE_Fraction, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "framerate", NULL }, + { SPA_FORMAT_VIDEO_maxFramerate, SPA_TYPE_Fraction, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "maxFramerate", NULL }, + { SPA_FORMAT_VIDEO_views, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "views", NULL }, + { SPA_FORMAT_VIDEO_interlaceMode, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "interlaceMode", + spa_type_video_interlace_mode, }, + { SPA_FORMAT_VIDEO_pixelAspectRatio, SPA_TYPE_Fraction, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "pixelAspectRatio", NULL }, + { SPA_FORMAT_VIDEO_multiviewMode, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "multiviewMode", NULL }, + { SPA_FORMAT_VIDEO_multiviewFlags, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "multiviewFlags", NULL }, + { SPA_FORMAT_VIDEO_chromaSite, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "chromaSite", NULL }, + { SPA_FORMAT_VIDEO_colorRange, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "colorRange", NULL }, + { SPA_FORMAT_VIDEO_colorMatrix, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "colorMatrix", NULL }, + { SPA_FORMAT_VIDEO_transferFunction, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "transferFunction", NULL }, + { SPA_FORMAT_VIDEO_colorPrimaries, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "colorPrimaries", NULL }, + { SPA_FORMAT_VIDEO_profile, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "profile", NULL }, + { SPA_FORMAT_VIDEO_level, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "level", NULL }, + + { SPA_FORMAT_VIDEO_H264_streamFormat, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_H264_BASE "streamFormat", NULL }, + { SPA_FORMAT_VIDEO_H264_alignment, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_H264_BASE "alignment", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_FORMAT_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/format-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/format-utils.h new file mode 100644 index 0000000000000..b4edb6f6efe4c --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/format-utils.h @@ -0,0 +1,38 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_FORMAT_UTILS_H +#define SPA_PARAM_FORMAT_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include + +static inline int +spa_format_parse(const struct spa_pod *format, uint32_t *media_type, uint32_t *media_subtype) +{ + return spa_pod_parse_object(format, + SPA_TYPE_OBJECT_Format, NULL, + SPA_FORMAT_mediaType, SPA_POD_Id(media_type), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(media_subtype)); +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_FORMAT_UTILS_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/format.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/format.h new file mode 100644 index 0000000000000..a7b425465239a --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/format.h @@ -0,0 +1,157 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_FORMAT_H +#define SPA_PARAM_FORMAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +/** media type for SPA_TYPE_OBJECT_Format */ +enum spa_media_type { + SPA_MEDIA_TYPE_unknown, + SPA_MEDIA_TYPE_audio, + SPA_MEDIA_TYPE_video, + SPA_MEDIA_TYPE_image, + SPA_MEDIA_TYPE_binary, + SPA_MEDIA_TYPE_stream, + SPA_MEDIA_TYPE_application, +}; + +/** media subtype for SPA_TYPE_OBJECT_Format */ +enum spa_media_subtype { + SPA_MEDIA_SUBTYPE_unknown, + SPA_MEDIA_SUBTYPE_raw, + SPA_MEDIA_SUBTYPE_dsp, + SPA_MEDIA_SUBTYPE_iec958, /** S/PDIF */ + SPA_MEDIA_SUBTYPE_dsd, + + SPA_MEDIA_SUBTYPE_START_Audio = 0x10000, + SPA_MEDIA_SUBTYPE_mp3, + SPA_MEDIA_SUBTYPE_aac, + SPA_MEDIA_SUBTYPE_vorbis, + SPA_MEDIA_SUBTYPE_wma, + SPA_MEDIA_SUBTYPE_ra, + SPA_MEDIA_SUBTYPE_sbc, + SPA_MEDIA_SUBTYPE_adpcm, + SPA_MEDIA_SUBTYPE_g723, + SPA_MEDIA_SUBTYPE_g726, + SPA_MEDIA_SUBTYPE_g729, + SPA_MEDIA_SUBTYPE_amr, + SPA_MEDIA_SUBTYPE_gsm, + SPA_MEDIA_SUBTYPE_alac, /** since 0.3.65 */ + SPA_MEDIA_SUBTYPE_flac, /** since 0.3.65 */ + SPA_MEDIA_SUBTYPE_ape, /** since 0.3.65 */ + SPA_MEDIA_SUBTYPE_opus, /** since 0.3.68 */ + + SPA_MEDIA_SUBTYPE_START_Video = 0x20000, + SPA_MEDIA_SUBTYPE_h264, + SPA_MEDIA_SUBTYPE_mjpg, + SPA_MEDIA_SUBTYPE_dv, + SPA_MEDIA_SUBTYPE_mpegts, + SPA_MEDIA_SUBTYPE_h263, + SPA_MEDIA_SUBTYPE_mpeg1, + SPA_MEDIA_SUBTYPE_mpeg2, + SPA_MEDIA_SUBTYPE_mpeg4, + SPA_MEDIA_SUBTYPE_xvid, + SPA_MEDIA_SUBTYPE_vc1, + SPA_MEDIA_SUBTYPE_vp8, + SPA_MEDIA_SUBTYPE_vp9, + SPA_MEDIA_SUBTYPE_bayer, + + SPA_MEDIA_SUBTYPE_START_Image = 0x30000, + SPA_MEDIA_SUBTYPE_jpeg, + + SPA_MEDIA_SUBTYPE_START_Binary = 0x40000, + + SPA_MEDIA_SUBTYPE_START_Stream = 0x50000, + SPA_MEDIA_SUBTYPE_midi, + + SPA_MEDIA_SUBTYPE_START_Application = 0x60000, + SPA_MEDIA_SUBTYPE_control, /**< control stream, data contains + * spa_pod_sequence with control info. */ +}; + +/** properties for audio SPA_TYPE_OBJECT_Format */ +enum spa_format { + SPA_FORMAT_START, + + SPA_FORMAT_mediaType, /**< media type (Id enum spa_media_type) */ + SPA_FORMAT_mediaSubtype, /**< media subtype (Id enum spa_media_subtype) */ + + /* Audio format keys */ + SPA_FORMAT_START_Audio = 0x10000, + SPA_FORMAT_AUDIO_format, /**< audio format, (Id enum spa_audio_format) */ + SPA_FORMAT_AUDIO_flags, /**< optional flags (Int) */ + SPA_FORMAT_AUDIO_rate, /**< sample rate (Int) */ + SPA_FORMAT_AUDIO_channels, /**< number of audio channels (Int) */ + SPA_FORMAT_AUDIO_position, /**< channel positions (Id enum spa_audio_position) */ + + SPA_FORMAT_AUDIO_iec958Codec, /**< codec used (IEC958) (Id enum spa_audio_iec958_codec) */ + + SPA_FORMAT_AUDIO_bitorder, /**< bit order (Id enum spa_param_bitorder) */ + SPA_FORMAT_AUDIO_interleave, /**< Interleave bytes (Int) */ + SPA_FORMAT_AUDIO_bitrate, /**< bit rate (Int) */ + SPA_FORMAT_AUDIO_blockAlign, /**< audio data block alignment (Int) */ + + SPA_FORMAT_AUDIO_AAC_streamFormat, /**< AAC stream format, (Id enum spa_audio_aac_stream_format) */ + + SPA_FORMAT_AUDIO_WMA_profile, /**< WMA profile (Id enum spa_audio_wma_profile) */ + + SPA_FORMAT_AUDIO_AMR_bandMode, /**< AMR band mode (Id enum spa_audio_amr_band_mode) */ + + + /* Video Format keys */ + SPA_FORMAT_START_Video = 0x20000, + SPA_FORMAT_VIDEO_format, /**< video format (Id enum spa_video_format) */ + SPA_FORMAT_VIDEO_modifier, /**< format modifier (Long) + * use only with DMA-BUF and omit for other buffer types */ + SPA_FORMAT_VIDEO_size, /**< size (Rectangle) */ + SPA_FORMAT_VIDEO_framerate, /**< frame rate (Fraction) */ + SPA_FORMAT_VIDEO_maxFramerate, /**< maximum frame rate (Fraction) */ + SPA_FORMAT_VIDEO_views, /**< number of views (Int) */ + SPA_FORMAT_VIDEO_interlaceMode, /**< (Id enum spa_video_interlace_mode) */ + SPA_FORMAT_VIDEO_pixelAspectRatio, /**< (Rectangle) */ + SPA_FORMAT_VIDEO_multiviewMode, /**< (Id enum spa_video_multiview_mode) */ + SPA_FORMAT_VIDEO_multiviewFlags, /**< (Id enum spa_video_multiview_flags) */ + SPA_FORMAT_VIDEO_chromaSite, /**< /Id enum spa_video_chroma_site) */ + SPA_FORMAT_VIDEO_colorRange, /**< /Id enum spa_video_color_range) */ + SPA_FORMAT_VIDEO_colorMatrix, /**< /Id enum spa_video_color_matrix) */ + SPA_FORMAT_VIDEO_transferFunction, /**< /Id enum spa_video_transfer_function) */ + SPA_FORMAT_VIDEO_colorPrimaries, /**< /Id enum spa_video_color_primaries) */ + SPA_FORMAT_VIDEO_profile, /**< (Int) */ + SPA_FORMAT_VIDEO_level, /**< (Int) */ + SPA_FORMAT_VIDEO_H264_streamFormat, /**< (Id enum spa_h264_stream_format) */ + SPA_FORMAT_VIDEO_H264_alignment, /**< (Id enum spa_h264_alignment) */ + + /* Image Format keys */ + SPA_FORMAT_START_Image = 0x30000, + /* Binary Format keys */ + SPA_FORMAT_START_Binary = 0x40000, + /* Stream Format keys */ + SPA_FORMAT_START_Stream = 0x50000, + /* Application Format keys */ + SPA_FORMAT_START_Application = 0x60000, +}; + +#define SPA_KEY_FORMAT_DSP "format.dsp" /**< a predefined DSP format, + * Ex. "32 bit float mono audio" */ + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_FORMAT_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/latency-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/latency-types.h new file mode 100644 index 0000000000000..6b9822bad350d --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/latency-types.h @@ -0,0 +1,55 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_LATENCY_TYPES_H +#define SPA_PARAM_LATENCY_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include +#include + +#define SPA_TYPE_INFO_PARAM_Latency SPA_TYPE_INFO_PARAM_BASE "Latency" +#define SPA_TYPE_INFO_PARAM_LATENCY_BASE SPA_TYPE_INFO_PARAM_Latency ":" + +static const struct spa_type_info spa_type_param_latency[] = { + { SPA_PARAM_LATENCY_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_LATENCY_BASE, spa_type_param, }, + { SPA_PARAM_LATENCY_direction, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_LATENCY_BASE "direction", spa_type_direction, }, + { SPA_PARAM_LATENCY_minQuantum, SPA_TYPE_Float, SPA_TYPE_INFO_PARAM_LATENCY_BASE "minQuantum", NULL, }, + { SPA_PARAM_LATENCY_maxQuantum, SPA_TYPE_Float, SPA_TYPE_INFO_PARAM_LATENCY_BASE "maxQuantum", NULL, }, + { SPA_PARAM_LATENCY_minRate, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_LATENCY_BASE "minRate", NULL, }, + { SPA_PARAM_LATENCY_maxRate, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_LATENCY_BASE "maxRate", NULL, }, + { SPA_PARAM_LATENCY_minNs, SPA_TYPE_Long, SPA_TYPE_INFO_PARAM_LATENCY_BASE "minNs", NULL, }, + { SPA_PARAM_LATENCY_maxNs, SPA_TYPE_Long, SPA_TYPE_INFO_PARAM_LATENCY_BASE "maxNs", NULL, }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_PARAM_ProcessLatency SPA_TYPE_INFO_PARAM_BASE "ProcessLatency" +#define SPA_TYPE_INFO_PARAM_PROCESS_LATENCY_BASE SPA_TYPE_INFO_PARAM_ProcessLatency ":" + +static const struct spa_type_info spa_type_param_process_latency[] = { + { SPA_PARAM_PROCESS_LATENCY_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_LATENCY_BASE, spa_type_param, }, + { SPA_PARAM_PROCESS_LATENCY_quantum, SPA_TYPE_Float, SPA_TYPE_INFO_PARAM_PROCESS_LATENCY_BASE "quantum", NULL, }, + { SPA_PARAM_PROCESS_LATENCY_rate, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PROCESS_LATENCY_BASE "rate", NULL, }, + { SPA_PARAM_PROCESS_LATENCY_ns, SPA_TYPE_Long, SPA_TYPE_INFO_PARAM_PROCESS_LATENCY_BASE "ns", NULL, }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_LATENCY_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/latency.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/latency.h new file mode 100644 index 0000000000000..5fa40b59b9f65 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/latency.h @@ -0,0 +1,69 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_LATENY_H +#define SPA_PARAM_LATENY_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +/** properties for SPA_TYPE_OBJECT_ParamLatency */ +enum spa_param_latency { + SPA_PARAM_LATENCY_START, + SPA_PARAM_LATENCY_direction, /**< direction, input/output (Id enum spa_direction) */ + SPA_PARAM_LATENCY_minQuantum, /**< min latency relative to quantum (Float) */ + SPA_PARAM_LATENCY_maxQuantum, /**< max latency relative to quantum (Float) */ + SPA_PARAM_LATENCY_minRate, /**< min latency (Int) relative to rate */ + SPA_PARAM_LATENCY_maxRate, /**< max latency (Int) relative to rate */ + SPA_PARAM_LATENCY_minNs, /**< min latency (Long) in nanoseconds */ + SPA_PARAM_LATENCY_maxNs, /**< max latency (Long) in nanoseconds */ +}; + +/** helper structure for managing latency objects */ +struct spa_latency_info { + enum spa_direction direction; + float min_quantum; + float max_quantum; + uint32_t min_rate; + uint32_t max_rate; + uint64_t min_ns; + uint64_t max_ns; +}; + +#define SPA_LATENCY_INFO(dir,...) ((struct spa_latency_info) { .direction = (dir), ## __VA_ARGS__ }) + +/** properties for SPA_TYPE_OBJECT_ParamProcessLatency */ +enum spa_param_process_latency { + SPA_PARAM_PROCESS_LATENCY_START, + SPA_PARAM_PROCESS_LATENCY_quantum, /**< latency relative to quantum (Float) */ + SPA_PARAM_PROCESS_LATENCY_rate, /**< latency (Int) relative to rate */ + SPA_PARAM_PROCESS_LATENCY_ns, /**< latency (Long) in nanoseconds */ +}; + +/** Helper structure for managing process latency objects */ +struct spa_process_latency_info { + float quantum; + uint32_t rate; + uint64_t ns; +}; + +#define SPA_PROCESS_LATENCY_INFO_INIT(...) ((struct spa_process_latency_info) { __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_LATENY_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/param-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/param-types.h new file mode 100644 index 0000000000000..ff2ddde1cd953 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/param-types.h @@ -0,0 +1,95 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_TYPES_H +#define SPA_PARAM_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include +#include + +/* base for parameter object enumerations */ +#define SPA_TYPE_INFO_ParamId SPA_TYPE_INFO_ENUM_BASE "ParamId" +#define SPA_TYPE_INFO_PARAM_ID_BASE SPA_TYPE_INFO_ParamId ":" + +static const struct spa_type_info spa_type_param[] = { + { SPA_PARAM_Invalid, SPA_TYPE_None, SPA_TYPE_INFO_PARAM_ID_BASE "Invalid", NULL }, + { SPA_PARAM_PropInfo, SPA_TYPE_OBJECT_PropInfo, SPA_TYPE_INFO_PARAM_ID_BASE "PropInfo", NULL }, + { SPA_PARAM_Props, SPA_TYPE_OBJECT_Props, SPA_TYPE_INFO_PARAM_ID_BASE "Props", NULL }, + { SPA_PARAM_EnumFormat, SPA_TYPE_OBJECT_Format, SPA_TYPE_INFO_PARAM_ID_BASE "EnumFormat", NULL }, + { SPA_PARAM_Format, SPA_TYPE_OBJECT_Format, SPA_TYPE_INFO_PARAM_ID_BASE "Format", NULL }, + { SPA_PARAM_Buffers, SPA_TYPE_OBJECT_ParamBuffers, SPA_TYPE_INFO_PARAM_ID_BASE "Buffers", NULL }, + { SPA_PARAM_Meta, SPA_TYPE_OBJECT_ParamMeta, SPA_TYPE_INFO_PARAM_ID_BASE "Meta", NULL }, + { SPA_PARAM_IO, SPA_TYPE_OBJECT_ParamIO, SPA_TYPE_INFO_PARAM_ID_BASE "IO", NULL }, + { SPA_PARAM_EnumProfile, SPA_TYPE_OBJECT_ParamProfile, SPA_TYPE_INFO_PARAM_ID_BASE "EnumProfile", NULL }, + { SPA_PARAM_Profile, SPA_TYPE_OBJECT_ParamProfile, SPA_TYPE_INFO_PARAM_ID_BASE "Profile", NULL }, + { SPA_PARAM_EnumPortConfig, SPA_TYPE_OBJECT_ParamPortConfig, SPA_TYPE_INFO_PARAM_ID_BASE "EnumPortConfig", NULL }, + { SPA_PARAM_PortConfig, SPA_TYPE_OBJECT_ParamPortConfig, SPA_TYPE_INFO_PARAM_ID_BASE "PortConfig", NULL }, + { SPA_PARAM_EnumRoute, SPA_TYPE_OBJECT_ParamRoute, SPA_TYPE_INFO_PARAM_ID_BASE "EnumRoute", NULL }, + { SPA_PARAM_Route, SPA_TYPE_OBJECT_ParamRoute, SPA_TYPE_INFO_PARAM_ID_BASE "Route", NULL }, + { SPA_PARAM_Control, SPA_TYPE_Sequence, SPA_TYPE_INFO_PARAM_ID_BASE "Control", NULL }, + { SPA_PARAM_Latency, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_INFO_PARAM_ID_BASE "Latency", NULL }, + { SPA_PARAM_ProcessLatency, SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_INFO_PARAM_ID_BASE "ProcessLatency", NULL }, + { 0, 0, NULL, NULL }, +}; + +/* base for parameter objects */ +#define SPA_TYPE_INFO_Param SPA_TYPE_INFO_OBJECT_BASE "Param" +#define SPA_TYPE_INFO_PARAM_BASE SPA_TYPE_INFO_Param ":" + +#include + +static const struct spa_type_info spa_type_prop_float_array[] = { + { SPA_PROP_START, SPA_TYPE_Float, SPA_TYPE_INFO_BASE "floatArray", NULL, }, + { 0, 0, NULL, NULL }, +}; + +static const struct spa_type_info spa_type_prop_channel_map[] = { + { SPA_PROP_START, SPA_TYPE_Id, SPA_TYPE_INFO_BASE "channelMap", spa_type_audio_channel, }, + { 0, 0, NULL, NULL }, +}; + +static const struct spa_type_info spa_type_prop_iec958_codec[] = { + { SPA_PROP_START, SPA_TYPE_Id, SPA_TYPE_INFO_BASE "iec958Codec", spa_type_audio_iec958_codec, }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_ParamBitorder SPA_TYPE_INFO_ENUM_BASE "ParamBitorder" +#define SPA_TYPE_INFO_PARAM_BITORDER_BASE SPA_TYPE_INFO_ParamBitorder ":" + +static const struct spa_type_info spa_type_param_bitorder[] = { + { SPA_PARAM_BITORDER_unknown, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BITORDER_BASE "unknown", NULL }, + { SPA_PARAM_BITORDER_msb, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BITORDER_BASE "msb", NULL }, + { SPA_PARAM_BITORDER_lsb, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BITORDER_BASE "lsb", NULL }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_ParamAvailability SPA_TYPE_INFO_ENUM_BASE "ParamAvailability" +#define SPA_TYPE_INFO_PARAM_AVAILABILITY_BASE SPA_TYPE_INFO_ParamAvailability ":" + +static const struct spa_type_info spa_type_param_availability[] = { + { SPA_PARAM_AVAILABILITY_unknown, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_AVAILABILITY_BASE "unknown", NULL }, + { SPA_PARAM_AVAILABILITY_no, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_AVAILABILITY_BASE "no", NULL }, + { SPA_PARAM_AVAILABILITY_yes, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_AVAILABILITY_BASE "yes", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/param.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/param.h new file mode 100644 index 0000000000000..48e7d9349d58d --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/param.h @@ -0,0 +1,87 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_H +#define SPA_PARAM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup spa_param Parameters + * Parameter value enumerations and type information + */ + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +/** different parameter types that can be queried */ +enum spa_param_type { + SPA_PARAM_Invalid, /**< invalid */ + SPA_PARAM_PropInfo, /**< property information as SPA_TYPE_OBJECT_PropInfo */ + SPA_PARAM_Props, /**< properties as SPA_TYPE_OBJECT_Props */ + SPA_PARAM_EnumFormat, /**< available formats as SPA_TYPE_OBJECT_Format */ + SPA_PARAM_Format, /**< configured format as SPA_TYPE_OBJECT_Format */ + SPA_PARAM_Buffers, /**< buffer configurations as SPA_TYPE_OBJECT_ParamBuffers*/ + SPA_PARAM_Meta, /**< allowed metadata for buffers as SPA_TYPE_OBJECT_ParamMeta*/ + SPA_PARAM_IO, /**< configurable IO areas as SPA_TYPE_OBJECT_ParamIO */ + SPA_PARAM_EnumProfile, /**< profile enumeration as SPA_TYPE_OBJECT_ParamProfile */ + SPA_PARAM_Profile, /**< profile configuration as SPA_TYPE_OBJECT_ParamProfile */ + SPA_PARAM_EnumPortConfig, /**< port configuration enumeration as SPA_TYPE_OBJECT_ParamPortConfig */ + SPA_PARAM_PortConfig, /**< port configuration as SPA_TYPE_OBJECT_ParamPortConfig */ + SPA_PARAM_EnumRoute, /**< routing enumeration as SPA_TYPE_OBJECT_ParamRoute */ + SPA_PARAM_Route, /**< routing configuration as SPA_TYPE_OBJECT_ParamRoute */ + SPA_PARAM_Control, /**< Control parameter, a SPA_TYPE_Sequence */ + SPA_PARAM_Latency, /**< latency reporting, a SPA_TYPE_OBJECT_ParamLatency */ + SPA_PARAM_ProcessLatency, /**< processing latency, a SPA_TYPE_OBJECT_ParamProcessLatency */ +}; + +/** information about a parameter */ +struct spa_param_info { + uint32_t id; /**< enum spa_param_type */ +#define SPA_PARAM_INFO_SERIAL (1<<0) /**< bit to signal update even when the + * read/write flags don't change */ +#define SPA_PARAM_INFO_READ (1<<1) +#define SPA_PARAM_INFO_WRITE (1<<2) +#define SPA_PARAM_INFO_READWRITE (SPA_PARAM_INFO_WRITE|SPA_PARAM_INFO_READ) + uint32_t flags; + uint32_t user; /**< private user field. You can use this to keep + * state. */ + int32_t seq; /**< private seq field. You can use this to keep + * state of a pending update. */ + uint32_t padding[4]; +}; + +#define SPA_PARAM_INFO(id,flags) ((struct spa_param_info){ (id), (flags) }) + +enum spa_param_bitorder { + SPA_PARAM_BITORDER_unknown, /**< unknown bitorder */ + SPA_PARAM_BITORDER_msb, /**< most significant bit */ + SPA_PARAM_BITORDER_lsb, /**< least significant bit */ +}; + +enum spa_param_availability { + SPA_PARAM_AVAILABILITY_unknown, /**< unknown availability */ + SPA_PARAM_AVAILABILITY_no, /**< not available */ + SPA_PARAM_AVAILABILITY_yes, /**< available */ +}; + +#include +#include +#include +#include + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/port-config-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/port-config-types.h new file mode 100644 index 0000000000000..b25d9ced1932b --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/port-config-types.h @@ -0,0 +1,53 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PORT_CONFIG_TYPES_H +#define SPA_PARAM_PORT_CONFIG_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include +#include + +#define SPA_TYPE_INFO_ParamPortConfigMode SPA_TYPE_INFO_ENUM_BASE "ParamPortConfigMode" +#define SPA_TYPE_INFO_PARAM_PORT_CONFIG_MODE_BASE SPA_TYPE_INFO_ParamPortConfigMode ":" + +static const struct spa_type_info spa_type_param_port_config_mode[] = { + { SPA_PARAM_PORT_CONFIG_MODE_none, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PORT_CONFIG_MODE_BASE "none", NULL }, + { SPA_PARAM_PORT_CONFIG_MODE_passthrough, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PORT_CONFIG_MODE_BASE "passthrough", NULL }, + { SPA_PARAM_PORT_CONFIG_MODE_convert, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PORT_CONFIG_MODE_BASE "convert", NULL }, + { SPA_PARAM_PORT_CONFIG_MODE_dsp, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PORT_CONFIG_MODE_BASE "dsp", NULL }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_PARAM_PortConfig SPA_TYPE_INFO_PARAM_BASE "PortConfig" +#define SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE SPA_TYPE_INFO_PARAM_PortConfig ":" + +static const struct spa_type_info spa_type_param_port_config[] = { + { SPA_PARAM_PORT_CONFIG_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE, spa_type_param, }, + { SPA_PARAM_PORT_CONFIG_direction, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE "direction", spa_type_direction, }, + { SPA_PARAM_PORT_CONFIG_mode, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE "mode", spa_type_param_port_config_mode }, + { SPA_PARAM_PORT_CONFIG_monitor, SPA_TYPE_Bool, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE "monitor", NULL }, + { SPA_PARAM_PORT_CONFIG_control, SPA_TYPE_Bool, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE "control", NULL }, + { SPA_PARAM_PORT_CONFIG_format, SPA_TYPE_OBJECT_Format, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE "format", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PORT_CONFIG_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/port-config.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/port-config.h new file mode 100644 index 0000000000000..ab77a1834140a --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/port-config.h @@ -0,0 +1,46 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PORT_CONFIG_H +#define SPA_PARAM_PORT_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +enum spa_param_port_config_mode { + SPA_PARAM_PORT_CONFIG_MODE_none, /**< no configuration */ + SPA_PARAM_PORT_CONFIG_MODE_passthrough, /**< passthrough configuration */ + SPA_PARAM_PORT_CONFIG_MODE_convert, /**< convert configuration */ + SPA_PARAM_PORT_CONFIG_MODE_dsp, /**< dsp configuration, depending on the external + * format. For audio, ports will be configured for + * the given number of channels with F32 format. */ +}; + +/** properties for SPA_TYPE_OBJECT_ParamPortConfig */ +enum spa_param_port_config { + SPA_PARAM_PORT_CONFIG_START, + SPA_PARAM_PORT_CONFIG_direction, /**< (Id enum spa_direction) direction */ + SPA_PARAM_PORT_CONFIG_mode, /**< (Id enum spa_param_port_config_mode) mode */ + SPA_PARAM_PORT_CONFIG_monitor, /**< (Bool) enable monitor output ports on input ports */ + SPA_PARAM_PORT_CONFIG_control, /**< (Bool) enable control ports */ + SPA_PARAM_PORT_CONFIG_format, /**< (Object) format filter */ +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PORT_CONFIG_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/profile-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/profile-types.h new file mode 100644 index 0000000000000..1176a0dd5a367 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/profile-types.h @@ -0,0 +1,45 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PROFILE_TYPES_H +#define SPA_PARAM_PROFILE_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +#include + +#define SPA_TYPE_INFO_PARAM_Profile SPA_TYPE_INFO_PARAM_BASE "Profile" +#define SPA_TYPE_INFO_PARAM_PROFILE_BASE SPA_TYPE_INFO_PARAM_Profile ":" + +static const struct spa_type_info spa_type_param_profile[] = { + { SPA_PARAM_PROFILE_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_PROFILE_BASE, spa_type_param, }, + { SPA_PARAM_PROFILE_index, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PROFILE_BASE "index", NULL }, + { SPA_PARAM_PROFILE_name, SPA_TYPE_String, SPA_TYPE_INFO_PARAM_PROFILE_BASE "name", NULL }, + { SPA_PARAM_PROFILE_description, SPA_TYPE_String, SPA_TYPE_INFO_PARAM_PROFILE_BASE "description", NULL }, + { SPA_PARAM_PROFILE_priority, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PROFILE_BASE "priority", NULL }, + { SPA_PARAM_PROFILE_available, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_PROFILE_BASE "available", spa_type_param_availability, }, + { SPA_PARAM_PROFILE_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_PROFILE_BASE "info", NULL, }, + { SPA_PARAM_PROFILE_classes, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_PROFILE_BASE "classes", NULL, }, + { SPA_PARAM_PROFILE_save, SPA_TYPE_Bool, SPA_TYPE_INFO_PARAM_PROFILE_BASE "save", NULL, }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PROFILE_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/profile.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/profile.h new file mode 100644 index 0000000000000..c31b714d17eab --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/profile.h @@ -0,0 +1,52 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PROFILE_H +#define SPA_PARAM_PROFILE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +/** properties for SPA_TYPE_OBJECT_ParamProfile */ +enum spa_param_profile { + SPA_PARAM_PROFILE_START, + SPA_PARAM_PROFILE_index, /**< profile index (Int) */ + SPA_PARAM_PROFILE_name, /**< profile name (String) */ + SPA_PARAM_PROFILE_description, /**< profile description (String) */ + SPA_PARAM_PROFILE_priority, /**< profile priority (Int) */ + SPA_PARAM_PROFILE_available, /**< availability of the profile + * (Id enum spa_param_availability) */ + SPA_PARAM_PROFILE_info, /**< info (Struct( + * Int : n_items, + * (String : key, + * String : value)*)) */ + SPA_PARAM_PROFILE_classes, /**< node classes provided by this profile + * (Struct( + * Int : number of items following + * Struct( + * String : class name (eg. "Audio/Source"), + * Int : number of nodes + * String : property (eg. "card.profile.devices"), + * Array of Int: device indexes + * )*)) */ + SPA_PARAM_PROFILE_save, /**< If profile should be saved (Bool) */ +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PROFILE_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler-types.h new file mode 100644 index 0000000000000..a1abc02a130c4 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler-types.h @@ -0,0 +1,40 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PROFILER_TYPES_H +#define SPA_PARAM_PROFILER_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include + +#define SPA_TYPE_INFO_Profiler SPA_TYPE_INFO_OBJECT_BASE "Profiler" +#define SPA_TYPE_INFO_PROFILER_BASE SPA_TYPE_INFO_Profiler ":" + +static const struct spa_type_info spa_type_profiler[] = { + { SPA_PROFILER_START, SPA_TYPE_Id, SPA_TYPE_INFO_PROFILER_BASE, spa_type_param, }, + { SPA_PROFILER_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "info", NULL, }, + { SPA_PROFILER_clock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "clock", NULL, }, + { SPA_PROFILER_driverBlock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "driverBlock", NULL, }, + { SPA_PROFILER_followerBlock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "followerBlock", NULL, }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PROFILER_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler.h new file mode 100644 index 0000000000000..e65835a1a8525 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/profiler.h @@ -0,0 +1,77 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PROFILER_H +#define SPA_PARAM_PROFILER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +/** properties for SPA_TYPE_OBJECT_Profiler */ +enum spa_profiler { + SPA_PROFILER_START, + + SPA_PROFILER_START_Driver = 0x10000, /**< driver related profiler properties */ + SPA_PROFILER_info, /**< Generic info, counter and CPU load, + * (Struct( + * Long : counter, + * Float : cpu_load fast, + * Float : cpu_load medium, + * Float : cpu_load slow), + * Int : xrun-count)) */ + SPA_PROFILER_clock, /**< clock information + * (Struct( + * Int : clock flags, + * Int : clock id, + * String: clock name, + * Long : clock nsec, + * Fraction : clock rate, + * Long : clock position, + * Long : clock duration, + * Long : clock delay, + * Double : clock rate_diff, + * Long : clock next_nsec)) */ + SPA_PROFILER_driverBlock, /**< generic driver info block + * (Struct( + * Int : driver_id, + * String : name, + * Long : driver prev_signal, + * Long : driver signal, + * Long : driver awake, + * Long : driver finish, + * Int : driver status), + * Fraction : latency)) */ + + SPA_PROFILER_START_Follower = 0x20000, /**< follower related profiler properties */ + SPA_PROFILER_followerBlock, /**< generic follower info block + * (Struct( + * Int : id, + * String : name, + * Long : prev_signal, + * Long : signal, + * Long : awake, + * Long : finish, + * Int : status, + * Fraction : latency)) */ + + SPA_PROFILER_START_CUSTOM = 0x1000000, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PROFILER_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/props-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/props-types.h new file mode 100644 index 0000000000000..5e4e0a0c9d6f1 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/props-types.h @@ -0,0 +1,104 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PROPS_TYPES_H +#define SPA_PARAM_PROPS_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +#include + +/** Props Param */ +#define SPA_TYPE_INFO_Props SPA_TYPE_INFO_PARAM_BASE "Props" +#define SPA_TYPE_INFO_PROPS_BASE SPA_TYPE_INFO_Props ":" + +static const struct spa_type_info spa_type_props[] = { + { SPA_PROP_START, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE, spa_type_param, }, + { SPA_PROP_unknown, SPA_TYPE_None, SPA_TYPE_INFO_PROPS_BASE "unknown", NULL }, + { SPA_PROP_device, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "device", NULL }, + { SPA_PROP_deviceName, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "deviceName", NULL }, + { SPA_PROP_deviceFd, SPA_TYPE_Fd, SPA_TYPE_INFO_PROPS_BASE "deviceFd", NULL }, + { SPA_PROP_card, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "card", NULL }, + { SPA_PROP_cardName, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "cardName", NULL }, + { SPA_PROP_minLatency, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "minLatency", NULL }, + { SPA_PROP_maxLatency, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "maxLatency", NULL }, + { SPA_PROP_periods, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "periods", NULL }, + { SPA_PROP_periodSize, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "periodSize", NULL }, + { SPA_PROP_periodEvent, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "periodEvent", NULL }, + { SPA_PROP_live, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "live", NULL }, + { SPA_PROP_rate, SPA_TYPE_Double, SPA_TYPE_INFO_PROPS_BASE "rate", NULL }, + { SPA_PROP_quality, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "quality", NULL }, + { SPA_PROP_bluetoothAudioCodec, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "bluetoothAudioCodec", spa_type_bluetooth_audio_codec }, + { SPA_PROP_bluetoothOffloadActive, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "bluetoothOffloadActive", NULL }, + + { SPA_PROP_waveType, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "waveType", NULL }, + { SPA_PROP_frequency, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "frequency", NULL }, + { SPA_PROP_volume, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "volume", NULL }, + { SPA_PROP_mute, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "mute", NULL }, + { SPA_PROP_patternType, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "patternType", NULL }, + { SPA_PROP_ditherType, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "ditherType", NULL }, + { SPA_PROP_truncate, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "truncate", NULL }, + { SPA_PROP_channelVolumes, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "channelVolumes", spa_type_prop_float_array }, + { SPA_PROP_volumeBase, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "volumeBase", NULL }, + { SPA_PROP_volumeStep, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "volumeStep", NULL }, + { SPA_PROP_channelMap, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "channelMap", spa_type_prop_channel_map }, + { SPA_PROP_monitorMute, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "monitorMute", NULL }, + { SPA_PROP_monitorVolumes, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "monitorVolumes", spa_type_prop_float_array }, + { SPA_PROP_latencyOffsetNsec, SPA_TYPE_Long, SPA_TYPE_INFO_PROPS_BASE "latencyOffsetNsec", NULL }, + { SPA_PROP_softMute, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "softMute", NULL }, + { SPA_PROP_softVolumes, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "softVolumes", spa_type_prop_float_array }, + { SPA_PROP_iec958Codecs, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "iec958Codecs", spa_type_prop_iec958_codec }, + { SPA_PROP_volumeRampSamples, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampSamples", NULL }, + { SPA_PROP_volumeRampStepSamples, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampStepSamples", NULL }, + { SPA_PROP_volumeRampTime, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampTime", NULL }, + { SPA_PROP_volumeRampStepTime, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "volumeRampStepTime", NULL }, + { SPA_PROP_volumeRampScale, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "volumeRampScale", NULL }, + + { SPA_PROP_brightness, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "brightness", NULL }, + { SPA_PROP_contrast, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "contrast", NULL }, + { SPA_PROP_saturation, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "saturation", NULL }, + { SPA_PROP_hue, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "hue", NULL }, + { SPA_PROP_gamma, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "gamma", NULL }, + { SPA_PROP_exposure, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "exposure", NULL }, + { SPA_PROP_gain, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "gain", NULL }, + { SPA_PROP_sharpness, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "sharpness", NULL }, + + { SPA_PROP_params, SPA_TYPE_Struct, SPA_TYPE_INFO_PROPS_BASE "params", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** Enum Property info */ +#define SPA_TYPE_INFO_PropInfo SPA_TYPE_INFO_PARAM_BASE "PropInfo" +#define SPA_TYPE_INFO_PROP_INFO_BASE SPA_TYPE_INFO_PropInfo ":" + +static const struct spa_type_info spa_type_prop_info[] = { + { SPA_PROP_INFO_START, SPA_TYPE_Id, SPA_TYPE_INFO_PROP_INFO_BASE, spa_type_param, }, + { SPA_PROP_INFO_id, SPA_TYPE_Id, SPA_TYPE_INFO_PROP_INFO_BASE "id", spa_type_props }, + { SPA_PROP_INFO_name, SPA_TYPE_String, SPA_TYPE_INFO_PROP_INFO_BASE "name", NULL }, + { SPA_PROP_INFO_type, SPA_TYPE_Pod, SPA_TYPE_INFO_PROP_INFO_BASE "type", NULL }, + { SPA_PROP_INFO_labels, SPA_TYPE_Struct, SPA_TYPE_INFO_PROP_INFO_BASE "labels", NULL }, + { SPA_PROP_INFO_container, SPA_TYPE_Id, SPA_TYPE_INFO_PROP_INFO_BASE "container", NULL }, + { SPA_PROP_INFO_params, SPA_TYPE_Bool, SPA_TYPE_INFO_PROP_INFO_BASE "params", NULL }, + { SPA_PROP_INFO_description, SPA_TYPE_String, SPA_TYPE_INFO_PROP_INFO_BASE "description", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PROPS_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/props.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/props.h new file mode 100644 index 0000000000000..8ecf6f69bc49c --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/props.h @@ -0,0 +1,120 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PROPS_H +#define SPA_PARAM_PROPS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +/** properties of SPA_TYPE_OBJECT_PropInfo */ +enum spa_prop_info { + SPA_PROP_INFO_START, + SPA_PROP_INFO_id, /**< associated id of the property */ + SPA_PROP_INFO_name, /**< name of the property */ + SPA_PROP_INFO_type, /**< type and range/enums of property */ + SPA_PROP_INFO_labels, /**< labels of property if any, this is a + * struct with pairs of values, the first one + * is of the type of the property, the second + * one is a string with a user readable label + * for the value. */ + SPA_PROP_INFO_container, /**< type of container if any (Id) */ + SPA_PROP_INFO_params, /**< is part of params property (Bool) */ + SPA_PROP_INFO_description, /**< User readable description */ +}; + +/** predefined properties for SPA_TYPE_OBJECT_Props */ +enum spa_prop { + SPA_PROP_START, + + SPA_PROP_unknown, /**< an unknown property */ + + SPA_PROP_START_Device = 0x100, /**< device related properties */ + SPA_PROP_device, + SPA_PROP_deviceName, + SPA_PROP_deviceFd, + SPA_PROP_card, + SPA_PROP_cardName, + + SPA_PROP_minLatency, + SPA_PROP_maxLatency, + SPA_PROP_periods, + SPA_PROP_periodSize, + SPA_PROP_periodEvent, + SPA_PROP_live, + SPA_PROP_rate, + SPA_PROP_quality, + SPA_PROP_bluetoothAudioCodec, + SPA_PROP_bluetoothOffloadActive, + + SPA_PROP_START_Audio = 0x10000, /**< audio related properties */ + SPA_PROP_waveType, + SPA_PROP_frequency, + SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 normal */ + SPA_PROP_mute, /**< mute (Bool) */ + SPA_PROP_patternType, + SPA_PROP_ditherType, + SPA_PROP_truncate, + SPA_PROP_channelVolumes, /**< a volume array, one volume per + * channel (Array of Float) */ + SPA_PROP_volumeBase, /**< a volume base (Float) */ + SPA_PROP_volumeStep, /**< a volume step (Float) */ + SPA_PROP_channelMap, /**< a channelmap array + * (Array (Id enum spa_audio_channel)) */ + SPA_PROP_monitorMute, /**< mute (Bool) */ + SPA_PROP_monitorVolumes, /**< a volume array, one volume per + * channel (Array of Float) */ + SPA_PROP_latencyOffsetNsec, /**< delay adjustment */ + SPA_PROP_softMute, /**< mute (Bool) */ + SPA_PROP_softVolumes, /**< a volume array, one volume per + * channel (Array of Float) */ + + SPA_PROP_iec958Codecs, /**< enabled IEC958 (S/PDIF) codecs, + * (Array (Id enum spa_audio_iec958_codec) */ + SPA_PROP_volumeRampSamples, /**< Samples to ramp the volume over */ + SPA_PROP_volumeRampStepSamples, /**< Step or incremental Samples to ramp + * the volume over */ + SPA_PROP_volumeRampTime, /**< Time in millisec to ramp the volume over */ + SPA_PROP_volumeRampStepTime, /**< Step or incremental Time in nano seconds + * to ramp the */ + SPA_PROP_volumeRampScale, /**< the scale or graph to used to ramp the + * volume */ + + SPA_PROP_START_Video = 0x20000, /**< video related properties */ + SPA_PROP_brightness, + SPA_PROP_contrast, + SPA_PROP_saturation, + SPA_PROP_hue, + SPA_PROP_gamma, + SPA_PROP_exposure, + SPA_PROP_gain, + SPA_PROP_sharpness, + + SPA_PROP_START_Other = 0x80000, /**< other properties */ + SPA_PROP_params, /**< simple control params + * (Struct( + * (String : key, + * Pod : value)*)) */ + + + SPA_PROP_START_CUSTOM = 0x1000000, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PROPS_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/route-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/route-types.h new file mode 100644 index 0000000000000..8eb839d3f7b5a --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/route-types.h @@ -0,0 +1,51 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_ROUTE_TYPES_H +#define SPA_PARAM_ROUTE_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include + +#include + +#define SPA_TYPE_INFO_PARAM_Route SPA_TYPE_INFO_PARAM_BASE "Route" +#define SPA_TYPE_INFO_PARAM_ROUTE_BASE SPA_TYPE_INFO_PARAM_Route ":" + +static const struct spa_type_info spa_type_param_route[] = { + { SPA_PARAM_ROUTE_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_ROUTE_BASE, spa_type_param, }, + { SPA_PARAM_ROUTE_index, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "index", NULL, }, + { SPA_PARAM_ROUTE_direction, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_ROUTE_BASE "direction", spa_type_direction, }, + { SPA_PARAM_ROUTE_device, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "device", NULL, }, + { SPA_PARAM_ROUTE_name, SPA_TYPE_String, SPA_TYPE_INFO_PARAM_ROUTE_BASE "name", NULL, }, + { SPA_PARAM_ROUTE_description, SPA_TYPE_String, SPA_TYPE_INFO_PARAM_ROUTE_BASE "description", NULL, }, + { SPA_PARAM_ROUTE_priority, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "priority", NULL, }, + { SPA_PARAM_ROUTE_available, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_ROUTE_BASE "available", spa_type_param_availability, }, + { SPA_PARAM_ROUTE_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_ROUTE_BASE "info", NULL, }, + { SPA_PARAM_ROUTE_profiles, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "profiles", NULL, }, + { SPA_PARAM_ROUTE_props, SPA_TYPE_OBJECT_Props, SPA_TYPE_INFO_PARAM_ROUTE_BASE "props", NULL, }, + { SPA_PARAM_ROUTE_devices, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "devices", NULL, }, + { SPA_PARAM_ROUTE_profile, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "profile", NULL, }, + { SPA_PARAM_ROUTE_save, SPA_TYPE_Bool, SPA_TYPE_INFO_PARAM_ROUTE_BASE "save", NULL, }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_ROUTE_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/route.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/route.h new file mode 100644 index 0000000000000..ad4f7ff40bf93 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/route.h @@ -0,0 +1,49 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_ROUTE_H +#define SPA_PARAM_ROUTE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +/** properties for SPA_TYPE_OBJECT_ParamRoute */ +enum spa_param_route { + SPA_PARAM_ROUTE_START, + SPA_PARAM_ROUTE_index, /**< index of the routing destination (Int) */ + SPA_PARAM_ROUTE_direction, /**< direction, input/output (Id enum spa_direction) */ + SPA_PARAM_ROUTE_device, /**< device id (Int) */ + SPA_PARAM_ROUTE_name, /**< name of the routing destination (String) */ + SPA_PARAM_ROUTE_description, /**< description of the destination (String) */ + SPA_PARAM_ROUTE_priority, /**< priority of the destination (Int) */ + SPA_PARAM_ROUTE_available, /**< availability of the destination + * (Id enum spa_param_availability) */ + SPA_PARAM_ROUTE_info, /**< info (Struct( + * Int : n_items, + * (String : key, + * String : value)*)) */ + SPA_PARAM_ROUTE_profiles, /**< associated profile indexes (Array of Int) */ + SPA_PARAM_ROUTE_props, /**< properties SPA_TYPE_OBJECT_Props */ + SPA_PARAM_ROUTE_devices, /**< associated device indexes (Array of Int) */ + SPA_PARAM_ROUTE_profile, /**< profile id (Int) */ + SPA_PARAM_ROUTE_save, /**< If route should be saved (Bool) */ +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_ROUTE_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/type-info.h new file mode 100644 index 0000000000000..730a1f53607f2 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/type-info.h @@ -0,0 +1,18 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_TYPE_INFO_H +#define SPA_PARAM_TYPE_INFO_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* SPA_PARAM_TYPE_INFO_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/chroma.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/chroma.h new file mode 100644 index 0000000000000..528018dbd3a65 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/chroma.h @@ -0,0 +1,44 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_CHROMA_H +#define SPA_VIDEO_CHROMA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +/** Various Chroma settings. + */ +enum spa_video_chroma_site { + SPA_VIDEO_CHROMA_SITE_UNKNOWN = 0, /**< unknown cositing */ + SPA_VIDEO_CHROMA_SITE_NONE = (1 << 0), /**< no cositing */ + SPA_VIDEO_CHROMA_SITE_H_COSITED = (1 << 1), /**< chroma is horizontally cosited */ + SPA_VIDEO_CHROMA_SITE_V_COSITED = (1 << 2), /**< chroma is vertically cosited */ + SPA_VIDEO_CHROMA_SITE_ALT_LINE = (1 << 3), /**< chroma samples are sited on alternate lines */ + /* some common chroma cositing */ + /** chroma samples cosited with luma samples */ + SPA_VIDEO_CHROMA_SITE_COSITED = (SPA_VIDEO_CHROMA_SITE_H_COSITED | SPA_VIDEO_CHROMA_SITE_V_COSITED), + /** jpeg style cositing, also for mpeg1 and mjpeg */ + SPA_VIDEO_CHROMA_SITE_JPEG = (SPA_VIDEO_CHROMA_SITE_NONE), + /** mpeg2 style cositing */ + SPA_VIDEO_CHROMA_SITE_MPEG2 = (SPA_VIDEO_CHROMA_SITE_H_COSITED), + /**< DV style cositing */ + SPA_VIDEO_CHROMA_SITE_DV = (SPA_VIDEO_CHROMA_SITE_COSITED | SPA_VIDEO_CHROMA_SITE_ALT_LINE), +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_CHROMA_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/color.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/color.h new file mode 100644 index 0000000000000..0fdb968c84189 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/color.h @@ -0,0 +1,105 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_COLOR_H +#define SPA_VIDEO_COLOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +/** + * Possible color range values. These constants are defined for 8 bit color + * values and can be scaled for other bit depths. + */ +enum spa_video_color_range { + SPA_VIDEO_COLOR_RANGE_UNKNOWN = 0, /**< unknown range */ + SPA_VIDEO_COLOR_RANGE_0_255, /**< [0..255] for 8 bit components */ + SPA_VIDEO_COLOR_RANGE_16_235 /**< [16..235] for 8 bit components. Chroma has + [16..240] range. */ +}; + +/** + * The color matrix is used to convert between Y'PbPr and + * non-linear RGB (R'G'B') + */ +enum spa_video_color_matrix { + SPA_VIDEO_COLOR_MATRIX_UNKNOWN = 0, /**< unknown matrix */ + SPA_VIDEO_COLOR_MATRIX_RGB, /**< identity matrix */ + SPA_VIDEO_COLOR_MATRIX_FCC, /**< FCC color matrix */ + SPA_VIDEO_COLOR_MATRIX_BT709, /**< ITU BT.709 color matrix */ + SPA_VIDEO_COLOR_MATRIX_BT601, /**< ITU BT.601 color matrix */ + SPA_VIDEO_COLOR_MATRIX_SMPTE240M, /**< SMTPE 240M color matrix */ + SPA_VIDEO_COLOR_MATRIX_BT2020, /**< ITU-R BT.2020 color matrix. since 1.6. */ +}; + +/** + * The video transfer function defines the formula for converting between + * non-linear RGB (R'G'B') and linear RGB + */ +enum spa_video_transfer_function { + SPA_VIDEO_TRANSFER_UNKNOWN = 0, /**< unknown transfer function */ + SPA_VIDEO_TRANSFER_GAMMA10, /**< linear RGB, gamma 1.0 curve */ + SPA_VIDEO_TRANSFER_GAMMA18, /**< Gamma 1.8 curve */ + SPA_VIDEO_TRANSFER_GAMMA20, /**< Gamma 2.0 curve */ + SPA_VIDEO_TRANSFER_GAMMA22, /**< Gamma 2.2 curve */ + SPA_VIDEO_TRANSFER_BT709, /**< Gamma 2.2 curve with a linear segment in the lower range */ + SPA_VIDEO_TRANSFER_SMPTE240M, /**< Gamma 2.2 curve with a linear segment in the lower range */ + SPA_VIDEO_TRANSFER_SRGB, /**< Gamma 2.4 curve with a linear segment in the lower range */ + SPA_VIDEO_TRANSFER_GAMMA28, /**< Gamma 2.8 curve */ + SPA_VIDEO_TRANSFER_LOG100, /**< Logarithmic transfer characteristic 100:1 range */ + SPA_VIDEO_TRANSFER_LOG316, /**< Logarithmic transfer characteristic 316.22777:1 range */ + SPA_VIDEO_TRANSFER_BT2020_12, /**< Gamma 2.2 curve with a linear segment in the lower + * range. Used for BT.2020 with 12 bits per + * component. \since 1.6. */ + SPA_VIDEO_TRANSFER_ADOBERGB, /**< Gamma 2.19921875. \since 1.8 */ +}; + +/** + * The color primaries define the how to transform linear RGB values to and from + * the CIE XYZ colorspace. + */ +enum spa_video_color_primaries { + SPA_VIDEO_COLOR_PRIMARIES_UNKNOWN = 0, /**< unknown color primaries */ + SPA_VIDEO_COLOR_PRIMARIES_BT709, /**< BT709 primaries */ + SPA_VIDEO_COLOR_PRIMARIES_BT470M, /**< BT470M primaries */ + SPA_VIDEO_COLOR_PRIMARIES_BT470BG, /**< BT470BG primaries */ + SPA_VIDEO_COLOR_PRIMARIES_SMPTE170M, /**< SMPTE170M primaries */ + SPA_VIDEO_COLOR_PRIMARIES_SMPTE240M, /**< SMPTE240M primaries */ + SPA_VIDEO_COLOR_PRIMARIES_FILM, /**< Generic film */ + SPA_VIDEO_COLOR_PRIMARIES_BT2020, /**< BT2020 primaries. \since 1.6. */ + SPA_VIDEO_COLOR_PRIMARIES_ADOBERGB, /**< Adobe RGB primaries. \since 1.8 */ +}; + +/** + * spa_video_colorimetry: + * + * Structure describing the color info. + */ +struct spa_video_colorimetry { + enum spa_video_color_range range; /**< The color range. This is the valid range for the + * samples. It is used to convert the samples to Y'PbPr + * values. */ + enum spa_video_color_matrix matrix; /**< the color matrix. Used to convert between Y'PbPr and + * non-linear RGB (R'G'B') */ + enum spa_video_transfer_function transfer; /**< The transfer function. Used to convert between + * R'G'B' and RGB */ + enum spa_video_color_primaries primaries; /**< Color primaries. Used to convert between R'G'B' + * and CIE XYZ */ +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_COLOR_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/dsp-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/dsp-utils.h new file mode 100644 index 0000000000000..2d4a83c2928b5 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/dsp-utils.h @@ -0,0 +1,63 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_DSP_UTILS_H +#define SPA_VIDEO_DSP_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include +#include + +static inline int +spa_format_video_dsp_parse(const struct spa_pod *format, + struct spa_video_info_dsp *info) +{ + info->flags = SPA_VIDEO_FLAG_NONE; + if (spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier)) { + info->flags |= SPA_VIDEO_FLAG_MODIFIER; + } + + return spa_pod_parse_object(format, + SPA_TYPE_OBJECT_Format, NULL, + SPA_FORMAT_VIDEO_format, SPA_POD_OPT_Id(&info->format), + SPA_FORMAT_VIDEO_modifier, SPA_POD_OPT_Long(&info->modifier)); +} + +static inline struct spa_pod * +spa_format_video_dsp_build(struct spa_pod_builder *builder, uint32_t id, + struct spa_video_info_dsp *info) +{ + struct spa_pod_frame f; + spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); + spa_pod_builder_add(builder, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp), + 0); + if (info->format != SPA_VIDEO_FORMAT_UNKNOWN) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_format, SPA_POD_Id(info->format), 0); + if (info->modifier != 0 || info->flags & SPA_VIDEO_FLAG_MODIFIER) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_modifier, SPA_POD_Long(info->modifier), 0); + return (struct spa_pod*)spa_pod_builder_pop(builder, &f); +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_DSP_UTILS_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/dsp.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/dsp.h new file mode 100644 index 0000000000000..6bd5d3c5a391c --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/dsp.h @@ -0,0 +1,35 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_DSP_H +#define SPA_VIDEO_DSP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +struct spa_video_info_dsp { + enum spa_video_format format; + uint32_t flags; + uint64_t modifier; +}; + +#define SPA_VIDEO_INFO_DSP_INIT(...) ((struct spa_video_info_dsp) { __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_DSP_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/encoded.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/encoded.h new file mode 100644 index 0000000000000..670da5af3f22e --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/encoded.h @@ -0,0 +1,11 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_ENCODED_H +#define SPA_VIDEO_ENCODED_H + +#include +#include + +#endif /* SPA_VIDEO_ENCODED_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/format-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/format-utils.h new file mode 100644 index 0000000000000..31d33101774bc --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/format-utils.h @@ -0,0 +1,64 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_VIDEO_FORMAT_UTILS_H +#define SPA_PARAM_VIDEO_FORMAT_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include + +static inline int +spa_format_video_parse(const struct spa_pod *format, struct spa_video_info *info) +{ + int res; + + if ((res = spa_format_parse(format, &info->media_type, &info->media_subtype)) < 0) + return res; + + if (info->media_type != SPA_MEDIA_TYPE_video) + return -EINVAL; + + switch (info->media_subtype) { + case SPA_MEDIA_SUBTYPE_raw: + return spa_format_video_raw_parse(format, &info->info.raw); + case SPA_MEDIA_SUBTYPE_dsp: + return spa_format_video_dsp_parse(format, &info->info.dsp); + case SPA_MEDIA_SUBTYPE_h264: + return spa_format_video_h264_parse(format, &info->info.h264); + case SPA_MEDIA_SUBTYPE_mjpg: + return spa_format_video_mjpg_parse(format, &info->info.mjpg); + } + return -ENOTSUP; +} + +static inline struct spa_pod * +spa_format_video_build(struct spa_pod_builder *builder, uint32_t id, struct spa_video_info *info) +{ + switch (info->media_subtype) { + case SPA_MEDIA_SUBTYPE_raw: + return spa_format_video_raw_build(builder, id, &info->info.raw); + case SPA_MEDIA_SUBTYPE_dsp: + return spa_format_video_dsp_build(builder, id, &info->info.dsp); + case SPA_MEDIA_SUBTYPE_h264: + return spa_format_video_h264_build(builder, id, &info->info.h264); + case SPA_MEDIA_SUBTYPE_mjpg: + return spa_format_video_mjpg_build(builder, id, &info->info.mjpg); + } + errno = ENOTSUP; + return NULL; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_VIDEO_FORMAT_UTILS_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/format.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/format.h new file mode 100644 index 0000000000000..b4daf69e03026 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/format.h @@ -0,0 +1,41 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_VIDEO_FORMAT_H +#define SPA_PARAM_VIDEO_FORMAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include +#include +#include + +struct spa_video_info { + uint32_t media_type; + uint32_t media_subtype; + union { + struct spa_video_info_raw raw; + struct spa_video_info_dsp dsp; + struct spa_video_info_h264 h264; + struct spa_video_info_mjpg mjpg; + } info; +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_VIDEO_FORMAT_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/h264-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/h264-utils.h new file mode 100644 index 0000000000000..89c73c98f7ef0 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/h264-utils.h @@ -0,0 +1,70 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_H264_UTILS_H +#define SPA_VIDEO_H264_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include +#include + +static inline int +spa_format_video_h264_parse(const struct spa_pod *format, + struct spa_video_info_h264 *info) +{ + return spa_pod_parse_object(format, + SPA_TYPE_OBJECT_Format, NULL, + SPA_FORMAT_VIDEO_size, SPA_POD_OPT_Rectangle(&info->size), + SPA_FORMAT_VIDEO_framerate, SPA_POD_OPT_Fraction(&info->framerate), + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_OPT_Fraction(&info->max_framerate), + SPA_FORMAT_VIDEO_H264_streamFormat, SPA_POD_OPT_Id(&info->stream_format), + SPA_FORMAT_VIDEO_H264_alignment, SPA_POD_OPT_Id(&info->alignment)); +} + +static inline struct spa_pod * +spa_format_video_h264_build(struct spa_pod_builder *builder, uint32_t id, + struct spa_video_info_h264 *info) +{ + struct spa_pod_frame f; + spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); + spa_pod_builder_add(builder, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_h264), + 0); + if (info->size.width != 0 && info->size.height != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&info->size), 0); + if (info->framerate.denom != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate), 0); + if (info->max_framerate.denom != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(info->max_framerate), 0); + if (info->stream_format != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_H264_streamFormat, SPA_POD_Id(info->stream_format), 0); + if (info->alignment != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_H264_alignment, SPA_POD_Id(info->alignment), 0); + return (struct spa_pod*)spa_pod_builder_pop(builder, &f); +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_H264_UTILS_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/h264.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/h264.h new file mode 100644 index 0000000000000..b5071ff0a355c --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/h264.h @@ -0,0 +1,48 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_H264_H +#define SPA_VIDEO_H264_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +enum spa_h264_stream_format { + SPA_H264_STREAM_FORMAT_UNKNOWN = 0, + SPA_H264_STREAM_FORMAT_AVC, + SPA_H264_STREAM_FORMAT_AVC3, + SPA_H264_STREAM_FORMAT_BYTESTREAM +}; + +enum spa_h264_alignment { + SPA_H264_ALIGNMENT_UNKNOWN = 0, + SPA_H264_ALIGNMENT_AU, + SPA_H264_ALIGNMENT_NAL +}; + +struct spa_video_info_h264 { + struct spa_rectangle size; + struct spa_fraction framerate; + struct spa_fraction max_framerate; + enum spa_h264_stream_format stream_format; + enum spa_h264_alignment alignment; +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_H264_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/mjpg-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/mjpg-utils.h new file mode 100644 index 0000000000000..5cb323e8077d6 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/mjpg-utils.h @@ -0,0 +1,62 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_MJPG_UTILS_H +#define SPA_VIDEO_MJPG_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include +#include + +static inline int +spa_format_video_mjpg_parse(const struct spa_pod *format, + struct spa_video_info_mjpg *info) +{ + return spa_pod_parse_object(format, + SPA_TYPE_OBJECT_Format, NULL, + SPA_FORMAT_VIDEO_size, SPA_POD_OPT_Rectangle(&info->size), + SPA_FORMAT_VIDEO_framerate, SPA_POD_OPT_Fraction(&info->framerate), + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_OPT_Fraction(&info->max_framerate)); +} + +static inline struct spa_pod * +spa_format_video_mjpg_build(struct spa_pod_builder *builder, uint32_t id, + struct spa_video_info_mjpg *info) +{ + struct spa_pod_frame f; + spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); + spa_pod_builder_add(builder, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_mjpg), + 0); + if (info->size.width != 0 && info->size.height != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&info->size), 0); + if (info->framerate.denom != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate), 0); + if (info->max_framerate.denom != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(info->max_framerate), 0); + return (struct spa_pod*)spa_pod_builder_pop(builder, &f); +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_MJPG_UTILS_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/mjpg.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/mjpg.h new file mode 100644 index 0000000000000..59725d7148b9a --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/mjpg.h @@ -0,0 +1,33 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_MJPG_H +#define SPA_VIDEO_MJPG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include + +struct spa_video_info_mjpg { + struct spa_rectangle size; + struct spa_fraction framerate; + struct spa_fraction max_framerate; +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_MJPG_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/multiview.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/multiview.h new file mode 100644 index 0000000000000..10a19323f4407 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/multiview.h @@ -0,0 +1,114 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_MULTIVIEW_H +#define SPA_VIDEO_MULTIVIEW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +/** + * All possible stereoscopic 3D and multiview representations. + * In conjunction with \ref spa_video_multiview_flags, describes how + * multiview content is being transported in the stream. + */ +enum spa_video_multiview_mode { + /** A special value indicating no multiview information. Used in spa_video_info and other + * places to indicate that no specific multiview handling has been requested or provided. + * This value is never carried on caps. */ + SPA_VIDEO_MULTIVIEW_MODE_NONE = -1, + SPA_VIDEO_MULTIVIEW_MODE_MONO = 0, /**< All frames are monoscopic */ + /* Single view modes */ + SPA_VIDEO_MULTIVIEW_MODE_LEFT, /**< All frames represent a left-eye view */ + SPA_VIDEO_MULTIVIEW_MODE_RIGHT, /**< All frames represent a right-eye view */ + /* Stereo view modes */ + SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE, /**< Left and right eye views are provided + * in the left and right half of the frame + * respectively. */ + SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX, /**< Left and right eye views are provided + * in the left and right half of the + * frame, but have been sampled using + * quincunx method, with half-pixel offset + * between the 2 views. */ + SPA_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED, /**< Alternating vertical columns of pixels + * represent the left and right eye view + * respectively. */ + SPA_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED, /**< Alternating horizontal rows of pixels + * represent the left and right eye view + * respectively. */ + SPA_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM, /**< The top half of the frame contains the + * left eye, and the bottom half the right + * eye. */ + SPA_VIDEO_MULTIVIEW_MODE_CHECKERBOARD, /**< Pixels are arranged with alternating + * pixels representing left and right eye + * views in a checkerboard fashion. */ + /* Padding for new frame packing modes */ + + SPA_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME = 32, /**< Left and right eye views are provided + * in separate frames alternately. */ + /* Multiview mode(s) */ + SPA_VIDEO_MULTIVIEW_MODE_MULTIVIEW_FRAME_BY_FRAME, /**< Multipleindependent views are + * provided in separate frames in + * sequence. This method only applies to + * raw video buffers at the moment. + * Specific view identification is via + * \ref spa_video_multiview_meta on raw + * video buffers. */ + SPA_VIDEO_MULTIVIEW_MODE_SEPARATED, /**< Multiple views are provided as separate + * \ref spa_data framebuffers attached + * to each \ref spa_buffer, described + * by the \ref spa_video_multiview_meta */ + /* future expansion for annotated modes */ +}; + +/** + * spa_video_multiview_flags are used to indicate extra properties of a + * stereo/multiview stream beyond the frame layout and buffer mapping + * that is conveyed in the \ref spa_video_multiview_mode. + */ +enum spa_video_multiview_flags { + SPA_VIDEO_MULTIVIEW_FLAGS_NONE = 0, /**< No flags */ + SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST = (1 << 0), /**< For stereo streams, the normal arrangement + * of left and right views is reversed */ + SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED = (1 << 1), /**< The left view is vertically mirrored */ + SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED = (1 << 2), /**< The left view is horizontally mirrored */ + SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED = (1 << 3), /**< The right view is vertically mirrored */ + SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED = (1 << 4), /**< The right view is horizontally mirrored */ + SPA_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT = (1 << 14), /**< For frame-packed multiview + * modes, indicates that the individual + * views have been encoded with half the true + * width or height and should be scaled back + * up for display. This flag is used for + * overriding input layout interpretation + * by adjusting pixel-aspect-ratio. + * For side-by-side, column interleaved or + * checkerboard packings, the + * pixel width will be doubled. + * For row interleaved and + * top-bottom encodings, pixel height will + * be doubled */ + SPA_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO = (1 << 15), /**< The video stream contains both + * mono and multiview portions, + * signalled on each buffer by the + * absence or presence of the + * \ref SPA_VIDEO_BUFFER_FLAG_MULTIPLE_VIEW + * buffer flag. */ +}; + + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_MULTIVIEW_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-types.h new file mode 100644 index 0000000000000..743dd4cd1a5ce --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-types.h @@ -0,0 +1,144 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_RAW_TYPES_H +#define SPA_VIDEO_RAW_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ +#include +#include + +#define SPA_TYPE_INFO_VideoFormat SPA_TYPE_INFO_ENUM_BASE "VideoFormat" +#define SPA_TYPE_INFO_VIDEO_FORMAT_BASE SPA_TYPE_INFO_VideoFormat ":" + +static const struct spa_type_info spa_type_video_format[] = { + { SPA_VIDEO_FORMAT_ENCODED, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "encoded", NULL }, + { SPA_VIDEO_FORMAT_I420, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", NULL }, + { SPA_VIDEO_FORMAT_YV12, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YV12", NULL }, + { SPA_VIDEO_FORMAT_YUY2, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YUY2", NULL }, + { SPA_VIDEO_FORMAT_UYVY, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "UYVY", NULL }, + { SPA_VIDEO_FORMAT_AYUV, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "AYUV", NULL }, + { SPA_VIDEO_FORMAT_RGBx, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBx", NULL }, + { SPA_VIDEO_FORMAT_BGRx, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRx", NULL }, + { SPA_VIDEO_FORMAT_xRGB, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "xRGB", NULL }, + { SPA_VIDEO_FORMAT_xBGR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "xBGR", NULL }, + { SPA_VIDEO_FORMAT_RGBA, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA", NULL }, + { SPA_VIDEO_FORMAT_BGRA, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRA", NULL }, + { SPA_VIDEO_FORMAT_ARGB, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ARGB", NULL }, + { SPA_VIDEO_FORMAT_ABGR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ABGR", NULL }, + { SPA_VIDEO_FORMAT_RGB, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB", NULL }, + { SPA_VIDEO_FORMAT_BGR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGR", NULL }, + { SPA_VIDEO_FORMAT_Y41B, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y41B", NULL }, + { SPA_VIDEO_FORMAT_Y42B, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y42B", NULL }, + { SPA_VIDEO_FORMAT_YVYU, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YVYU", NULL }, + { SPA_VIDEO_FORMAT_Y444, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444", NULL }, + { SPA_VIDEO_FORMAT_v210, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "v210", NULL }, + { SPA_VIDEO_FORMAT_v216, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "v216", NULL }, + { SPA_VIDEO_FORMAT_NV12, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV12", NULL }, + { SPA_VIDEO_FORMAT_NV21, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV21", NULL }, + { SPA_VIDEO_FORMAT_GRAY8, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GRAY8", NULL }, + { SPA_VIDEO_FORMAT_GRAY16_BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GRAY16_BE", NULL }, + { SPA_VIDEO_FORMAT_GRAY16_LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GRAY16_LE", NULL }, + { SPA_VIDEO_FORMAT_v308, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "v308", NULL }, + { SPA_VIDEO_FORMAT_RGB16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB16", NULL }, + { SPA_VIDEO_FORMAT_BGR16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGR16", NULL }, + { SPA_VIDEO_FORMAT_RGB15, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB15", NULL }, + { SPA_VIDEO_FORMAT_BGR15, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGR15", NULL }, + { SPA_VIDEO_FORMAT_UYVP, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "UYVP", NULL }, + { SPA_VIDEO_FORMAT_A420, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A420", NULL }, + { SPA_VIDEO_FORMAT_RGB8P, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB8P", NULL }, + { SPA_VIDEO_FORMAT_YUV9, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YUV9", NULL }, + { SPA_VIDEO_FORMAT_YVU9, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YVU9", NULL }, + { SPA_VIDEO_FORMAT_IYU1, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "IYU1", NULL }, + { SPA_VIDEO_FORMAT_ARGB64, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ARGB64", NULL }, + { SPA_VIDEO_FORMAT_AYUV64, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "AYUV64", NULL }, + { SPA_VIDEO_FORMAT_r210, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "r210", NULL }, + { SPA_VIDEO_FORMAT_I420_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_10BE", NULL }, + { SPA_VIDEO_FORMAT_I420_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_10LE", NULL }, + { SPA_VIDEO_FORMAT_I422_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_10BE", NULL }, + { SPA_VIDEO_FORMAT_I422_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_10LE", NULL }, + { SPA_VIDEO_FORMAT_Y444_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_10BE", NULL }, + { SPA_VIDEO_FORMAT_Y444_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_10LE", NULL }, + { SPA_VIDEO_FORMAT_GBR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR", NULL }, + { SPA_VIDEO_FORMAT_GBR_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_10BE", NULL }, + { SPA_VIDEO_FORMAT_GBR_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_10LE", NULL }, + { SPA_VIDEO_FORMAT_NV16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV16", NULL }, + { SPA_VIDEO_FORMAT_NV24, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV24", NULL }, + { SPA_VIDEO_FORMAT_NV12_64Z32, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV12_64Z32", NULL }, + { SPA_VIDEO_FORMAT_A420_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A420_10BE", NULL }, + { SPA_VIDEO_FORMAT_A420_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A420_10LE", NULL }, + { SPA_VIDEO_FORMAT_A422_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A422_10BE", NULL }, + { SPA_VIDEO_FORMAT_A422_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A422_10LE", NULL }, + { SPA_VIDEO_FORMAT_A444_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A444_10BE", NULL }, + { SPA_VIDEO_FORMAT_A444_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A444_10LE", NULL }, + { SPA_VIDEO_FORMAT_NV61, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV61", NULL }, + { SPA_VIDEO_FORMAT_P010_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "P010_10BE", NULL }, + { SPA_VIDEO_FORMAT_P010_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "P010_10LE", NULL }, + { SPA_VIDEO_FORMAT_IYU2, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "IYU2", NULL }, + { SPA_VIDEO_FORMAT_VYUY, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "VYUY", NULL }, + { SPA_VIDEO_FORMAT_GBRA, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA", NULL }, + { SPA_VIDEO_FORMAT_GBRA_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_10BE", NULL }, + { SPA_VIDEO_FORMAT_GBRA_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_10LE", NULL }, + { SPA_VIDEO_FORMAT_GBR_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_12BE", NULL }, + { SPA_VIDEO_FORMAT_GBR_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_12LE", NULL }, + { SPA_VIDEO_FORMAT_GBRA_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_12BE", NULL }, + { SPA_VIDEO_FORMAT_GBRA_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_12LE", NULL }, + { SPA_VIDEO_FORMAT_I420_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_12BE", NULL }, + { SPA_VIDEO_FORMAT_I420_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_12LE", NULL }, + { SPA_VIDEO_FORMAT_I422_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_12BE", NULL }, + { SPA_VIDEO_FORMAT_I422_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_12LE", NULL }, + { SPA_VIDEO_FORMAT_Y444_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_12BE", NULL }, + { SPA_VIDEO_FORMAT_Y444_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_12LE", NULL }, + { SPA_VIDEO_FORMAT_RGBA_F16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA_F16", NULL }, + { SPA_VIDEO_FORMAT_RGBA_F32, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA_F32", NULL }, + { SPA_VIDEO_FORMAT_xRGB_210LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "xRGB_210LE", NULL }, + { SPA_VIDEO_FORMAT_xBGR_210LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "xBGR_210LE", NULL }, + { SPA_VIDEO_FORMAT_RGBx_102LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBx_102LE", NULL }, + { SPA_VIDEO_FORMAT_BGRx_102LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRx_102LE", NULL }, + { SPA_VIDEO_FORMAT_ARGB_210LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ARGB_210LE", NULL }, + { SPA_VIDEO_FORMAT_ABGR_210LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ABGR_210LE", NULL }, + { SPA_VIDEO_FORMAT_RGBA_102LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA_102LE", NULL }, + { SPA_VIDEO_FORMAT_BGRA_102LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRA_102LE", NULL }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_VideoFlags SPA_TYPE_INFO_FLAGS_BASE "VideoFlags" +#define SPA_TYPE_INFO_VIDEO_FLAGS_BASE SPA_TYPE_INFO_VideoFlags ":" + +static const struct spa_type_info spa_type_video_flags[] = { + + { SPA_VIDEO_FLAG_NONE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FLAGS_BASE "none", NULL }, + { SPA_VIDEO_FLAG_VARIABLE_FPS, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FLAGS_BASE "variable-fps", NULL }, + { SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FLAGS_BASE "premultiplied-alpha", NULL }, + { SPA_VIDEO_FLAG_MODIFIER, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FLAGS_BASE "modifier", NULL }, + { 0, 0, NULL, NULL }, +}; + +#define SPA_TYPE_INFO_VideoInterlaceMode SPA_TYPE_INFO_ENUM_BASE "VideoInterlaceMode" +#define SPA_TYPE_INFO_VIDEO_INTERLACE_MODE_BASE SPA_TYPE_INFO_VideoInterlaceMode ":" + +static const struct spa_type_info spa_type_video_interlace_mode[] = { + { SPA_VIDEO_INTERLACE_MODE_PROGRESSIVE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_INTERLACE_MODE_BASE "progressive", NULL }, + { SPA_VIDEO_INTERLACE_MODE_INTERLEAVED, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_INTERLACE_MODE_BASE "interleaved", NULL }, + { SPA_VIDEO_INTERLACE_MODE_MIXED, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_INTERLACE_MODE_BASE "mixed", NULL }, + { SPA_VIDEO_INTERLACE_MODE_FIELDS, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_INTERLACE_MODE_BASE "fields", NULL }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_RAW_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-utils.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-utils.h new file mode 100644 index 0000000000000..e39c430136d1d --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw-utils.h @@ -0,0 +1,115 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_RAW_UTILS_H +#define SPA_VIDEO_RAW_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include +#include + +static inline int +spa_format_video_raw_parse(const struct spa_pod *format, + struct spa_video_info_raw *info) +{ + info->flags = SPA_VIDEO_FLAG_NONE; + if (spa_pod_find_prop (format, NULL, SPA_FORMAT_VIDEO_modifier)) { + info->flags |= SPA_VIDEO_FLAG_MODIFIER; + } + + return spa_pod_parse_object(format, + SPA_TYPE_OBJECT_Format, NULL, + SPA_FORMAT_VIDEO_format, SPA_POD_OPT_Id(&info->format), + SPA_FORMAT_VIDEO_modifier, SPA_POD_OPT_Long(&info->modifier), + SPA_FORMAT_VIDEO_size, SPA_POD_OPT_Rectangle(&info->size), + SPA_FORMAT_VIDEO_framerate, SPA_POD_OPT_Fraction(&info->framerate), + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_OPT_Fraction(&info->max_framerate), + SPA_FORMAT_VIDEO_views, SPA_POD_OPT_Int(&info->views), + SPA_FORMAT_VIDEO_interlaceMode, SPA_POD_OPT_Id(&info->interlace_mode), + SPA_FORMAT_VIDEO_pixelAspectRatio, SPA_POD_OPT_Fraction(&info->pixel_aspect_ratio), + SPA_FORMAT_VIDEO_multiviewMode, SPA_POD_OPT_Id(&info->multiview_mode), + SPA_FORMAT_VIDEO_multiviewFlags, SPA_POD_OPT_Id(&info->multiview_flags), + SPA_FORMAT_VIDEO_chromaSite, SPA_POD_OPT_Id(&info->chroma_site), + SPA_FORMAT_VIDEO_colorRange, SPA_POD_OPT_Id(&info->color_range), + SPA_FORMAT_VIDEO_colorMatrix, SPA_POD_OPT_Id(&info->color_matrix), + SPA_FORMAT_VIDEO_transferFunction, SPA_POD_OPT_Id(&info->transfer_function), + SPA_FORMAT_VIDEO_colorPrimaries, SPA_POD_OPT_Id(&info->color_primaries)); +} + +static inline struct spa_pod * +spa_format_video_raw_build(struct spa_pod_builder *builder, uint32_t id, + struct spa_video_info_raw *info) +{ + struct spa_pod_frame f; + spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); + spa_pod_builder_add(builder, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + 0); + if (info->format != SPA_VIDEO_FORMAT_UNKNOWN) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_format, SPA_POD_Id(info->format), 0); + if (info->size.width != 0 && info->size.height != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&info->size), 0); + if (info->framerate.denom != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate), 0); + if (info->modifier != 0 || info->flags & SPA_VIDEO_FLAG_MODIFIER) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_modifier, SPA_POD_Long(info->modifier), 0); + if (info->max_framerate.denom != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(info->max_framerate), 0); + if (info->views != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_views, SPA_POD_Int(info->views), 0); + if (info->interlace_mode != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_interlaceMode, SPA_POD_Id(info->interlace_mode), 0); + if (info->pixel_aspect_ratio.denom != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_pixelAspectRatio,SPA_POD_Fraction(info->pixel_aspect_ratio), 0); + if (info->multiview_mode != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_multiviewMode, SPA_POD_Id(info->multiview_mode), 0); + if (info->multiview_flags != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_multiviewFlags,SPA_POD_Id(info->multiview_flags), 0); + if (info->chroma_site != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_chromaSite, SPA_POD_Id(info->chroma_site), 0); + if (info->color_range != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_colorRange, SPA_POD_Id(info->color_range), 0); + if (info->color_matrix != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_colorMatrix, SPA_POD_Id(info->color_matrix), 0); + if (info->transfer_function != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_transferFunction,SPA_POD_Id(info->transfer_function), 0); + if (info->color_primaries != 0) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_colorPrimaries,SPA_POD_Id(info->color_primaries), 0); + return (struct spa_pod*)spa_pod_builder_pop(builder, &f); +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_RAW_UTILS_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw.h new file mode 100644 index 0000000000000..5f5a5b64a5192 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/raw.h @@ -0,0 +1,201 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_RAW_H +#define SPA_VIDEO_RAW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include +#include +#include +#include + +#define SPA_VIDEO_MAX_PLANES 4 +#define SPA_VIDEO_MAX_COMPONENTS 4 + +/** + * Video formats + * + * The components are in general described in big-endian order. There are some + * exceptions (e.g. RGB15 and RGB16) which use the host endianness. + * + * Most of the formats are identical to their GStreamer equivalent. See the + * GStreamer video formats documentation for more details: + * + * https://gstreamer.freedesktop.org/documentation/additional/design/mediatype-video-raw.html#formats + */ +enum spa_video_format { + SPA_VIDEO_FORMAT_UNKNOWN, + SPA_VIDEO_FORMAT_ENCODED, + + SPA_VIDEO_FORMAT_I420, + SPA_VIDEO_FORMAT_YV12, + SPA_VIDEO_FORMAT_YUY2, + SPA_VIDEO_FORMAT_UYVY, + SPA_VIDEO_FORMAT_AYUV, + SPA_VIDEO_FORMAT_RGBx, + SPA_VIDEO_FORMAT_BGRx, + SPA_VIDEO_FORMAT_xRGB, + SPA_VIDEO_FORMAT_xBGR, + SPA_VIDEO_FORMAT_RGBA, + SPA_VIDEO_FORMAT_BGRA, + SPA_VIDEO_FORMAT_ARGB, + SPA_VIDEO_FORMAT_ABGR, + SPA_VIDEO_FORMAT_RGB, + SPA_VIDEO_FORMAT_BGR, + SPA_VIDEO_FORMAT_Y41B, + SPA_VIDEO_FORMAT_Y42B, + SPA_VIDEO_FORMAT_YVYU, + SPA_VIDEO_FORMAT_Y444, + SPA_VIDEO_FORMAT_v210, + SPA_VIDEO_FORMAT_v216, + SPA_VIDEO_FORMAT_NV12, + SPA_VIDEO_FORMAT_NV21, + SPA_VIDEO_FORMAT_GRAY8, + SPA_VIDEO_FORMAT_GRAY16_BE, + SPA_VIDEO_FORMAT_GRAY16_LE, + SPA_VIDEO_FORMAT_v308, + SPA_VIDEO_FORMAT_RGB16, + SPA_VIDEO_FORMAT_BGR16, + SPA_VIDEO_FORMAT_RGB15, + SPA_VIDEO_FORMAT_BGR15, + SPA_VIDEO_FORMAT_UYVP, + SPA_VIDEO_FORMAT_A420, + SPA_VIDEO_FORMAT_RGB8P, + SPA_VIDEO_FORMAT_YUV9, + SPA_VIDEO_FORMAT_YVU9, + SPA_VIDEO_FORMAT_IYU1, + SPA_VIDEO_FORMAT_ARGB64, + SPA_VIDEO_FORMAT_AYUV64, + SPA_VIDEO_FORMAT_r210, + SPA_VIDEO_FORMAT_I420_10BE, + SPA_VIDEO_FORMAT_I420_10LE, + SPA_VIDEO_FORMAT_I422_10BE, + SPA_VIDEO_FORMAT_I422_10LE, + SPA_VIDEO_FORMAT_Y444_10BE, + SPA_VIDEO_FORMAT_Y444_10LE, + SPA_VIDEO_FORMAT_GBR, + SPA_VIDEO_FORMAT_GBR_10BE, + SPA_VIDEO_FORMAT_GBR_10LE, + SPA_VIDEO_FORMAT_NV16, + SPA_VIDEO_FORMAT_NV24, + SPA_VIDEO_FORMAT_NV12_64Z32, + SPA_VIDEO_FORMAT_A420_10BE, + SPA_VIDEO_FORMAT_A420_10LE, + SPA_VIDEO_FORMAT_A422_10BE, + SPA_VIDEO_FORMAT_A422_10LE, + SPA_VIDEO_FORMAT_A444_10BE, + SPA_VIDEO_FORMAT_A444_10LE, + SPA_VIDEO_FORMAT_NV61, + SPA_VIDEO_FORMAT_P010_10BE, + SPA_VIDEO_FORMAT_P010_10LE, + SPA_VIDEO_FORMAT_IYU2, + SPA_VIDEO_FORMAT_VYUY, + SPA_VIDEO_FORMAT_GBRA, + SPA_VIDEO_FORMAT_GBRA_10BE, + SPA_VIDEO_FORMAT_GBRA_10LE, + SPA_VIDEO_FORMAT_GBR_12BE, + SPA_VIDEO_FORMAT_GBR_12LE, + SPA_VIDEO_FORMAT_GBRA_12BE, + SPA_VIDEO_FORMAT_GBRA_12LE, + SPA_VIDEO_FORMAT_I420_12BE, + SPA_VIDEO_FORMAT_I420_12LE, + SPA_VIDEO_FORMAT_I422_12BE, + SPA_VIDEO_FORMAT_I422_12LE, + SPA_VIDEO_FORMAT_Y444_12BE, + SPA_VIDEO_FORMAT_Y444_12LE, + + SPA_VIDEO_FORMAT_RGBA_F16, + SPA_VIDEO_FORMAT_RGBA_F32, + + SPA_VIDEO_FORMAT_xRGB_210LE, /**< 32-bit x:R:G:B 2:10:10:10 little endian */ + SPA_VIDEO_FORMAT_xBGR_210LE, /**< 32-bit x:B:G:R 2:10:10:10 little endian */ + SPA_VIDEO_FORMAT_RGBx_102LE, /**< 32-bit R:G:B:x 10:10:10:2 little endian */ + SPA_VIDEO_FORMAT_BGRx_102LE, /**< 32-bit B:G:R:x 10:10:10:2 little endian */ + SPA_VIDEO_FORMAT_ARGB_210LE, /**< 32-bit A:R:G:B 2:10:10:10 little endian */ + SPA_VIDEO_FORMAT_ABGR_210LE, /**< 32-bit A:B:G:R 2:10:10:10 little endian */ + SPA_VIDEO_FORMAT_RGBA_102LE, /**< 32-bit R:G:B:A 10:10:10:2 little endian */ + SPA_VIDEO_FORMAT_BGRA_102LE, /**< 32-bit B:G:R:A 10:10:10:2 little endian */ + + /* Aliases */ + SPA_VIDEO_FORMAT_DSP_F32 = SPA_VIDEO_FORMAT_RGBA_F32, +}; + +/** + * Extra video flags + */ +enum spa_video_flags { + SPA_VIDEO_FLAG_NONE = 0, /**< no flags */ + SPA_VIDEO_FLAG_VARIABLE_FPS = (1 << 0), /**< a variable fps is selected, fps_n and fps_d + * denote the maximum fps of the video */ + SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA = (1 << 1), /**< Each color has been scaled by the alpha value. */ + SPA_VIDEO_FLAG_MODIFIER = (1 << 2), /**< use the format modifier */ +}; + +/** + * The possible values of the #spa_video_interlace_mode describing the interlace + * mode of the stream. + */ +enum spa_video_interlace_mode { + SPA_VIDEO_INTERLACE_MODE_PROGRESSIVE = 0, /**< all frames are progressive */ + SPA_VIDEO_INTERLACE_MODE_INTERLEAVED, /**< 2 fields are interleaved in one video frame. + * Extra buffer flags describe the field order. */ + SPA_VIDEO_INTERLACE_MODE_MIXED, /**< frames contains both interlaced and progressive + * video, the buffer flags describe the frame and + * fields. */ + SPA_VIDEO_INTERLACE_MODE_FIELDS, /**< 2 fields are stored in one buffer, use the + * frame ID to get access to the required + * field. For multiview (the 'views' + * property > 1) the fields of view N can + * be found at frame ID (N * 2) and (N * + * 2) + 1. Each field has only half the + * amount of lines as noted in the height + * property. This mode requires multiple + * spa_data to describe the fields. */ +}; + +/** + */ +struct spa_video_info_raw { + enum spa_video_format format; /**< the format */ + uint32_t flags; /**< extra video flags */ + uint64_t modifier; /**< format modifier + * only used with DMA-BUF */ + struct spa_rectangle size; /**< the frame size of the video */ + struct spa_fraction framerate; /**< the framerate of the video, 0/1 means variable rate */ + struct spa_fraction max_framerate; /**< the maximum framerate of the video. This is only valid when + \ref framerate is 0/1 */ + uint32_t views; /**< the number of views in this video */ + enum spa_video_interlace_mode interlace_mode; /**< the interlace mode */ + struct spa_fraction pixel_aspect_ratio; /**< the pixel aspect ratio */ + enum spa_video_multiview_mode multiview_mode; /**< multiview mode */ + enum spa_video_multiview_flags multiview_flags; /**< multiview flags */ + enum spa_video_chroma_site chroma_site; /**< the chroma siting */ + enum spa_video_color_range color_range; /**< the color range. This is the valid range for the samples. + * It is used to convert the samples to Y'PbPr values. */ + enum spa_video_color_matrix color_matrix; /**< the color matrix. Used to convert between Y'PbPr and + * non-linear RGB (R'G'B') */ + enum spa_video_transfer_function transfer_function; /**< the transfer function. used to convert between R'G'B' and RGB */ + enum spa_video_color_primaries color_primaries; /**< color primaries. used to convert between R'G'B' and CIE XYZ */ +}; + +#define SPA_VIDEO_INFO_RAW_INIT(...) ((struct spa_video_info_raw) { __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_VIDEO_RAW_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/param/video/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/type-info.h new file mode 100644 index 0000000000000..04e00c9c1afca --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/param/video/type-info.h @@ -0,0 +1,10 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_VIDEO_TYPES_H +#define SPA_VIDEO_TYPES_H + +#include + +#endif /* SPA_VIDEO_TYPES_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/builder.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/builder.h new file mode 100644 index 0000000000000..d9283baa036c4 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/builder.h @@ -0,0 +1,681 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_POD_BUILDER_H +#define SPA_POD_BUILDER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup spa_pod POD + * Binary data serialization format + */ + +/** + * \addtogroup spa_pod + * \{ + */ + +#include + +#include +#include +#include + +struct spa_pod_builder_state { + uint32_t offset; +#define SPA_POD_BUILDER_FLAG_BODY (1<<0) +#define SPA_POD_BUILDER_FLAG_FIRST (1<<1) + uint32_t flags; + struct spa_pod_frame *frame; +}; + +struct spa_pod_builder; + +struct spa_pod_builder_callbacks { +#define SPA_VERSION_POD_BUILDER_CALLBACKS 0 + uint32_t version; + + int (*overflow) (void *data, uint32_t size); +}; + +struct spa_pod_builder { + void *data; + uint32_t size; + uint32_t _padding; + struct spa_pod_builder_state state; + struct spa_callbacks callbacks; +}; + +#define SPA_POD_BUILDER_INIT(buffer,size) ((struct spa_pod_builder){ (buffer), (size), 0, {}, {} }) + +static inline void +spa_pod_builder_get_state(struct spa_pod_builder *builder, struct spa_pod_builder_state *state) +{ + *state = builder->state; +} + +static inline void +spa_pod_builder_set_callbacks(struct spa_pod_builder *builder, + const struct spa_pod_builder_callbacks *callbacks, void *data) +{ + builder->callbacks = SPA_CALLBACKS_INIT(callbacks, data); +} + +static inline void +spa_pod_builder_reset(struct spa_pod_builder *builder, struct spa_pod_builder_state *state) +{ + struct spa_pod_frame *f; + uint32_t size = builder->state.offset - state->offset; + builder->state = *state; + for (f = builder->state.frame; f ; f = f->parent) + f->pod.size -= size; +} + +static inline void spa_pod_builder_init(struct spa_pod_builder *builder, void *data, uint32_t size) +{ + *builder = SPA_POD_BUILDER_INIT(data, size); +} + +static inline struct spa_pod * +spa_pod_builder_deref(struct spa_pod_builder *builder, uint32_t offset) +{ + uint32_t size = builder->size; + if (offset + 8 <= size) { + struct spa_pod *pod = SPA_PTROFF(builder->data, offset, struct spa_pod); + if (offset + SPA_POD_SIZE(pod) <= size) + return pod; + } + return NULL; +} + +static inline struct spa_pod * +spa_pod_builder_frame(struct spa_pod_builder *builder, struct spa_pod_frame *frame) +{ + if (frame->offset + SPA_POD_SIZE(&frame->pod) <= builder->size) + return SPA_PTROFF(builder->data, frame->offset, struct spa_pod); + return NULL; +} + +static inline void +spa_pod_builder_push(struct spa_pod_builder *builder, + struct spa_pod_frame *frame, + const struct spa_pod *pod, + uint32_t offset) +{ + frame->pod = *pod; + frame->offset = offset; + frame->parent = builder->state.frame; + frame->flags = builder->state.flags; + builder->state.frame = frame; + + if (frame->pod.type == SPA_TYPE_Array || frame->pod.type == SPA_TYPE_Choice) + builder->state.flags = SPA_POD_BUILDER_FLAG_FIRST | SPA_POD_BUILDER_FLAG_BODY; +} + +static inline int spa_pod_builder_raw(struct spa_pod_builder *builder, const void *data, uint32_t size) +{ + int res = 0; + struct spa_pod_frame *f; + uint32_t offset = builder->state.offset; + + if (offset + size > builder->size) { + res = -ENOSPC; + if (offset <= builder->size) + spa_callbacks_call_res(&builder->callbacks, + struct spa_pod_builder_callbacks, res, + overflow, 0, offset + size); + } + if (res == 0 && data) + memcpy(SPA_PTROFF(builder->data, offset, void), data, size); + + builder->state.offset += size; + + for (f = builder->state.frame; f ; f = f->parent) + f->pod.size += size; + + return res; +} + +static inline int spa_pod_builder_pad(struct spa_pod_builder *builder, uint32_t size) +{ + uint64_t zeroes = 0; + size = SPA_ROUND_UP_N(size, 8) - size; + return size ? spa_pod_builder_raw(builder, &zeroes, size) : 0; +} + +static inline int +spa_pod_builder_raw_padded(struct spa_pod_builder *builder, const void *data, uint32_t size) +{ + int r, res = spa_pod_builder_raw(builder, data, size); + if ((r = spa_pod_builder_pad(builder, size)) < 0) + res = r; + return res; +} + +static inline void *spa_pod_builder_pop(struct spa_pod_builder *builder, struct spa_pod_frame *frame) +{ + struct spa_pod *pod; + + if (SPA_FLAG_IS_SET(builder->state.flags, SPA_POD_BUILDER_FLAG_FIRST)) { + const struct spa_pod p = { 0, SPA_TYPE_None }; + spa_pod_builder_raw(builder, &p, sizeof(p)); + } + if ((pod = (struct spa_pod*)spa_pod_builder_frame(builder, frame)) != NULL) + *pod = frame->pod; + + builder->state.frame = frame->parent; + builder->state.flags = frame->flags; + spa_pod_builder_pad(builder, builder->state.offset); + return pod; +} + +static inline int +spa_pod_builder_primitive(struct spa_pod_builder *builder, const struct spa_pod *p) +{ + const void *data; + uint32_t size; + int r, res; + + if (builder->state.flags == SPA_POD_BUILDER_FLAG_BODY) { + data = SPA_POD_BODY_CONST(p); + size = SPA_POD_BODY_SIZE(p); + } else { + data = p; + size = SPA_POD_SIZE(p); + SPA_FLAG_CLEAR(builder->state.flags, SPA_POD_BUILDER_FLAG_FIRST); + } + res = spa_pod_builder_raw(builder, data, size); + if (builder->state.flags != SPA_POD_BUILDER_FLAG_BODY) + if ((r = spa_pod_builder_pad(builder, size)) < 0) + res = r; + return res; +} + +#define SPA_POD_INIT(size,type) ((struct spa_pod) { (size), (type) }) + +#define SPA_POD_INIT_None() SPA_POD_INIT(0, SPA_TYPE_None) + +static inline int spa_pod_builder_none(struct spa_pod_builder *builder) +{ + const struct spa_pod p = SPA_POD_INIT_None(); + return spa_pod_builder_primitive(builder, &p); +} + +static inline int spa_pod_builder_child(struct spa_pod_builder *builder, uint32_t size, uint32_t type) +{ + const struct spa_pod p = SPA_POD_INIT(size,type); + SPA_FLAG_CLEAR(builder->state.flags, SPA_POD_BUILDER_FLAG_FIRST); + return spa_pod_builder_raw(builder, &p, sizeof(p)); +} + +#define SPA_POD_INIT_Bool(val) ((struct spa_pod_bool){ { sizeof(uint32_t), SPA_TYPE_Bool }, (val) ? 1 : 0, 0 }) + +static inline int spa_pod_builder_bool(struct spa_pod_builder *builder, bool val) +{ + const struct spa_pod_bool p = SPA_POD_INIT_Bool(val); + return spa_pod_builder_primitive(builder, &p.pod); +} + +#define SPA_POD_INIT_Id(val) ((struct spa_pod_id){ { sizeof(uint32_t), SPA_TYPE_Id }, (val), 0 }) + +static inline int spa_pod_builder_id(struct spa_pod_builder *builder, uint32_t val) +{ + const struct spa_pod_id p = SPA_POD_INIT_Id(val); + return spa_pod_builder_primitive(builder, &p.pod); +} + +#define SPA_POD_INIT_Int(val) ((struct spa_pod_int){ { sizeof(int32_t), SPA_TYPE_Int }, (val), 0 }) + +static inline int spa_pod_builder_int(struct spa_pod_builder *builder, int32_t val) +{ + const struct spa_pod_int p = SPA_POD_INIT_Int(val); + return spa_pod_builder_primitive(builder, &p.pod); +} + +#define SPA_POD_INIT_Long(val) ((struct spa_pod_long){ { sizeof(int64_t), SPA_TYPE_Long }, (val) }) + +static inline int spa_pod_builder_long(struct spa_pod_builder *builder, int64_t val) +{ + const struct spa_pod_long p = SPA_POD_INIT_Long(val); + return spa_pod_builder_primitive(builder, &p.pod); +} + +#define SPA_POD_INIT_Float(val) ((struct spa_pod_float){ { sizeof(float), SPA_TYPE_Float }, (val), 0 }) + +static inline int spa_pod_builder_float(struct spa_pod_builder *builder, float val) +{ + const struct spa_pod_float p = SPA_POD_INIT_Float(val); + return spa_pod_builder_primitive(builder, &p.pod); +} + +#define SPA_POD_INIT_Double(val) ((struct spa_pod_double){ { sizeof(double), SPA_TYPE_Double }, (val) }) + +static inline int spa_pod_builder_double(struct spa_pod_builder *builder, double val) +{ + const struct spa_pod_double p = SPA_POD_INIT_Double(val); + return spa_pod_builder_primitive(builder, &p.pod); +} + +#define SPA_POD_INIT_String(len) ((struct spa_pod_string){ { (len), SPA_TYPE_String } }) + +static inline int +spa_pod_builder_write_string(struct spa_pod_builder *builder, const char *str, uint32_t len) +{ + int r, res; + res = spa_pod_builder_raw(builder, str, len); + if ((r = spa_pod_builder_raw(builder, "", 1)) < 0) + res = r; + if ((r = spa_pod_builder_pad(builder, builder->state.offset)) < 0) + res = r; + return res; +} + +static inline int +spa_pod_builder_string_len(struct spa_pod_builder *builder, const char *str, uint32_t len) +{ + const struct spa_pod_string p = SPA_POD_INIT_String(len+1); + int r, res = spa_pod_builder_raw(builder, &p, sizeof(p)); + if ((r = spa_pod_builder_write_string(builder, str, len)) < 0) + res = r; + return res; +} + +static inline int spa_pod_builder_string(struct spa_pod_builder *builder, const char *str) +{ + uint32_t len = str ? strlen(str) : 0; + return spa_pod_builder_string_len(builder, str ? str : "", len); +} + +#define SPA_POD_INIT_Bytes(len) ((struct spa_pod_bytes){ { (len), SPA_TYPE_Bytes } }) + +static inline int +spa_pod_builder_bytes(struct spa_pod_builder *builder, const void *bytes, uint32_t len) +{ + const struct spa_pod_bytes p = SPA_POD_INIT_Bytes(len); + int r, res = spa_pod_builder_raw(builder, &p, sizeof(p)); + if ((r = spa_pod_builder_raw_padded(builder, bytes, len)) < 0) + res = r; + return res; +} +static inline void * +spa_pod_builder_reserve_bytes(struct spa_pod_builder *builder, uint32_t len) +{ + uint32_t offset = builder->state.offset; + if (spa_pod_builder_bytes(builder, NULL, len) < 0) + return NULL; + return SPA_POD_BODY(spa_pod_builder_deref(builder, offset)); +} + +#define SPA_POD_INIT_Pointer(type,value) ((struct spa_pod_pointer){ { sizeof(struct spa_pod_pointer_body), SPA_TYPE_Pointer }, { (type), 0, (value) } }) + +static inline int +spa_pod_builder_pointer(struct spa_pod_builder *builder, uint32_t type, const void *val) +{ + const struct spa_pod_pointer p = SPA_POD_INIT_Pointer(type, val); + return spa_pod_builder_primitive(builder, &p.pod); +} + +#define SPA_POD_INIT_Fd(fd) ((struct spa_pod_fd){ { sizeof(int64_t), SPA_TYPE_Fd }, (fd) }) + +static inline int spa_pod_builder_fd(struct spa_pod_builder *builder, int64_t fd) +{ + const struct spa_pod_fd p = SPA_POD_INIT_Fd(fd); + return spa_pod_builder_primitive(builder, &p.pod); +} + +#define SPA_POD_INIT_Rectangle(val) ((struct spa_pod_rectangle){ { sizeof(struct spa_rectangle), SPA_TYPE_Rectangle }, (val) }) + +static inline int +spa_pod_builder_rectangle(struct spa_pod_builder *builder, uint32_t width, uint32_t height) +{ + const struct spa_pod_rectangle p = SPA_POD_INIT_Rectangle(SPA_RECTANGLE(width, height)); + return spa_pod_builder_primitive(builder, &p.pod); +} + +#define SPA_POD_INIT_Fraction(val) ((struct spa_pod_fraction){ { sizeof(struct spa_fraction), SPA_TYPE_Fraction }, (val) }) + +static inline int +spa_pod_builder_fraction(struct spa_pod_builder *builder, uint32_t num, uint32_t denom) +{ + const struct spa_pod_fraction p = SPA_POD_INIT_Fraction(SPA_FRACTION(num, denom)); + return spa_pod_builder_primitive(builder, &p.pod); +} + +static inline int +spa_pod_builder_push_array(struct spa_pod_builder *builder, struct spa_pod_frame *frame) +{ + const struct spa_pod_array p = + { {sizeof(struct spa_pod_array_body) - sizeof(struct spa_pod), SPA_TYPE_Array}, + {{0, 0}} }; + uint32_t offset = builder->state.offset; + int res = spa_pod_builder_raw(builder, &p, sizeof(p) - sizeof(struct spa_pod)); + spa_pod_builder_push(builder, frame, &p.pod, offset); + return res; +} + +static inline int +spa_pod_builder_array(struct spa_pod_builder *builder, + uint32_t child_size, uint32_t child_type, uint32_t n_elems, const void *elems) +{ + const struct spa_pod_array p = { + {(uint32_t)(sizeof(struct spa_pod_array_body) + n_elems * child_size), SPA_TYPE_Array}, + {{child_size, child_type}} + }; + int r, res = spa_pod_builder_raw(builder, &p, sizeof(p)); + if ((r = spa_pod_builder_raw_padded(builder, elems, child_size * n_elems)) < 0) + res = r; + return res; +} + +#define SPA_POD_INIT_CHOICE_BODY(type, flags, child_size, child_type) \ + ((struct spa_pod_choice_body) { (type), (flags), { (child_size), (child_type) }}) + +#define SPA_POD_INIT_Choice(type, ctype, child_type, n_vals, ...) \ + ((struct { struct spa_pod_choice choice; ctype vals[(n_vals)];}) \ + { { { (n_vals) * sizeof(ctype) + sizeof(struct spa_pod_choice_body), SPA_TYPE_Choice }, \ + { (type), 0, { sizeof(ctype), (child_type) } } }, { __VA_ARGS__ } }) + +static inline int +spa_pod_builder_push_choice(struct spa_pod_builder *builder, struct spa_pod_frame *frame, + uint32_t type, uint32_t flags) +{ + const struct spa_pod_choice p = + { {sizeof(struct spa_pod_choice_body) - sizeof(struct spa_pod), SPA_TYPE_Choice}, + { type, flags, {0, 0}} }; + uint32_t offset = builder->state.offset; + int res = spa_pod_builder_raw(builder, &p, sizeof(p) - sizeof(struct spa_pod)); + spa_pod_builder_push(builder, frame, &p.pod, offset); + return res; +} + +#define SPA_POD_INIT_Struct(size) ((struct spa_pod_struct){ { (size), SPA_TYPE_Struct } }) + +static inline int +spa_pod_builder_push_struct(struct spa_pod_builder *builder, struct spa_pod_frame *frame) +{ + const struct spa_pod_struct p = SPA_POD_INIT_Struct(0); + uint32_t offset = builder->state.offset; + int res = spa_pod_builder_raw(builder, &p, sizeof(p)); + spa_pod_builder_push(builder, frame, &p.pod, offset); + return res; +} + +#define SPA_POD_INIT_Object(size,type,id,...) ((struct spa_pod_object){ { (size), SPA_TYPE_Object }, { (type), (id) }, ##__VA_ARGS__ }) + +static inline int +spa_pod_builder_push_object(struct spa_pod_builder *builder, struct spa_pod_frame *frame, + uint32_t type, uint32_t id) +{ + const struct spa_pod_object p = + SPA_POD_INIT_Object(sizeof(struct spa_pod_object_body), type, id); + uint32_t offset = builder->state.offset; + int res = spa_pod_builder_raw(builder, &p, sizeof(p)); + spa_pod_builder_push(builder, frame, &p.pod, offset); + return res; +} + +#define SPA_POD_INIT_Prop(key,flags,size,type) \ + ((struct spa_pod_prop){ (key), (flags), { (size), (type) } }) + +static inline int +spa_pod_builder_prop(struct spa_pod_builder *builder, uint32_t key, uint32_t flags) +{ + const struct { uint32_t key; uint32_t flags; } p = { key, flags }; + return spa_pod_builder_raw(builder, &p, sizeof(p)); +} + +#define SPA_POD_INIT_Sequence(size,unit) \ + ((struct spa_pod_sequence){ { (size), SPA_TYPE_Sequence}, {(unit), 0 } }) + +static inline int +spa_pod_builder_push_sequence(struct spa_pod_builder *builder, struct spa_pod_frame *frame, uint32_t unit) +{ + const struct spa_pod_sequence p = + SPA_POD_INIT_Sequence(sizeof(struct spa_pod_sequence_body), unit); + uint32_t offset = builder->state.offset; + int res = spa_pod_builder_raw(builder, &p, sizeof(p)); + spa_pod_builder_push(builder, frame, &p.pod, offset); + return res; +} + +static inline uint32_t +spa_pod_builder_control(struct spa_pod_builder *builder, uint32_t offset, uint32_t type) +{ + const struct { uint32_t offset; uint32_t type; } p = { offset, type }; + return spa_pod_builder_raw(builder, &p, sizeof(p)); +} + +static inline uint32_t spa_choice_from_id(char id) +{ + switch (id) { + case 'r': + return SPA_CHOICE_Range; + case 's': + return SPA_CHOICE_Step; + case 'e': + return SPA_CHOICE_Enum; + case 'f': + return SPA_CHOICE_Flags; + case 'n': + default: + return SPA_CHOICE_None; + } +} + +#define SPA_POD_BUILDER_COLLECT(builder,type,args) \ +do { \ + switch (type) { \ + case 'b': \ + spa_pod_builder_bool(builder, !!va_arg(args, int)); \ + break; \ + case 'I': \ + spa_pod_builder_id(builder, va_arg(args, uint32_t)); \ + break; \ + case 'i': \ + spa_pod_builder_int(builder, va_arg(args, int)); \ + break; \ + case 'l': \ + spa_pod_builder_long(builder, va_arg(args, int64_t)); \ + break; \ + case 'f': \ + spa_pod_builder_float(builder, va_arg(args, double)); \ + break; \ + case 'd': \ + spa_pod_builder_double(builder, va_arg(args, double)); \ + break; \ + case 's': \ + { \ + char *strval = va_arg(args, char *); \ + if (strval != NULL) { \ + size_t len = strlen(strval); \ + spa_pod_builder_string_len(builder, strval, len); \ + } \ + else \ + spa_pod_builder_none(builder); \ + break; \ + } \ + case 'S': \ + { \ + char *strval = va_arg(args, char *); \ + size_t len = va_arg(args, int); \ + spa_pod_builder_string_len(builder, strval, len); \ + break; \ + } \ + case 'y': \ + { \ + void *ptr = va_arg(args, void *); \ + int len = va_arg(args, int); \ + spa_pod_builder_bytes(builder, ptr, len); \ + break; \ + } \ + case 'R': \ + { \ + struct spa_rectangle *rectval = \ + va_arg(args, struct spa_rectangle *); \ + spa_pod_builder_rectangle(builder, \ + rectval->width, rectval->height); \ + break; \ + } \ + case 'F': \ + { \ + struct spa_fraction *fracval = \ + va_arg(args, struct spa_fraction *); \ + spa_pod_builder_fraction(builder, fracval->num, fracval->denom);\ + break; \ + } \ + case 'a': \ + { \ + int child_size = va_arg(args, int); \ + int child_type = va_arg(args, int); \ + int n_elems = va_arg(args, int); \ + void *elems = va_arg(args, void *); \ + spa_pod_builder_array(builder, child_size, \ + child_type, n_elems, elems); \ + break; \ + } \ + case 'p': \ + { \ + int t = va_arg(args, uint32_t); \ + spa_pod_builder_pointer(builder, t, va_arg(args, void *)); \ + break; \ + } \ + case 'h': \ + spa_pod_builder_fd(builder, va_arg(args, int)); \ + break; \ + case 'P': \ + case 'O': \ + case 'T': \ + case 'V': \ + { \ + struct spa_pod *pod = va_arg(args, struct spa_pod *); \ + if (pod == NULL) \ + spa_pod_builder_none(builder); \ + else \ + spa_pod_builder_primitive(builder, pod); \ + break; \ + } \ + } \ +} while(false) + +static inline int +spa_pod_builder_addv(struct spa_pod_builder *builder, va_list args) +{ + int res = 0; + struct spa_pod_frame *frame = builder->state.frame; + uint32_t ftype = frame ? frame->pod.type : (uint32_t)SPA_TYPE_None; + + do { + const char *format; + int n_values = 1; + struct spa_pod_frame f; + bool choice; + + switch (ftype) { + case SPA_TYPE_Object: + { + uint32_t key = va_arg(args, uint32_t); + if (key == 0) + goto exit; + spa_pod_builder_prop(builder, key, 0); + break; + } + case SPA_TYPE_Sequence: + { + uint32_t offset = va_arg(args, uint32_t); + uint32_t type = va_arg(args, uint32_t); + if (type == 0) + goto exit; + spa_pod_builder_control(builder, offset, type); + SPA_FALLTHROUGH + } + default: + break; + } + if ((format = va_arg(args, const char *)) == NULL) + break; + + choice = *format == '?'; + if (choice) { + uint32_t type = spa_choice_from_id(*++format); + if (*format != '\0') + format++; + + spa_pod_builder_push_choice(builder, &f, type, 0); + + n_values = va_arg(args, int); + } + while (n_values-- > 0) + SPA_POD_BUILDER_COLLECT(builder, *format, args); + + if (choice) + spa_pod_builder_pop(builder, &f); + } while (true); + + exit: + return res; +} + +static inline int spa_pod_builder_add(struct spa_pod_builder *builder, ...) +{ + int res; + va_list args; + + va_start(args, builder); + res = spa_pod_builder_addv(builder, args); + va_end(args); + + return res; +} + +#define spa_pod_builder_add_object(b,type,id,...) \ +({ \ + struct spa_pod_builder *_b = (b); \ + struct spa_pod_frame _f; \ + spa_pod_builder_push_object(_b, &_f, type, id); \ + spa_pod_builder_add(_b, ##__VA_ARGS__, 0); \ + spa_pod_builder_pop(_b, &_f); \ +}) + +#define spa_pod_builder_add_struct(b,...) \ +({ \ + struct spa_pod_builder *_b = (b); \ + struct spa_pod_frame _f; \ + spa_pod_builder_push_struct(_b, &_f); \ + spa_pod_builder_add(_b, ##__VA_ARGS__, NULL); \ + spa_pod_builder_pop(_b, &_f); \ +}) + +#define spa_pod_builder_add_sequence(b,unit,...) \ +({ \ + struct spa_pod_builder *_b = (b); \ + struct spa_pod_frame _f; \ + spa_pod_builder_push_sequence(_b, &_f, unit); \ + spa_pod_builder_add(_b, ##__VA_ARGS__, 0, 0); \ + spa_pod_builder_pop(_b, &_f); \ +}) + +/** Copy a pod structure */ +static inline struct spa_pod * +spa_pod_copy(const struct spa_pod *pod) +{ + size_t size; + struct spa_pod *c; + + size = SPA_POD_SIZE(pod); + if ((c = (struct spa_pod *) malloc(size)) == NULL) + return NULL; + return (struct spa_pod *) memcpy(c, pod, size); +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_POD_BUILDER_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/command.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/command.h new file mode 100644 index 0000000000000..9290a44b8db1a --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/command.h @@ -0,0 +1,49 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_COMMAND_H +#define SPA_COMMAND_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * \addtogroup spa_pod + * \{ + */ + +struct spa_command_body { + struct spa_pod_object_body body; +}; + +struct spa_command { + struct spa_pod pod; + struct spa_command_body body; +}; + +#define SPA_COMMAND_TYPE(cmd) ((cmd)->body.body.type) +#define SPA_COMMAND_ID(cmd,type) (SPA_COMMAND_TYPE(cmd) == (type) ? \ + (cmd)->body.body.id : SPA_ID_INVALID) + +#define SPA_COMMAND_INIT_FULL(t,size,type,id,...) ((t) \ + { { (size), SPA_TYPE_Object }, \ + { { (type), (id) }, ##__VA_ARGS__ } }) + +#define SPA_COMMAND_INIT(type,id) \ + SPA_COMMAND_INIT_FULL(struct spa_command, \ + sizeof(struct spa_command_body), type, id) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_COMMAND_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/event.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/event.h new file mode 100644 index 0000000000000..23a75a2fd439d --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/event.h @@ -0,0 +1,48 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_EVENT_H +#define SPA_EVENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * \addtogroup spa_pod + * \{ + */ + +struct spa_event_body { + struct spa_pod_object_body body; +}; + +struct spa_event { + struct spa_pod pod; + struct spa_event_body body; +}; + +#define SPA_EVENT_TYPE(ev) ((ev)->body.body.type) +#define SPA_EVENT_ID(ev,type) (SPA_EVENT_TYPE(ev) == (type) ? \ + (ev)->body.body.id : SPA_ID_INVALID) + +#define SPA_EVENT_INIT_FULL(t,size,type,id,...) ((t) \ + { { (size), SPA_TYPE_OBJECT }, \ + { { (type), (id) }, ##__VA_ARGS__ } }) \ + +#define SPA_EVENT_INIT(type,id) \ + SPA_EVENT_INIT_FULL(struct spa_event, \ + sizeof(struct spa_event_body), type, id) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_EVENT_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/iter.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/iter.h new file mode 100644 index 0000000000000..3dd24a8eb4425 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/iter.h @@ -0,0 +1,455 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_POD_ITER_H +#define SPA_POD_ITER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +/** + * \addtogroup spa_pod + * \{ + */ + +struct spa_pod_frame { + struct spa_pod pod; + struct spa_pod_frame *parent; + uint32_t offset; + uint32_t flags; +}; + +static inline bool spa_pod_is_inside(const void *pod, uint32_t size, const void *iter) +{ + return SPA_POD_BODY(iter) <= SPA_PTROFF(pod, size, void) && + SPA_PTROFF(iter, SPA_POD_SIZE(iter), void) <= SPA_PTROFF(pod, size, void); +} + +static inline void *spa_pod_next(const void *iter) +{ + return SPA_PTROFF(iter, SPA_ROUND_UP_N(SPA_POD_SIZE(iter), 8), void); +} + +static inline struct spa_pod_prop *spa_pod_prop_first(const struct spa_pod_object_body *body) +{ + return SPA_PTROFF(body, sizeof(struct spa_pod_object_body), struct spa_pod_prop); +} + +static inline bool spa_pod_prop_is_inside(const struct spa_pod_object_body *body, + uint32_t size, const struct spa_pod_prop *iter) +{ + return SPA_POD_CONTENTS(struct spa_pod_prop, iter) <= SPA_PTROFF(body, size, void) && + SPA_PTROFF(iter, SPA_POD_PROP_SIZE(iter), void) <= SPA_PTROFF(body, size, void); +} + +static inline struct spa_pod_prop *spa_pod_prop_next(const struct spa_pod_prop *iter) +{ + return SPA_PTROFF(iter, SPA_ROUND_UP_N(SPA_POD_PROP_SIZE(iter), 8), struct spa_pod_prop); +} + +static inline struct spa_pod_control *spa_pod_control_first(const struct spa_pod_sequence_body *body) +{ + return SPA_PTROFF(body, sizeof(struct spa_pod_sequence_body), struct spa_pod_control); +} + +static inline bool spa_pod_control_is_inside(const struct spa_pod_sequence_body *body, + uint32_t size, const struct spa_pod_control *iter) +{ + return SPA_POD_CONTENTS(struct spa_pod_control, iter) <= SPA_PTROFF(body, size, void) && + SPA_PTROFF(iter, SPA_POD_CONTROL_SIZE(iter), void) <= SPA_PTROFF(body, size, void); +} + +static inline struct spa_pod_control *spa_pod_control_next(const struct spa_pod_control *iter) +{ + return SPA_PTROFF(iter, SPA_ROUND_UP_N(SPA_POD_CONTROL_SIZE(iter), 8), struct spa_pod_control); +} + +#define SPA_POD_ARRAY_BODY_FOREACH(body, _size, iter) \ + for ((iter) = (__typeof__(iter))SPA_PTROFF((body), sizeof(struct spa_pod_array_body), void); \ + (iter) < (__typeof__(iter))SPA_PTROFF((body), (_size), void); \ + (iter) = (__typeof__(iter))SPA_PTROFF((iter), (body)->child.size, void)) + +#define SPA_POD_ARRAY_FOREACH(obj, iter) \ + SPA_POD_ARRAY_BODY_FOREACH(&(obj)->body, SPA_POD_BODY_SIZE(obj), iter) + +#define SPA_POD_CHOICE_BODY_FOREACH(body, _size, iter) \ + for ((iter) = (__typeof__(iter))SPA_PTROFF((body), sizeof(struct spa_pod_choice_body), void); \ + (iter) < (__typeof__(iter))SPA_PTROFF((body), (_size), void); \ + (iter) = (__typeof__(iter))SPA_PTROFF((iter), (body)->child.size, void)) + +#define SPA_POD_CHOICE_FOREACH(obj, iter) \ + SPA_POD_CHOICE_BODY_FOREACH(&(obj)->body, SPA_POD_BODY_SIZE(obj), iter) + +#define SPA_POD_FOREACH(pod, size, iter) \ + for ((iter) = (pod); \ + spa_pod_is_inside(pod, size, iter); \ + (iter) = (__typeof__(iter))spa_pod_next(iter)) + +#define SPA_POD_STRUCT_FOREACH(obj, iter) \ + SPA_POD_FOREACH(SPA_POD_BODY(obj), SPA_POD_BODY_SIZE(obj), iter) + +#define SPA_POD_OBJECT_BODY_FOREACH(body, size, iter) \ + for ((iter) = spa_pod_prop_first(body); \ + spa_pod_prop_is_inside(body, size, iter); \ + (iter) = spa_pod_prop_next(iter)) + +#define SPA_POD_OBJECT_FOREACH(obj, iter) \ + SPA_POD_OBJECT_BODY_FOREACH(&(obj)->body, SPA_POD_BODY_SIZE(obj), iter) + +#define SPA_POD_SEQUENCE_BODY_FOREACH(body, size, iter) \ + for ((iter) = spa_pod_control_first(body); \ + spa_pod_control_is_inside(body, size, iter); \ + (iter) = spa_pod_control_next(iter)) + +#define SPA_POD_SEQUENCE_FOREACH(seq, iter) \ + SPA_POD_SEQUENCE_BODY_FOREACH(&(seq)->body, SPA_POD_BODY_SIZE(seq), iter) + + +static inline void *spa_pod_from_data(void *data, size_t maxsize, off_t offset, size_t size) +{ + void *pod; + if (size < sizeof(struct spa_pod) || offset + size > maxsize) + return NULL; + pod = SPA_PTROFF(data, offset, void); + if (SPA_POD_SIZE(pod) > size) + return NULL; + return pod; +} + +static inline int spa_pod_is_none(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_None); +} + +static inline int spa_pod_is_bool(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Bool && SPA_POD_BODY_SIZE(pod) >= sizeof(int32_t)); +} + +static inline int spa_pod_get_bool(const struct spa_pod *pod, bool *value) +{ + if (!spa_pod_is_bool(pod)) + return -EINVAL; + *value = !!SPA_POD_VALUE(struct spa_pod_bool, pod); + return 0; +} + +static inline int spa_pod_is_id(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Id && SPA_POD_BODY_SIZE(pod) >= sizeof(uint32_t)); +} + +static inline int spa_pod_get_id(const struct spa_pod *pod, uint32_t *value) +{ + if (!spa_pod_is_id(pod)) + return -EINVAL; + *value = SPA_POD_VALUE(struct spa_pod_id, pod); + return 0; +} + +static inline int spa_pod_is_int(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Int && SPA_POD_BODY_SIZE(pod) >= sizeof(int32_t)); +} + +static inline int spa_pod_get_int(const struct spa_pod *pod, int32_t *value) +{ + if (!spa_pod_is_int(pod)) + return -EINVAL; + *value = SPA_POD_VALUE(struct spa_pod_int, pod); + return 0; +} + +static inline int spa_pod_is_long(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Long && SPA_POD_BODY_SIZE(pod) >= sizeof(int64_t)); +} + +static inline int spa_pod_get_long(const struct spa_pod *pod, int64_t *value) +{ + if (!spa_pod_is_long(pod)) + return -EINVAL; + *value = SPA_POD_VALUE(struct spa_pod_long, pod); + return 0; +} + +static inline int spa_pod_is_float(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Float && SPA_POD_BODY_SIZE(pod) >= sizeof(float)); +} + +static inline int spa_pod_get_float(const struct spa_pod *pod, float *value) +{ + if (!spa_pod_is_float(pod)) + return -EINVAL; + *value = SPA_POD_VALUE(struct spa_pod_float, pod); + return 0; +} + +static inline int spa_pod_is_double(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Double && SPA_POD_BODY_SIZE(pod) >= sizeof(double)); +} + +static inline int spa_pod_get_double(const struct spa_pod *pod, double *value) +{ + if (!spa_pod_is_double(pod)) + return -EINVAL; + *value = SPA_POD_VALUE(struct spa_pod_double, pod); + return 0; +} + +static inline int spa_pod_is_string(const struct spa_pod *pod) +{ + const char *s = (const char *)SPA_POD_CONTENTS(struct spa_pod_string, pod); + return (SPA_POD_TYPE(pod) == SPA_TYPE_String && + SPA_POD_BODY_SIZE(pod) > 0 && + s[SPA_POD_BODY_SIZE(pod)-1] == '\0'); +} + +static inline int spa_pod_get_string(const struct spa_pod *pod, const char **value) +{ + if (!spa_pod_is_string(pod)) + return -EINVAL; + *value = (const char *)SPA_POD_CONTENTS(struct spa_pod_string, pod); + return 0; +} + +static inline int spa_pod_copy_string(const struct spa_pod *pod, size_t maxlen, char *dest) +{ + const char *s = (const char *)SPA_POD_CONTENTS(struct spa_pod_string, pod); + if (!spa_pod_is_string(pod) || maxlen < 1) + return -EINVAL; + strncpy(dest, s, maxlen-1); + dest[maxlen-1]= '\0'; + return 0; +} + +static inline int spa_pod_is_bytes(const struct spa_pod *pod) +{ + return SPA_POD_TYPE(pod) == SPA_TYPE_Bytes; +} + +static inline int spa_pod_get_bytes(const struct spa_pod *pod, const void **value, uint32_t *len) +{ + if (!spa_pod_is_bytes(pod)) + return -EINVAL; + *value = (const void *)SPA_POD_CONTENTS(struct spa_pod_bytes, pod); + *len = SPA_POD_BODY_SIZE(pod); + return 0; +} + +static inline int spa_pod_is_pointer(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Pointer && + SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_pointer_body)); +} + +static inline int spa_pod_get_pointer(const struct spa_pod *pod, uint32_t *type, const void **value) +{ + if (!spa_pod_is_pointer(pod)) + return -EINVAL; + *type = ((struct spa_pod_pointer*)pod)->body.type; + *value = ((struct spa_pod_pointer*)pod)->body.value; + return 0; +} + +static inline int spa_pod_is_fd(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Fd && + SPA_POD_BODY_SIZE(pod) >= sizeof(int64_t)); +} + +static inline int spa_pod_get_fd(const struct spa_pod *pod, int64_t *value) +{ + if (!spa_pod_is_fd(pod)) + return -EINVAL; + *value = SPA_POD_VALUE(struct spa_pod_fd, pod); + return 0; +} + +static inline int spa_pod_is_rectangle(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Rectangle && + SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_rectangle)); +} + +static inline int spa_pod_get_rectangle(const struct spa_pod *pod, struct spa_rectangle *value) +{ + if (!spa_pod_is_rectangle(pod)) + return -EINVAL; + *value = SPA_POD_VALUE(struct spa_pod_rectangle, pod); + return 0; +} + +static inline int spa_pod_is_fraction(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Fraction && + SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_fraction)); +} + +static inline int spa_pod_get_fraction(const struct spa_pod *pod, struct spa_fraction *value) +{ + spa_return_val_if_fail(spa_pod_is_fraction(pod), -EINVAL); + *value = SPA_POD_VALUE(struct spa_pod_fraction, pod); + return 0; +} + +static inline int spa_pod_is_bitmap(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Bitmap && + SPA_POD_BODY_SIZE(pod) >= sizeof(uint8_t)); +} + +static inline int spa_pod_is_array(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Array && + SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_array_body)); +} + +static inline void *spa_pod_get_array(const struct spa_pod *pod, uint32_t *n_values) +{ + spa_return_val_if_fail(spa_pod_is_array(pod), NULL); + *n_values = SPA_POD_ARRAY_N_VALUES(pod); + return SPA_POD_ARRAY_VALUES(pod); +} + +static inline uint32_t spa_pod_copy_array(const struct spa_pod *pod, uint32_t type, + void *values, uint32_t max_values) +{ + uint32_t n_values; + void *v = spa_pod_get_array(pod, &n_values); + if (v == NULL || max_values == 0 || SPA_POD_ARRAY_VALUE_TYPE(pod) != type) + return 0; + n_values = SPA_MIN(n_values, max_values); + memcpy(values, v, SPA_POD_ARRAY_VALUE_SIZE(pod) * n_values); + return n_values; +} + +static inline int spa_pod_is_choice(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Choice && + SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_choice_body)); +} + +static inline struct spa_pod *spa_pod_get_values(const struct spa_pod *pod, uint32_t *n_vals, uint32_t *choice) +{ + if (pod->type == SPA_TYPE_Choice) { + *n_vals = SPA_POD_CHOICE_N_VALUES(pod); + if ((*choice = SPA_POD_CHOICE_TYPE(pod)) == SPA_CHOICE_None) + *n_vals = SPA_MIN(1u, SPA_POD_CHOICE_N_VALUES(pod)); + return (struct spa_pod*)SPA_POD_CHOICE_CHILD(pod); + } else { + *n_vals = 1; + *choice = SPA_CHOICE_None; + return (struct spa_pod*)pod; + } +} + +static inline int spa_pod_is_struct(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Struct); +} + +static inline int spa_pod_is_object(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Object && + SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_object_body)); +} + +static inline bool spa_pod_is_object_type(const struct spa_pod *pod, uint32_t type) +{ + return (pod && spa_pod_is_object(pod) && SPA_POD_OBJECT_TYPE(pod) == type); +} + +static inline bool spa_pod_is_object_id(const struct spa_pod *pod, uint32_t id) +{ + return (pod && spa_pod_is_object(pod) && SPA_POD_OBJECT_ID(pod) == id); +} + +static inline int spa_pod_is_sequence(const struct spa_pod *pod) +{ + return (SPA_POD_TYPE(pod) == SPA_TYPE_Sequence && + SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_sequence_body)); +} + +static inline const struct spa_pod_prop *spa_pod_object_find_prop(const struct spa_pod_object *pod, + const struct spa_pod_prop *start, uint32_t key) +{ + const struct spa_pod_prop *first, *res; + + first = spa_pod_prop_first(&pod->body); + start = start ? spa_pod_prop_next(start) : first; + + for (res = start; spa_pod_prop_is_inside(&pod->body, pod->pod.size, res); + res = spa_pod_prop_next(res)) { + if (res->key == key) + return res; + } + for (res = first; res != start; res = spa_pod_prop_next(res)) { + if (res->key == key) + return res; + } + return NULL; +} + +static inline const struct spa_pod_prop *spa_pod_find_prop(const struct spa_pod *pod, + const struct spa_pod_prop *start, uint32_t key) +{ + if (!spa_pod_is_object(pod)) + return NULL; + return spa_pod_object_find_prop((const struct spa_pod_object *)pod, start, key); +} + +static inline int spa_pod_object_fixate(struct spa_pod_object *pod) +{ + struct spa_pod_prop *res; + SPA_POD_OBJECT_FOREACH(pod, res) { + if (res->value.type == SPA_TYPE_Choice && + !SPA_FLAG_IS_SET(res->flags, SPA_POD_PROP_FLAG_DONT_FIXATE)) + ((struct spa_pod_choice*)&res->value)->body.type = SPA_CHOICE_None; + } + return 0; +} + +static inline int spa_pod_fixate(struct spa_pod *pod) +{ + if (!spa_pod_is_object(pod)) + return -EINVAL; + return spa_pod_object_fixate((struct spa_pod_object *)pod); +} + +static inline int spa_pod_object_is_fixated(const struct spa_pod_object *pod) +{ + struct spa_pod_prop *res; + SPA_POD_OBJECT_FOREACH(pod, res) { + if (res->value.type == SPA_TYPE_Choice && + ((struct spa_pod_choice*)&res->value)->body.type != SPA_CHOICE_None) + return 0; + } + return 1; +} + +static inline int spa_pod_is_fixated(const struct spa_pod *pod) +{ + if (!spa_pod_is_object(pod)) + return -EINVAL; + return spa_pod_object_is_fixated((const struct spa_pod_object *)pod); +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_POD_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/parser.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/parser.h new file mode 100644 index 0000000000000..76c73e2e6cae9 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/parser.h @@ -0,0 +1,574 @@ +/* Spa */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_POD_PARSER_H +#define SPA_POD_PARSER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include + +/** + * \addtogroup spa_pod + * \{ + */ + +struct spa_pod_parser_state { + uint32_t offset; + uint32_t flags; + struct spa_pod_frame *frame; +}; + +struct spa_pod_parser { + const void *data; + uint32_t size; + uint32_t _padding; + struct spa_pod_parser_state state; +}; + +#define SPA_POD_PARSER_INIT(buffer,size) ((struct spa_pod_parser){ (buffer), (size), 0, {} }) + +static inline void spa_pod_parser_init(struct spa_pod_parser *parser, + const void *data, uint32_t size) +{ + *parser = SPA_POD_PARSER_INIT(data, size); +} + +static inline void spa_pod_parser_pod(struct spa_pod_parser *parser, + const struct spa_pod *pod) +{ + spa_pod_parser_init(parser, pod, SPA_POD_SIZE(pod)); +} + +static inline void +spa_pod_parser_get_state(struct spa_pod_parser *parser, struct spa_pod_parser_state *state) +{ + *state = parser->state; +} + +static inline void +spa_pod_parser_reset(struct spa_pod_parser *parser, struct spa_pod_parser_state *state) +{ + parser->state = *state; +} + +static inline struct spa_pod * +spa_pod_parser_deref(struct spa_pod_parser *parser, uint32_t offset, uint32_t size) +{ + /* Cast to uint64_t to avoid wraparound. Add 8 for the pod itself. */ + const uint64_t long_offset = (uint64_t)offset + 8; + if (long_offset <= size && (offset & 7) == 0) { + /* Use void* because creating a misaligned pointer is undefined. */ + void *pod = SPA_PTROFF(parser->data, offset, void); + /* + * Check that the pointer is aligned and that the size (rounded + * to the next multiple of 8) is in bounds. + */ + if (SPA_IS_ALIGNED(pod, __alignof__(struct spa_pod)) && + long_offset + SPA_ROUND_UP_N((uint64_t)SPA_POD_BODY_SIZE(pod), 8) <= size) + return (struct spa_pod *)pod; + } + return NULL; +} + +static inline struct spa_pod *spa_pod_parser_frame(struct spa_pod_parser *parser, struct spa_pod_frame *frame) +{ + return SPA_PTROFF(parser->data, frame->offset, struct spa_pod); +} + +static inline void spa_pod_parser_push(struct spa_pod_parser *parser, + struct spa_pod_frame *frame, const struct spa_pod *pod, uint32_t offset) +{ + frame->pod = *pod; + frame->offset = offset; + frame->parent = parser->state.frame; + frame->flags = parser->state.flags; + parser->state.frame = frame; +} + +static inline struct spa_pod *spa_pod_parser_current(struct spa_pod_parser *parser) +{ + struct spa_pod_frame *f = parser->state.frame; + uint32_t size = f ? f->offset + SPA_POD_SIZE(&f->pod) : parser->size; + return spa_pod_parser_deref(parser, parser->state.offset, size); +} + +static inline void spa_pod_parser_advance(struct spa_pod_parser *parser, const struct spa_pod *pod) +{ + parser->state.offset += SPA_ROUND_UP_N(SPA_POD_SIZE(pod), 8); +} + +static inline struct spa_pod *spa_pod_parser_next(struct spa_pod_parser *parser) +{ + struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod) + spa_pod_parser_advance(parser, pod); + return pod; +} + +static inline int spa_pod_parser_pop(struct spa_pod_parser *parser, + struct spa_pod_frame *frame) +{ + parser->state.frame = frame->parent; + parser->state.offset = frame->offset + SPA_ROUND_UP_N(SPA_POD_SIZE(&frame->pod), 8); + return 0; +} + +static inline int spa_pod_parser_get_bool(struct spa_pod_parser *parser, bool *value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_bool(pod, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_id(struct spa_pod_parser *parser, uint32_t *value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_id(pod, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_int(struct spa_pod_parser *parser, int32_t *value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_int(pod, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_long(struct spa_pod_parser *parser, int64_t *value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_long(pod, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_float(struct spa_pod_parser *parser, float *value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_float(pod, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_double(struct spa_pod_parser *parser, double *value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_double(pod, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_string(struct spa_pod_parser *parser, const char **value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_string(pod, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_bytes(struct spa_pod_parser *parser, const void **value, uint32_t *len) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_bytes(pod, value, len)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_pointer(struct spa_pod_parser *parser, uint32_t *type, const void **value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_pointer(pod, type, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_fd(struct spa_pod_parser *parser, int64_t *value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_fd(pod, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_rectangle(struct spa_pod_parser *parser, struct spa_rectangle *value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_rectangle(pod, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_fraction(struct spa_pod_parser *parser, struct spa_fraction *value) +{ + int res = -EPIPE; + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod != NULL && (res = spa_pod_get_fraction(pod, value)) >= 0) + spa_pod_parser_advance(parser, pod); + return res; +} + +static inline int spa_pod_parser_get_pod(struct spa_pod_parser *parser, struct spa_pod **value) +{ + struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod == NULL) + return -EPIPE; + *value = pod; + spa_pod_parser_advance(parser, pod); + return 0; +} +static inline int spa_pod_parser_push_struct(struct spa_pod_parser *parser, + struct spa_pod_frame *frame) +{ + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod == NULL) + return -EPIPE; + if (!spa_pod_is_struct(pod)) + return -EINVAL; + spa_pod_parser_push(parser, frame, pod, parser->state.offset); + parser->state.offset += sizeof(struct spa_pod_struct); + return 0; +} + +static inline int spa_pod_parser_push_object(struct spa_pod_parser *parser, + struct spa_pod_frame *frame, uint32_t type, uint32_t *id) +{ + const struct spa_pod *pod = spa_pod_parser_current(parser); + if (pod == NULL) + return -EPIPE; + if (!spa_pod_is_object(pod)) + return -EINVAL; + if (type != SPA_POD_OBJECT_TYPE(pod)) + return -EPROTO; + if (id != NULL) + *id = SPA_POD_OBJECT_ID(pod); + spa_pod_parser_push(parser, frame, pod, parser->state.offset); + parser->state.offset = parser->size; + return 0; +} + +static inline bool spa_pod_parser_can_collect(const struct spa_pod *pod, char type) +{ + if (pod == NULL) + return false; + + if (SPA_POD_TYPE(pod) == SPA_TYPE_Choice) { + if (!spa_pod_is_choice(pod)) + return false; + if (type == 'V') + return true; + if (SPA_POD_CHOICE_TYPE(pod) != SPA_CHOICE_None) + return false; + pod = SPA_POD_CHOICE_CHILD(pod); + } + + switch (type) { + case 'P': + return true; + case 'b': + return spa_pod_is_bool(pod); + case 'I': + return spa_pod_is_id(pod); + case 'i': + return spa_pod_is_int(pod); + case 'l': + return spa_pod_is_long(pod); + case 'f': + return spa_pod_is_float(pod); + case 'd': + return spa_pod_is_double(pod); + case 's': + return spa_pod_is_string(pod) || spa_pod_is_none(pod); + case 'S': + return spa_pod_is_string(pod); + case 'y': + return spa_pod_is_bytes(pod); + case 'R': + return spa_pod_is_rectangle(pod); + case 'F': + return spa_pod_is_fraction(pod); + case 'B': + return spa_pod_is_bitmap(pod); + case 'a': + return spa_pod_is_array(pod); + case 'p': + return spa_pod_is_pointer(pod); + case 'h': + return spa_pod_is_fd(pod); + case 'T': + return spa_pod_is_struct(pod) || spa_pod_is_none(pod); + case 'O': + return spa_pod_is_object(pod) || spa_pod_is_none(pod); + case 'V': + default: + return false; + } +} + +#define SPA_POD_PARSER_COLLECT(pod,_type,args) \ +do { \ + switch (_type) { \ + case 'b': \ + *va_arg(args, bool*) = SPA_POD_VALUE(struct spa_pod_bool, pod); \ + break; \ + case 'I': \ + case 'i': \ + *va_arg(args, int32_t*) = SPA_POD_VALUE(struct spa_pod_int, pod); \ + break; \ + case 'l': \ + *va_arg(args, int64_t*) = SPA_POD_VALUE(struct spa_pod_long, pod); \ + break; \ + case 'f': \ + *va_arg(args, float*) = SPA_POD_VALUE(struct spa_pod_float, pod); \ + break; \ + case 'd': \ + *va_arg(args, double*) = SPA_POD_VALUE(struct spa_pod_double, pod); \ + break; \ + case 's': \ + *va_arg(args, char**) = \ + ((pod) == NULL || (SPA_POD_TYPE(pod) == SPA_TYPE_None) \ + ? NULL \ + : (char *)SPA_POD_CONTENTS(struct spa_pod_string, pod)); \ + break; \ + case 'S': \ + { \ + char *dest = va_arg(args, char*); \ + uint32_t maxlen = va_arg(args, uint32_t); \ + strncpy(dest, (char *)SPA_POD_CONTENTS(struct spa_pod_string, pod), maxlen-1); \ + dest[maxlen-1] = '\0'; \ + break; \ + } \ + case 'y': \ + *(va_arg(args, void **)) = SPA_POD_CONTENTS(struct spa_pod_bytes, pod); \ + *(va_arg(args, uint32_t *)) = SPA_POD_BODY_SIZE(pod); \ + break; \ + case 'R': \ + *va_arg(args, struct spa_rectangle*) = \ + SPA_POD_VALUE(struct spa_pod_rectangle, pod); \ + break; \ + case 'F': \ + *va_arg(args, struct spa_fraction*) = \ + SPA_POD_VALUE(struct spa_pod_fraction, pod); \ + break; \ + case 'B': \ + *va_arg(args, uint32_t **) = \ + (uint32_t *) SPA_POD_CONTENTS(struct spa_pod_bitmap, pod); \ + break; \ + case 'a': \ + *va_arg(args, uint32_t*) = SPA_POD_ARRAY_VALUE_SIZE(pod); \ + *va_arg(args, uint32_t*) = SPA_POD_ARRAY_VALUE_TYPE(pod); \ + *va_arg(args, uint32_t*) = SPA_POD_ARRAY_N_VALUES(pod); \ + *va_arg(args, void**) = SPA_POD_ARRAY_VALUES(pod); \ + break; \ + case 'p': \ + { \ + struct spa_pod_pointer_body *b = \ + (struct spa_pod_pointer_body *) SPA_POD_BODY(pod); \ + *(va_arg(args, uint32_t *)) = b->type; \ + *(va_arg(args, const void **)) = b->value; \ + break; \ + } \ + case 'h': \ + *va_arg(args, int64_t*) = SPA_POD_VALUE(struct spa_pod_fd, pod); \ + break; \ + case 'P': \ + case 'T': \ + case 'O': \ + case 'V': \ + { \ + const struct spa_pod **d = va_arg(args, const struct spa_pod**); \ + if (d) \ + *d = ((pod) == NULL || (SPA_POD_TYPE(pod) == SPA_TYPE_None) \ + ? NULL : (pod)); \ + break; \ + } \ + default: \ + break; \ + } \ +} while(false) + +#define SPA_POD_PARSER_SKIP(_type,args) \ +do { \ + switch (_type) { \ + case 'S': \ + va_arg(args, char*); \ + va_arg(args, uint32_t); \ + break; \ + case 'a': \ + va_arg(args, void*); \ + va_arg(args, void*); \ + SPA_FALLTHROUGH \ + case 'p': \ + case 'y': \ + va_arg(args, void*); \ + SPA_FALLTHROUGH \ + case 'b': \ + case 'I': \ + case 'i': \ + case 'l': \ + case 'f': \ + case 'd': \ + case 's': \ + case 'R': \ + case 'F': \ + case 'B': \ + case 'h': \ + case 'V': \ + case 'P': \ + case 'T': \ + case 'O': \ + va_arg(args, void*); \ + break; \ + } \ +} while(false) + +static inline int spa_pod_parser_getv(struct spa_pod_parser *parser, va_list args) +{ + struct spa_pod_frame *f = parser->state.frame; + uint32_t ftype = f ? f->pod.type : (uint32_t)SPA_TYPE_Struct; + const struct spa_pod_prop *prop = NULL; + int count = 0; + + do { + bool optional; + const struct spa_pod *pod = NULL; + const char *format; + + if (ftype == SPA_TYPE_Object) { + uint32_t key = va_arg(args, uint32_t); + const struct spa_pod_object *object; + + if (key == 0) + break; + + object = (const struct spa_pod_object *)spa_pod_parser_frame(parser, f); + prop = spa_pod_object_find_prop(object, prop, key); + pod = prop ? &prop->value : NULL; + } + + if ((format = va_arg(args, char *)) == NULL) + break; + + if (ftype == SPA_TYPE_Struct) + pod = spa_pod_parser_next(parser); + + if ((optional = (*format == '?'))) + format++; + + if (!spa_pod_parser_can_collect(pod, *format)) { + if (!optional) { + if (pod == NULL) + return -ESRCH; + else + return -EPROTO; + } + SPA_POD_PARSER_SKIP(*format, args); + } else { + if (pod->type == SPA_TYPE_Choice && *format != 'V') + pod = SPA_POD_CHOICE_CHILD(pod); + + SPA_POD_PARSER_COLLECT(pod, *format, args); + count++; + } + } while (true); + + return count; +} + +static inline int spa_pod_parser_get(struct spa_pod_parser *parser, ...) +{ + int res; + va_list args; + + va_start(args, parser); + res = spa_pod_parser_getv(parser, args); + va_end(args); + + return res; +} + +#define SPA_POD_OPT_Bool(val) "?" SPA_POD_Bool(val) +#define SPA_POD_OPT_Id(val) "?" SPA_POD_Id(val) +#define SPA_POD_OPT_Int(val) "?" SPA_POD_Int(val) +#define SPA_POD_OPT_Long(val) "?" SPA_POD_Long(val) +#define SPA_POD_OPT_Float(val) "?" SPA_POD_Float(val) +#define SPA_POD_OPT_Double(val) "?" SPA_POD_Double(val) +#define SPA_POD_OPT_String(val) "?" SPA_POD_String(val) +#define SPA_POD_OPT_Stringn(val,len) "?" SPA_POD_Stringn(val,len) +#define SPA_POD_OPT_Bytes(val,len) "?" SPA_POD_Bytes(val,len) +#define SPA_POD_OPT_Rectangle(val) "?" SPA_POD_Rectangle(val) +#define SPA_POD_OPT_Fraction(val) "?" SPA_POD_Fraction(val) +#define SPA_POD_OPT_Array(csize,ctype,n_vals,vals) "?" SPA_POD_Array(csize,ctype,n_vals,vals) +#define SPA_POD_OPT_Pointer(type,val) "?" SPA_POD_Pointer(type,val) +#define SPA_POD_OPT_Fd(val) "?" SPA_POD_Fd(val) +#define SPA_POD_OPT_Pod(val) "?" SPA_POD_Pod(val) +#define SPA_POD_OPT_PodObject(val) "?" SPA_POD_PodObject(val) +#define SPA_POD_OPT_PodStruct(val) "?" SPA_POD_PodStruct(val) +#define SPA_POD_OPT_PodChoice(val) "?" SPA_POD_PodChoice(val) + +#define spa_pod_parser_get_object(p,type,id,...) \ +({ \ + struct spa_pod_frame _f; \ + int _res; \ + if ((_res = spa_pod_parser_push_object(p, &_f, type, id)) == 0) { \ + _res = spa_pod_parser_get(p,##__VA_ARGS__, 0); \ + spa_pod_parser_pop(p, &_f); \ + } \ + _res; \ +}) + +#define spa_pod_parser_get_struct(p,...) \ +({ \ + struct spa_pod_frame _f; \ + int _res; \ + if ((_res = spa_pod_parser_push_struct(p, &_f)) == 0) { \ + _res = spa_pod_parser_get(p,##__VA_ARGS__, NULL); \ + spa_pod_parser_pop(p, &_f); \ + } \ + _res; \ +}) + +#define spa_pod_parse_object(pod,type,id,...) \ +({ \ + struct spa_pod_parser _p; \ + spa_pod_parser_pod(&_p, pod); \ + spa_pod_parser_get_object(&_p,type,id,##__VA_ARGS__); \ +}) + +#define spa_pod_parse_struct(pod,...) \ +({ \ + struct spa_pod_parser _p; \ + spa_pod_parser_pod(&_p, pod); \ + spa_pod_parser_get_struct(&_p,##__VA_ARGS__); \ +}) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_POD_PARSER_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/pod.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/pod.h new file mode 100644 index 0000000000000..9d54d57522a34 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/pod.h @@ -0,0 +1,226 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_POD_H +#define SPA_POD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * \addtogroup spa_pod + * \{ + */ + +#define SPA_POD_BODY_SIZE(pod) (((struct spa_pod*)(pod))->size) +#define SPA_POD_TYPE(pod) (((struct spa_pod*)(pod))->type) +#define SPA_POD_SIZE(pod) ((uint64_t)sizeof(struct spa_pod) + SPA_POD_BODY_SIZE(pod)) +#define SPA_POD_CONTENTS_SIZE(type,pod) (SPA_POD_SIZE(pod)-sizeof(type)) + +#define SPA_POD_CONTENTS(type,pod) SPA_PTROFF((pod),sizeof(type),void) +#define SPA_POD_CONTENTS_CONST(type,pod) SPA_PTROFF((pod),sizeof(type),const void) +#define SPA_POD_BODY(pod) SPA_PTROFF((pod),sizeof(struct spa_pod),void) +#define SPA_POD_BODY_CONST(pod) SPA_PTROFF((pod),sizeof(struct spa_pod),const void) + +struct spa_pod { + uint32_t size; /* size of the body */ + uint32_t type; /* a basic id of enum spa_type */ +}; + +#define SPA_POD_VALUE(type,pod) (((type*)(pod))->value) + +struct spa_pod_bool { + struct spa_pod pod; + int32_t value; + int32_t _padding; +}; + +struct spa_pod_id { + struct spa_pod pod; + uint32_t value; + int32_t _padding; +}; + +struct spa_pod_int { + struct spa_pod pod; + int32_t value; + int32_t _padding; +}; + +struct spa_pod_long { + struct spa_pod pod; + int64_t value; +}; + +struct spa_pod_float { + struct spa_pod pod; + float value; + int32_t _padding; +}; + +struct spa_pod_double { + struct spa_pod pod; + double value; +}; + +struct spa_pod_string { + struct spa_pod pod; + /* value here */ +}; + +struct spa_pod_bytes { + struct spa_pod pod; + /* value here */ +}; + +struct spa_pod_rectangle { + struct spa_pod pod; + struct spa_rectangle value; +}; + +struct spa_pod_fraction { + struct spa_pod pod; + struct spa_fraction value; +}; + +struct spa_pod_bitmap { + struct spa_pod pod; + /* array of uint8_t follows with the bitmap */ +}; + +#define SPA_POD_ARRAY_CHILD(arr) (&((struct spa_pod_array*)(arr))->body.child) +#define SPA_POD_ARRAY_VALUE_TYPE(arr) (SPA_POD_TYPE(SPA_POD_ARRAY_CHILD(arr))) +#define SPA_POD_ARRAY_VALUE_SIZE(arr) (SPA_POD_BODY_SIZE(SPA_POD_ARRAY_CHILD(arr))) +#define SPA_POD_ARRAY_N_VALUES(arr) (SPA_POD_ARRAY_VALUE_SIZE(arr) ? ((SPA_POD_BODY_SIZE(arr) - sizeof(struct spa_pod_array_body)) / SPA_POD_ARRAY_VALUE_SIZE(arr)) : 0) +#define SPA_POD_ARRAY_VALUES(arr) SPA_POD_CONTENTS(struct spa_pod_array, arr) + +struct spa_pod_array_body { + struct spa_pod child; + /* array with elements of child.size follows */ +}; + +struct spa_pod_array { + struct spa_pod pod; + struct spa_pod_array_body body; +}; + +#define SPA_POD_CHOICE_CHILD(choice) (&((struct spa_pod_choice*)(choice))->body.child) +#define SPA_POD_CHOICE_TYPE(choice) (((struct spa_pod_choice*)(choice))->body.type) +#define SPA_POD_CHOICE_FLAGS(choice) (((struct spa_pod_choice*)(choice))->body.flags) +#define SPA_POD_CHOICE_VALUE_TYPE(choice) (SPA_POD_TYPE(SPA_POD_CHOICE_CHILD(choice))) +#define SPA_POD_CHOICE_VALUE_SIZE(choice) (SPA_POD_BODY_SIZE(SPA_POD_CHOICE_CHILD(choice))) +#define SPA_POD_CHOICE_N_VALUES(choice) (SPA_POD_CHOICE_VALUE_SIZE(choice) ? ((SPA_POD_BODY_SIZE(choice) - sizeof(struct spa_pod_choice_body)) / SPA_POD_CHOICE_VALUE_SIZE(choice)) : 0) +#define SPA_POD_CHOICE_VALUES(choice) (SPA_POD_CONTENTS(struct spa_pod_choice, choice)) + +enum spa_choice_type { + SPA_CHOICE_None, /**< no choice, first value is current */ + SPA_CHOICE_Range, /**< range: default, min, max */ + SPA_CHOICE_Step, /**< range with step: default, min, max, step */ + SPA_CHOICE_Enum, /**< list: default, alternative,... */ + SPA_CHOICE_Flags, /**< flags: default, possible flags,... */ +}; + +struct spa_pod_choice_body { + uint32_t type; /**< type of choice, one of enum spa_choice_type */ + uint32_t flags; /**< extra flags */ + struct spa_pod child; + /* array with elements of child.size follows. Note that there might be more + * elements than required by \a type, which should be ignored. */ +}; + +struct spa_pod_choice { + struct spa_pod pod; + struct spa_pod_choice_body body; +}; + +struct spa_pod_struct { + struct spa_pod pod; + /* one or more spa_pod follow */ +}; + +#define SPA_POD_OBJECT_TYPE(obj) (((struct spa_pod_object*)(obj))->body.type) +#define SPA_POD_OBJECT_ID(obj) (((struct spa_pod_object*)(obj))->body.id) + +struct spa_pod_object_body { + uint32_t type; /**< one of enum spa_type */ + uint32_t id; /**< id of the object, depends on the object type */ + /* contents follow, series of spa_pod_prop */ +}; + +struct spa_pod_object { + struct spa_pod pod; + struct spa_pod_object_body body; +}; + +struct spa_pod_pointer_body { + uint32_t type; /**< pointer id, one of enum spa_type */ + uint32_t _padding; + const void *value; +}; + +struct spa_pod_pointer { + struct spa_pod pod; + struct spa_pod_pointer_body body; +}; + +struct spa_pod_fd { + struct spa_pod pod; + int64_t value; +}; + +#define SPA_POD_PROP_SIZE(prop) (sizeof(struct spa_pod_prop) + (prop)->value.size) + +/* props can be inside an object */ +struct spa_pod_prop { + uint32_t key; /**< key of property, list of valid keys depends on the + * object type */ +#define SPA_POD_PROP_FLAG_READONLY (1u<<0) /**< is read-only */ +#define SPA_POD_PROP_FLAG_HARDWARE (1u<<1) /**< some sort of hardware parameter */ +#define SPA_POD_PROP_FLAG_HINT_DICT (1u<<2) /**< contains a dictionary struct as + * (Struct( + * Int : n_items, + * (String : key, + * String : value)*)) */ +#define SPA_POD_PROP_FLAG_MANDATORY (1u<<3) /**< is mandatory */ +#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u<<4) /**< choices need no fixation */ + uint32_t flags; /**< flags for property */ + struct spa_pod value; + /* value follows */ +}; + +#define SPA_POD_CONTROL_SIZE(ev) (sizeof(struct spa_pod_control) + (ev)->value.size) + +/* controls can be inside a sequence and mark timed values */ +struct spa_pod_control { + uint32_t offset; /**< media offset */ + uint32_t type; /**< type of control, enum spa_control_type */ + struct spa_pod value; /**< control value, depends on type */ + /* value contents follow */ +}; + +struct spa_pod_sequence_body { + uint32_t unit; + uint32_t pad; + /* series of struct spa_pod_control follows */ +}; + +/** a sequence of timed controls */ +struct spa_pod_sequence { + struct spa_pod pod; + struct spa_pod_sequence_body body; +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_POD_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/pod/vararg.h b/src/java.desktop/unix/native/libpipewire/include/spa/pod/vararg.h new file mode 100644 index 0000000000000..b64761604ba5e --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/pod/vararg.h @@ -0,0 +1,93 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_POD_VARARG_H +#define SPA_POD_VARARG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/** + * \addtogroup spa_pod + * \{ + */ + +#define SPA_POD_Prop(key,...) \ + key, ##__VA_ARGS__ + +#define SPA_POD_Control(offset,type,...) \ + offset, type, ##__VA_ARGS__ + +#define SPA_CHOICE_RANGE(def,min,max) 3,(def),(min),(max) +#define SPA_CHOICE_STEP(def,min,max,step) 4,(def),(min),(max),(step) +#define SPA_CHOICE_ENUM(n_vals,...) (n_vals),##__VA_ARGS__ +#define SPA_CHOICE_FLAGS(flags) 1, (flags) +#define SPA_CHOICE_BOOL(def) 3,(def),(def),!(def) + +#define SPA_POD_Bool(val) "b", val +#define SPA_POD_CHOICE_Bool(def) "?eb", SPA_CHOICE_BOOL(def) + +#define SPA_POD_Id(val) "I", val +#define SPA_POD_CHOICE_ENUM_Id(n_vals,...) "?eI", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__) + +#define SPA_POD_Int(val) "i", val +#define SPA_POD_CHOICE_ENUM_Int(n_vals,...) "?ei", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__) +#define SPA_POD_CHOICE_RANGE_Int(def,min,max) "?ri", SPA_CHOICE_RANGE(def, min, max) +#define SPA_POD_CHOICE_STEP_Int(def,min,max,step) "?si", SPA_CHOICE_STEP(def, min, max, step) +#define SPA_POD_CHOICE_FLAGS_Int(flags) "?fi", SPA_CHOICE_FLAGS(flags) + +#define SPA_POD_Long(val) "l", val +#define SPA_POD_CHOICE_ENUM_Long(n_vals,...) "?el", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__) +#define SPA_POD_CHOICE_RANGE_Long(def,min,max) "?rl", SPA_CHOICE_RANGE(def, min, max) +#define SPA_POD_CHOICE_STEP_Long(def,min,max,step) "?sl", SPA_CHOICE_STEP(def, min, max, step) +#define SPA_POD_CHOICE_FLAGS_Long(flags) "?fl", SPA_CHOICE_FLAGS(flags) + +#define SPA_POD_Float(val) "f", val +#define SPA_POD_CHOICE_ENUM_Float(n_vals,...) "?ef", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__) +#define SPA_POD_CHOICE_RANGE_Float(def,min,max) "?rf", SPA_CHOICE_RANGE(def, min, max) +#define SPA_POD_CHOICE_STEP_Float(def,min,max,step) "?sf", SPA_CHOICE_STEP(def, min, max, step) + +#define SPA_POD_Double(val) "d", val +#define SPA_POD_CHOICE_ENUM_Double(n_vals,...) "?ed", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__) +#define SPA_POD_CHOICE_RANGE_Double(def,min,max) "?rd", SPA_CHOICE_RANGE(def, min, max) +#define SPA_POD_CHOICE_STEP_Double(def,min,max,step) "?sd", SPA_CHOICE_STEP(def, min, max, step) + +#define SPA_POD_String(val) "s",val +#define SPA_POD_Stringn(val,len) "S",val,len + +#define SPA_POD_Bytes(val,len) "y",val,len + +#define SPA_POD_Rectangle(val) "R",val +#define SPA_POD_CHOICE_ENUM_Rectangle(n_vals,...) "?eR", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__) +#define SPA_POD_CHOICE_RANGE_Rectangle(def,min,max) "?rR", SPA_CHOICE_RANGE((def),(min),(max)) +#define SPA_POD_CHOICE_STEP_Rectangle(def,min,max,step) "?sR", SPA_CHOICE_STEP((def),(min),(max),(step)) + +#define SPA_POD_Fraction(val) "F",val +#define SPA_POD_CHOICE_ENUM_Fraction(n_vals,...) "?eF", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__) +#define SPA_POD_CHOICE_RANGE_Fraction(def,min,max) "?rF", SPA_CHOICE_RANGE((def),(min),(max)) +#define SPA_POD_CHOICE_STEP_Fraction(def,min,max,step) "?sF", SPA_CHOICE_STEP(def, min, max, step) + +#define SPA_POD_Array(csize,ctype,n_vals,vals) "a", csize,ctype,n_vals,vals +#define SPA_POD_Pointer(type,val) "p", type,val +#define SPA_POD_Fd(val) "h", val +#define SPA_POD_None() "P", NULL +#define SPA_POD_Pod(val) "P", val +#define SPA_POD_PodObject(val) "O", val +#define SPA_POD_PodStruct(val) "T", val +#define SPA_POD_PodChoice(val) "V", val + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_POD_VARARG_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/support/loop.h b/src/java.desktop/unix/native/libpipewire/include/spa/support/loop.h new file mode 100644 index 0000000000000..4beac560b250b --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/support/loop.h @@ -0,0 +1,318 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_LOOP_H +#define SPA_LOOP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** \defgroup spa_loop Loop + * Event loop interface + */ + +/** + * \addtogroup spa_loop + * \{ + */ + +#define SPA_TYPE_INTERFACE_Loop SPA_TYPE_INFO_INTERFACE_BASE "Loop" +#define SPA_TYPE_INTERFACE_DataLoop SPA_TYPE_INFO_INTERFACE_BASE "DataLoop" +#define SPA_VERSION_LOOP 0 +struct spa_loop { struct spa_interface iface; }; + +#define SPA_TYPE_INTERFACE_LoopControl SPA_TYPE_INFO_INTERFACE_BASE "LoopControl" +#define SPA_VERSION_LOOP_CONTROL 1 +struct spa_loop_control { struct spa_interface iface; }; + +#define SPA_TYPE_INTERFACE_LoopUtils SPA_TYPE_INFO_INTERFACE_BASE "LoopUtils" +#define SPA_VERSION_LOOP_UTILS 0 +struct spa_loop_utils { struct spa_interface iface; }; + +struct spa_source; + +typedef void (*spa_source_func_t) (struct spa_source *source); + +struct spa_source { + struct spa_loop *loop; + spa_source_func_t func; + void *data; + int fd; + uint32_t mask; + uint32_t rmask; + /* private data for the loop implementer */ + void *priv; +}; + +typedef int (*spa_invoke_func_t) (struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data); + +/** + * Register sources and work items to an event loop + */ +struct spa_loop_methods { + /* the version of this structure. This can be used to expand this + * structure in the future */ +#define SPA_VERSION_LOOP_METHODS 0 + uint32_t version; + + /** add a source to the loop */ + int (*add_source) (void *object, + struct spa_source *source); + + /** update the source io mask */ + int (*update_source) (void *object, + struct spa_source *source); + + /** remove a source from the loop */ + int (*remove_source) (void *object, + struct spa_source *source); + + /** invoke a function in the context of this loop */ + int (*invoke) (void *object, + spa_invoke_func_t func, + uint32_t seq, + const void *data, + size_t size, + bool block, + void *user_data); +}; + +#define spa_loop_method(o,method,version,...) \ +({ \ + int _res = -ENOTSUP; \ + struct spa_loop *_o = o; \ + spa_interface_call_res(&_o->iface, \ + struct spa_loop_methods, _res, \ + method, version, ##__VA_ARGS__); \ + _res; \ +}) + +#define spa_loop_add_source(l,...) spa_loop_method(l,add_source,0,##__VA_ARGS__) +#define spa_loop_update_source(l,...) spa_loop_method(l,update_source,0,##__VA_ARGS__) +#define spa_loop_remove_source(l,...) spa_loop_method(l,remove_source,0,##__VA_ARGS__) +#define spa_loop_invoke(l,...) spa_loop_method(l,invoke,0,##__VA_ARGS__) + + +/** Control hooks. These hooks can't be removed from their + * callbacks and must be removed from a safe place (when the loop + * is not running or when it is locked). */ +struct spa_loop_control_hooks { +#define SPA_VERSION_LOOP_CONTROL_HOOKS 0 + uint32_t version; + /** Executed right before waiting for events. It is typically used to + * release locks. */ + void (*before) (void *data); + /** Executed right after waiting for events. It is typically used to + * reacquire locks. */ + void (*after) (void *data); +}; + +#define spa_loop_control_hook_before(l) \ +({ \ + struct spa_hook_list *_l = l; \ + struct spa_hook *_h; \ + spa_list_for_each_reverse(_h, &_l->list, link) \ + spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, before, 0); \ +}) + +#define spa_loop_control_hook_after(l) \ +({ \ + struct spa_hook_list *_l = l; \ + struct spa_hook *_h; \ + spa_list_for_each(_h, &_l->list, link) \ + spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, after, 0); \ +}) + +/** + * Control an event loop + */ +struct spa_loop_control_methods { + /* the version of this structure. This can be used to expand this + * structure in the future */ +#define SPA_VERSION_LOOP_CONTROL_METHODS 1 + uint32_t version; + + int (*get_fd) (void *object); + + /** Add a hook + * \param ctrl the control to change + * \param hooks the hooks to add + * + * Adds hooks to the loop controlled by \a ctrl. + */ + void (*add_hook) (void *object, + struct spa_hook *hook, + const struct spa_loop_control_hooks *hooks, + void *data); + + /** Enter a loop + * \param ctrl the control + * + * Start an iteration of the loop. This function should be called + * before calling iterate and is typically used to capture the thread + * that this loop will run in. + */ + void (*enter) (void *object); + /** Leave a loop + * \param ctrl the control + * + * Ends the iteration of a loop. This should be called after calling + * iterate. + */ + void (*leave) (void *object); + + /** Perform one iteration of the loop. + * \param ctrl the control + * \param timeout an optional timeout in milliseconds. + * 0 for no timeout, -1 for infinite timeout. + * + * This function will block + * up to \a timeout milliseconds and then dispatch the fds with activity. + * The number of dispatched fds is returned. + */ + int (*iterate) (void *object, int timeout); + + /** Check context of the loop + * \param ctrl the control + * + * This function will check if the current thread is currently the + * one that did the enter call. Since version 1:1. + * + * returns 1 on success, 0 or negative errno value on error. + */ + int (*check) (void *object); +}; + +#define spa_loop_control_method_v(o,method,version,...) \ +({ \ + struct spa_loop_control *_o = o; \ + spa_interface_call(&_o->iface, \ + struct spa_loop_control_methods, \ + method, version, ##__VA_ARGS__); \ +}) + +#define spa_loop_control_method_r(o,method,version,...) \ +({ \ + int _res = -ENOTSUP; \ + struct spa_loop_control *_o = o; \ + spa_interface_call_res(&_o->iface, \ + struct spa_loop_control_methods, _res, \ + method, version, ##__VA_ARGS__); \ + _res; \ +}) + +#define spa_loop_control_get_fd(l) spa_loop_control_method_r(l,get_fd,0) +#define spa_loop_control_add_hook(l,...) spa_loop_control_method_v(l,add_hook,0,__VA_ARGS__) +#define spa_loop_control_enter(l) spa_loop_control_method_v(l,enter,0) +#define spa_loop_control_leave(l) spa_loop_control_method_v(l,leave,0) +#define spa_loop_control_iterate(l,...) spa_loop_control_method_r(l,iterate,0,__VA_ARGS__) +#define spa_loop_control_check(l) spa_loop_control_method_r(l,check,1) + +typedef void (*spa_source_io_func_t) (void *data, int fd, uint32_t mask); +typedef void (*spa_source_idle_func_t) (void *data); +typedef void (*spa_source_event_func_t) (void *data, uint64_t count); +typedef void (*spa_source_timer_func_t) (void *data, uint64_t expirations); +typedef void (*spa_source_signal_func_t) (void *data, int signal_number); + +/** + * Create sources for an event loop + */ +struct spa_loop_utils_methods { + /* the version of this structure. This can be used to expand this + * structure in the future */ +#define SPA_VERSION_LOOP_UTILS_METHODS 0 + uint32_t version; + + struct spa_source *(*add_io) (void *object, + int fd, + uint32_t mask, + bool close, + spa_source_io_func_t func, void *data); + + int (*update_io) (void *object, struct spa_source *source, uint32_t mask); + + struct spa_source *(*add_idle) (void *object, + bool enabled, + spa_source_idle_func_t func, void *data); + int (*enable_idle) (void *object, struct spa_source *source, bool enabled); + + struct spa_source *(*add_event) (void *object, + spa_source_event_func_t func, void *data); + int (*signal_event) (void *object, struct spa_source *source); + + struct spa_source *(*add_timer) (void *object, + spa_source_timer_func_t func, void *data); + int (*update_timer) (void *object, + struct spa_source *source, + struct timespec *value, + struct timespec *interval, + bool absolute); + struct spa_source *(*add_signal) (void *object, + int signal_number, + spa_source_signal_func_t func, void *data); + + /** destroy a source allocated with this interface. This function + * should only be called when the loop is not running or from the + * context of the running loop */ + void (*destroy_source) (void *object, struct spa_source *source); +}; + +#define spa_loop_utils_method_v(o,method,version,...) \ +({ \ + struct spa_loop_utils *_o = o; \ + spa_interface_call(&_o->iface, \ + struct spa_loop_utils_methods, \ + method, version, ##__VA_ARGS__); \ +}) + +#define spa_loop_utils_method_r(o,method,version,...) \ +({ \ + int _res = -ENOTSUP; \ + struct spa_loop_utils *_o = o; \ + spa_interface_call_res(&_o->iface, \ + struct spa_loop_utils_methods, _res, \ + method, version, ##__VA_ARGS__); \ + _res; \ +}) +#define spa_loop_utils_method_s(o,method,version,...) \ +({ \ + struct spa_source *_res = NULL; \ + struct spa_loop_utils *_o = o; \ + spa_interface_call_res(&_o->iface, \ + struct spa_loop_utils_methods, _res, \ + method, version, ##__VA_ARGS__); \ + _res; \ +}) + + +#define spa_loop_utils_add_io(l,...) spa_loop_utils_method_s(l,add_io,0,__VA_ARGS__) +#define spa_loop_utils_update_io(l,...) spa_loop_utils_method_r(l,update_io,0,__VA_ARGS__) +#define spa_loop_utils_add_idle(l,...) spa_loop_utils_method_s(l,add_idle,0,__VA_ARGS__) +#define spa_loop_utils_enable_idle(l,...) spa_loop_utils_method_r(l,enable_idle,0,__VA_ARGS__) +#define spa_loop_utils_add_event(l,...) spa_loop_utils_method_s(l,add_event,0,__VA_ARGS__) +#define spa_loop_utils_signal_event(l,...) spa_loop_utils_method_r(l,signal_event,0,__VA_ARGS__) +#define spa_loop_utils_add_timer(l,...) spa_loop_utils_method_s(l,add_timer,0,__VA_ARGS__) +#define spa_loop_utils_update_timer(l,...) spa_loop_utils_method_r(l,update_timer,0,__VA_ARGS__) +#define spa_loop_utils_add_signal(l,...) spa_loop_utils_method_s(l,add_signal,0,__VA_ARGS__) +#define spa_loop_utils_destroy_source(l,...) spa_loop_utils_method_v(l,destroy_source,0,__VA_ARGS__) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_LOOP_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/support/system.h b/src/java.desktop/unix/native/libpipewire/include/spa/support/system.h new file mode 100644 index 0000000000000..6bd9dcd015985 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/support/system.h @@ -0,0 +1,145 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_SYSTEM_H +#define SPA_SYSTEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct itimerspec; + +#include +#include + +#include +#include + +/** \defgroup spa_system System + * I/O, clock, polling, timer, and signal interfaces + */ + +/** + * \addtogroup spa_system + * \{ + */ + +/** + * a collection of core system functions + */ +#define SPA_TYPE_INTERFACE_System SPA_TYPE_INFO_INTERFACE_BASE "System" +#define SPA_TYPE_INTERFACE_DataSystem SPA_TYPE_INFO_INTERFACE_BASE "DataSystem" + +#define SPA_VERSION_SYSTEM 0 +struct spa_system { struct spa_interface iface; }; + +/* IO events */ +#define SPA_IO_IN (1 << 0) +#define SPA_IO_OUT (1 << 2) +#define SPA_IO_ERR (1 << 3) +#define SPA_IO_HUP (1 << 4) + +/* flags */ +#define SPA_FD_CLOEXEC (1<<0) +#define SPA_FD_NONBLOCK (1<<1) +#define SPA_FD_EVENT_SEMAPHORE (1<<2) +#define SPA_FD_TIMER_ABSTIME (1<<3) +#define SPA_FD_TIMER_CANCEL_ON_SET (1<<4) + +struct spa_poll_event { + uint32_t events; + void *data; +}; + +struct spa_system_methods { +#define SPA_VERSION_SYSTEM_METHODS 0 + uint32_t version; + + /* read/write/ioctl */ + ssize_t (*read) (void *object, int fd, void *buf, size_t count); + ssize_t (*write) (void *object, int fd, const void *buf, size_t count); + int (*ioctl) (void *object, int fd, unsigned long request, ...); + int (*close) (void *object, int fd); + + /* clock */ + int (*clock_gettime) (void *object, + int clockid, struct timespec *value); + int (*clock_getres) (void *object, + int clockid, struct timespec *res); + + /* poll */ + int (*pollfd_create) (void *object, int flags); + int (*pollfd_add) (void *object, int pfd, int fd, uint32_t events, void *data); + int (*pollfd_mod) (void *object, int pfd, int fd, uint32_t events, void *data); + int (*pollfd_del) (void *object, int pfd, int fd); + int (*pollfd_wait) (void *object, int pfd, + struct spa_poll_event *ev, int n_ev, int timeout); + + /* timers */ + int (*timerfd_create) (void *object, int clockid, int flags); + int (*timerfd_settime) (void *object, + int fd, int flags, + const struct itimerspec *new_value, + struct itimerspec *old_value); + int (*timerfd_gettime) (void *object, + int fd, struct itimerspec *curr_value); + int (*timerfd_read) (void *object, int fd, uint64_t *expirations); + + /* events */ + int (*eventfd_create) (void *object, int flags); + int (*eventfd_write) (void *object, int fd, uint64_t count); + int (*eventfd_read) (void *object, int fd, uint64_t *count); + + /* signals */ + int (*signalfd_create) (void *object, int signal, int flags); + int (*signalfd_read) (void *object, int fd, int *signal); +}; + +#define spa_system_method_r(o,method,version,...) \ +({ \ + volatile int _res = -ENOTSUP; \ + struct spa_system *_o = o; \ + spa_interface_call_res(&_o->iface, \ + struct spa_system_methods, _res, \ + method, version, ##__VA_ARGS__); \ + _res; \ +}) + + +#define spa_system_read(s,...) spa_system_method_r(s,read,0,__VA_ARGS__) +#define spa_system_write(s,...) spa_system_method_r(s,write,0,__VA_ARGS__) +#define spa_system_ioctl(s,...) spa_system_method_r(s,ioctl,0,__VA_ARGS__) +#define spa_system_close(s,...) spa_system_method_r(s,close,0,__VA_ARGS__) + +#define spa_system_clock_gettime(s,...) spa_system_method_r(s,clock_gettime,0,__VA_ARGS__) +#define spa_system_clock_getres(s,...) spa_system_method_r(s,clock_getres,0,__VA_ARGS__) + +#define spa_system_pollfd_create(s,...) spa_system_method_r(s,pollfd_create,0,__VA_ARGS__) +#define spa_system_pollfd_add(s,...) spa_system_method_r(s,pollfd_add,0,__VA_ARGS__) +#define spa_system_pollfd_mod(s,...) spa_system_method_r(s,pollfd_mod,0,__VA_ARGS__) +#define spa_system_pollfd_del(s,...) spa_system_method_r(s,pollfd_del,0,__VA_ARGS__) +#define spa_system_pollfd_wait(s,...) spa_system_method_r(s,pollfd_wait,0,__VA_ARGS__) + +#define spa_system_timerfd_create(s,...) spa_system_method_r(s,timerfd_create,0,__VA_ARGS__) +#define spa_system_timerfd_settime(s,...) spa_system_method_r(s,timerfd_settime,0,__VA_ARGS__) +#define spa_system_timerfd_gettime(s,...) spa_system_method_r(s,timerfd_gettime,0,__VA_ARGS__) +#define spa_system_timerfd_read(s,...) spa_system_method_r(s,timerfd_read,0,__VA_ARGS__) + +#define spa_system_eventfd_create(s,...) spa_system_method_r(s,eventfd_create,0,__VA_ARGS__) +#define spa_system_eventfd_write(s,...) spa_system_method_r(s,eventfd_write,0,__VA_ARGS__) +#define spa_system_eventfd_read(s,...) spa_system_method_r(s,eventfd_read,0,__VA_ARGS__) + +#define spa_system_signalfd_create(s,...) spa_system_method_r(s,signalfd_create,0,__VA_ARGS__) +#define spa_system_signalfd_read(s,...) spa_system_method_r(s,signalfd_read,0,__VA_ARGS__) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_SYSTEM_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/defs.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/defs.h new file mode 100644 index 0000000000000..66c238987d0ac --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/defs.h @@ -0,0 +1,382 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_UTILS_DEFS_H +#define SPA_UTILS_DEFS_H + +#ifdef __cplusplus +extern "C" { +# if __cplusplus >= 201103L +# define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) static_assert(expr, msg) +# endif +#else +# include +# if __STDC_VERSION__ >= 201112L +# define SPA_STATIC_ASSERT_IMPL(expr, msg, ...) _Static_assert(expr, msg) +# endif +#endif +#ifndef SPA_STATIC_ASSERT_IMPL +#define SPA_STATIC_ASSERT_IMPL(expr, ...) \ + ((void)sizeof(struct { int spa_static_assertion_failed : 2 * !!(expr) - 1; })) +#endif + +#define SPA_STATIC_ASSERT(expr, ...) SPA_STATIC_ASSERT_IMPL(expr, ## __VA_ARGS__, "`" #expr "` evaluated to false") + +#include +#include +#include +#include +#include +#include + +/** + * \defgroup spa_utils_defs Miscellaneous + * Helper macros and functions + */ + +/** + * \addtogroup spa_utils_defs + * \{ + */ + +/** + * SPA_FALLTHROUGH is an annotation to suppress compiler warnings about switch + * cases that fall through without a break or return statement. SPA_FALLTHROUGH + * is only needed on cases that have code: + * + * switch (foo) { + * case 1: // These cases have no code. No fallthrough annotations are needed. + * case 2: + * case 3: + * foo = 4; // This case has code, so a fallthrough annotation is needed: + * SPA_FALLTHROUGH; + * default: + * return foo; + * } + */ +#if defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201103L + /* clang's fallthrough annotations are only available starting in C++11. */ +# define SPA_FALLTHROUGH [[clang::fallthrough]]; +#elif __GNUC__ >= 7 || __clang_major__ >= 10 +# define SPA_FALLTHROUGH __attribute__ ((fallthrough)); +#else +# define SPA_FALLTHROUGH /* FALLTHROUGH */ +#endif + +#define SPA_FLAG_MASK(field,mask,flag) (((field) & (mask)) == (flag)) +#define SPA_FLAG_IS_SET(field,flag) SPA_FLAG_MASK(field, flag, flag) + +#define SPA_FLAG_SET(field,flag) ((field) |= (flag)) +#define SPA_FLAG_CLEAR(field, flag) \ +({ \ + SPA_STATIC_ASSERT(__builtin_constant_p(flag) ? \ + (__typeof__(flag))(__typeof__(field))(__typeof__(flag))(flag) == (flag) : \ + sizeof(field) >= sizeof(flag), \ + "truncation problem when masking " #field \ + " with ~" #flag); \ + ((field) &= ~(__typeof__(field))(flag)); \ +}) +#define SPA_FLAG_UPDATE(field,flag,val) ((val) ? SPA_FLAG_SET((field),(flag)) : SPA_FLAG_CLEAR((field),(flag))) + +enum spa_direction { + SPA_DIRECTION_INPUT = 0, + SPA_DIRECTION_OUTPUT = 1, +}; + +#define SPA_DIRECTION_REVERSE(d) ((d) ^ 1) + +#define SPA_RECTANGLE(width,height) ((struct spa_rectangle){ (width), (height) }) +struct spa_rectangle { + uint32_t width; + uint32_t height; +}; + +#define SPA_POINT(x,y) ((struct spa_point){ (x), (y) }) +struct spa_point { + int32_t x; + int32_t y; +}; + +#define SPA_REGION(x,y,width,height) ((struct spa_region){ SPA_POINT(x,y), SPA_RECTANGLE(width,height) }) +struct spa_region { + struct spa_point position; + struct spa_rectangle size; +}; + +#define SPA_FRACTION(num,denom) ((struct spa_fraction){ (num), (denom) }) +struct spa_fraction { + uint32_t num; + uint32_t denom; +}; + +#define SPA_N_ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0])) +/** + * Array iterator macro. Usage: + * ```c + * struct foo array[16]; + * struct foo *f; + * SPA_FOR_EACH_ELEMENT(array, f) { + * f->bar = baz; + * } + * ``` + */ +#define SPA_FOR_EACH_ELEMENT(arr, ptr) \ + for ((ptr) = arr; (void*)(ptr) < SPA_PTROFF(arr, sizeof(arr), void); (ptr)++) + +#define SPA_FOR_EACH_ELEMENT_VAR(arr, var) \ + for (__typeof__((arr)[0])* var = arr; (void*)(var) < SPA_PTROFF(arr, sizeof(arr), void); (var)++) + +#define SPA_ABS(a) \ +({ \ + __typeof__(a) _a = (a); \ + SPA_LIKELY(_a >= 0) ? _a : -_a; \ +}) +#define SPA_MIN(a,b) \ +({ \ + __typeof__(a) _min_a = (a); \ + __typeof__(b) _min_b = (b); \ + SPA_LIKELY(_min_a <= _min_b) ? _min_a : _min_b; \ +}) +#define SPA_MAX(a,b) \ +({ \ + __typeof__(a) _max_a = (a); \ + __typeof__(b) _max_b = (b); \ + SPA_LIKELY(_max_a >= _max_b) ? _max_a : _max_b; \ +}) +#define SPA_CLAMP(v,low,high) \ +({ \ + __typeof__(v) _v = (v); \ + __typeof__(low) _low = (low); \ + __typeof__(high) _high = (high); \ + SPA_MIN(SPA_MAX(_v, _low), _high); \ +}) + +#define SPA_CLAMPF(v,low,high) \ +({ \ + fminf(fmaxf(v, low), high); \ +}) + + +#define SPA_SWAP(a,b) \ +({ \ + __typeof__(a) _t = (a); \ + (a) = b; (b) = _t; \ +}) + +#define SPA_TYPECHECK(type,x) \ +({ type _dummy; \ + typeof(x) _dummy2; \ + (void)(&_dummy == &_dummy2); \ + x; \ +}) + +/** + * Return the address (buffer + offset) as pointer of \a type + */ +#define SPA_PTROFF(ptr_,offset_,type_) ((type_*)((uintptr_t)(ptr_) + (ptrdiff_t)(offset_))) +#define SPA_PTROFF_ALIGN(ptr_,offset_,alignment_,type_) \ + SPA_PTR_ALIGN(SPA_PTROFF(ptr_,offset_,type_),alignment_,type_) + + +/** + * Deprecated, use SPA_PTROFF and SPA_PTROFF_ALIGN instead + */ +#define SPA_MEMBER(b,o,t) SPA_PTROFF(b,o,t) +#define SPA_MEMBER_ALIGN(b,o,a,t) SPA_PTROFF_ALIGN(b,o,a,t) + +#define SPA_CONTAINER_OF(p,t,m) ((t*)((uintptr_t)(p) - offsetof(t,m))) + +#define SPA_PTRDIFF(p1,p2) ((intptr_t)(p1) - (intptr_t)(p2)) + +#define SPA_PTR_TO_INT(p) ((int) ((intptr_t) (p))) +#define SPA_INT_TO_PTR(u) ((void*) ((intptr_t) (u))) + +#define SPA_PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) +#define SPA_UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u))) + +#define SPA_TIME_INVALID ((int64_t)INT64_MIN) +#define SPA_IDX_INVALID ((unsigned int)-1) +#define SPA_ID_INVALID ((uint32_t)0xffffffff) + +#define SPA_NSEC_PER_SEC (1000000000LL) +#define SPA_NSEC_PER_MSEC (1000000ll) +#define SPA_NSEC_PER_USEC (1000ll) +#define SPA_USEC_PER_SEC (1000000ll) +#define SPA_USEC_PER_MSEC (1000ll) +#define SPA_MSEC_PER_SEC (1000ll) + +#define SPA_TIMESPEC_TO_NSEC(ts) ((ts)->tv_sec * SPA_NSEC_PER_SEC + (ts)->tv_nsec) +#define SPA_TIMESPEC_TO_USEC(ts) ((ts)->tv_sec * SPA_USEC_PER_SEC + (ts)->tv_nsec / SPA_NSEC_PER_USEC) +#define SPA_TIMEVAL_TO_NSEC(tv) ((tv)->tv_sec * SPA_NSEC_PER_SEC + (tv)->tv_usec * SPA_NSEC_PER_USEC) +#define SPA_TIMEVAL_TO_USEC(tv) ((tv)->tv_sec * SPA_USEC_PER_SEC + (tv)->tv_usec) + +#ifdef __GNUC__ +#define SPA_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) +#define SPA_FORMAT_ARG_FUNC(arg1) __attribute__((format_arg(arg1))) +#define SPA_ALIGNED(align) __attribute__((aligned(align))) +#define SPA_DEPRECATED __attribute__ ((deprecated)) +#define SPA_EXPORT __attribute__((visibility("default"))) +#define SPA_SENTINEL __attribute__((__sentinel__)) +#define SPA_UNUSED __attribute__ ((unused)) +#define SPA_NORETURN __attribute__ ((noreturn)) +#define SPA_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) +#else +#define SPA_PRINTF_FUNC(fmt, arg1) +#define SPA_FORMAT_ARG_FUNC(arg1) +#define SPA_ALIGNED(align) +#define SPA_DEPRECATED +#define SPA_EXPORT +#define SPA_SENTINEL +#define SPA_UNUSED +#define SPA_NORETURN +#define SPA_WARN_UNUSED_RESULT +#endif + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define SPA_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define SPA_RESTRICT __restrict__ +#else +#define SPA_RESTRICT +#endif + +#define SPA_ROUND_DOWN(num,value) \ +({ \ + __typeof__(num) _num = (num); \ + ((_num) - ((_num) % (value))); \ +}) +#define SPA_ROUND_UP(num,value) \ +({ \ + __typeof__(value) _v = (value); \ + ((((num) + (_v) - 1) / (_v)) * (_v)); \ +}) + +#define SPA_ROUND_MASK(num,mask) ((__typeof__(num))((mask)-1)) + +#define SPA_ROUND_DOWN_N(num,align) ((num) & ~SPA_ROUND_MASK(num, align)) +#define SPA_ROUND_UP_N(num,align) ((((num)-1) | SPA_ROUND_MASK(num, align))+1) + +#define SPA_SCALE32_UP(val,num,denom) \ +({ \ + uint64_t _val = (val); \ + uint64_t _denom = (denom); \ + (uint32_t)(((_val) * (num) + (_denom)-1) / (_denom)); \ +}) + + +#define SPA_PTR_ALIGNMENT(p,align) ((intptr_t)(p) & ((align)-1)) +#define SPA_IS_ALIGNED(p,align) (SPA_PTR_ALIGNMENT(p,align) == 0) +#define SPA_PTR_ALIGN(p,align,type) ((type*)SPA_ROUND_UP_N((intptr_t)(p), (intptr_t)(align))) + +#ifndef SPA_LIKELY +#ifdef __GNUC__ +#define SPA_LIKELY(x) (__builtin_expect(!!(x),1)) +#define SPA_UNLIKELY(x) (__builtin_expect(!!(x),0)) +#else +#define SPA_LIKELY(x) (x) +#define SPA_UNLIKELY(x) (x) +#endif +#endif + +#define SPA_STRINGIFY_1(...) #__VA_ARGS__ +#define SPA_STRINGIFY(...) SPA_STRINGIFY_1(__VA_ARGS__) + +#define spa_return_if_fail(expr) \ + do { \ + if (SPA_UNLIKELY(!(expr))) { \ + fprintf(stderr, "'%s' failed at %s:%u %s()\n", \ + #expr , __FILE__, __LINE__, __func__); \ + return; \ + } \ + } while(false) + +#define spa_return_val_if_fail(expr, val) \ + do { \ + if (SPA_UNLIKELY(!(expr))) { \ + fprintf(stderr, "'%s' failed at %s:%u %s()\n", \ + #expr , __FILE__, __LINE__, __func__); \ + return (val); \ + } \ + } while(false) + +/* spa_assert_se() is an assert which guarantees side effects of x, + * i.e. is never optimized away, regardless of NDEBUG or FASTPATH. */ +#ifndef __COVERITY__ +#define spa_assert_se(expr) \ + do { \ + if (SPA_UNLIKELY(!(expr))) { \ + fprintf(stderr, "'%s' failed at %s:%u %s()\n", \ + #expr , __FILE__, __LINE__, __func__); \ + abort(); \ + } \ + } while (false) +#else +#define spa_assert_se(expr) \ + do { \ + int _unique_var = (expr); \ + if (!_unique_var) \ + abort(); \ + } while (false) +#endif + +/* Does exactly nothing */ +#define spa_nop() do {} while (false) + +#ifdef NDEBUG +#define spa_assert(expr) spa_nop() +#elif defined (FASTPATH) +#define spa_assert(expr) spa_assert_se(expr) +#else +#define spa_assert(expr) spa_assert_se(expr) +#endif + +#ifdef NDEBUG +#define spa_assert_not_reached() abort() +#else +#define spa_assert_not_reached() \ + do { \ + fprintf(stderr, "Code should not be reached at %s:%u %s()\n", \ + __FILE__, __LINE__, __func__); \ + abort(); \ + } while (false) +#endif + +#define spa_memzero(x,l) (memset((x), 0, (l))) +#define spa_zero(x) (spa_memzero(&(x), sizeof(x))) + +#ifdef SPA_DEBUG_MEMCPY +#define spa_memcpy(d,s,n) \ +({ \ + fprintf(stderr, "%s:%u %s() memcpy(%p, %p, %zd)\n", \ + __FILE__, __LINE__, __func__, (d), (s), (size_t)(n)); \ + memcpy(d,s,n); \ +}) +#define spa_memmove(d,s,n) \ +({ \ + fprintf(stderr, "%s:%u %s() memmove(%p, %p, %zd)\n", \ + __FILE__, __LINE__, __func__, (d), (s), (size_t)(n)); \ + memmove(d,s,n); \ +}) +#else +#define spa_memcpy(d,s,n) memcpy(d,s,n) +#define spa_memmove(d,s,n) memmove(d,s,n) +#endif + +#define spa_aprintf(_fmt, ...) \ +({ \ + char *_strp; \ + if (asprintf(&(_strp), (_fmt), ## __VA_ARGS__ ) == -1) \ + _strp = NULL; \ + _strp; \ +}) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_UTILS_DEFS_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/dict.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/dict.h new file mode 100644 index 0000000000000..f34cd21f84078 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/dict.h @@ -0,0 +1,100 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_DICT_H +#define SPA_DICT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/** + * \defgroup spa_dict Dictionary + * Dictionary data structure + */ + +/** + * \addtogroup spa_dict + * \{ + */ + +struct spa_dict_item { + const char *key; + const char *value; +}; + +#define SPA_DICT_ITEM_INIT(key,value) ((struct spa_dict_item) { (key), (value) }) + +struct spa_dict { +#define SPA_DICT_FLAG_SORTED (1<<0) /**< items are sorted */ + uint32_t flags; + uint32_t n_items; + const struct spa_dict_item *items; +}; + +#define SPA_DICT_INIT(items,n_items) ((struct spa_dict) { 0, (n_items), (items) }) +#define SPA_DICT_INIT_ARRAY(items) ((struct spa_dict) { 0, SPA_N_ELEMENTS(items), (items) }) + +#define spa_dict_for_each(item, dict) \ + for ((item) = (dict)->items; \ + (item) < &(dict)->items[(dict)->n_items]; \ + (item)++) + +static inline int spa_dict_item_compare(const void *i1, const void *i2) +{ + const struct spa_dict_item *it1 = (const struct spa_dict_item *)i1, + *it2 = (const struct spa_dict_item *)i2; + return strcmp(it1->key, it2->key); +} + +static inline void spa_dict_qsort(struct spa_dict *dict) +{ + if (dict->n_items > 0) + qsort((void*)dict->items, dict->n_items, sizeof(struct spa_dict_item), + spa_dict_item_compare); + SPA_FLAG_SET(dict->flags, SPA_DICT_FLAG_SORTED); +} + +static inline const struct spa_dict_item *spa_dict_lookup_item(const struct spa_dict *dict, + const char *key) +{ + const struct spa_dict_item *item; + + if (SPA_FLAG_IS_SET(dict->flags, SPA_DICT_FLAG_SORTED) && + dict->n_items > 0) { + struct spa_dict_item k = SPA_DICT_ITEM_INIT(key, NULL); + item = (const struct spa_dict_item *)bsearch(&k, + (const void *) dict->items, dict->n_items, + sizeof(struct spa_dict_item), + spa_dict_item_compare); + if (item != NULL) + return item; + } else { + spa_dict_for_each(item, dict) { + if (!strcmp(item->key, key)) + return item; + } + } + return NULL; +} + +static inline const char *spa_dict_lookup(const struct spa_dict *dict, const char *key) +{ + const struct spa_dict_item *item = spa_dict_lookup_item(dict, key); + return item ? item->value : NULL; +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_DICT_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/enum-types.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/enum-types.h new file mode 100644 index 0000000000000..b374995b5cf1f --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/enum-types.h @@ -0,0 +1,45 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_ENUM_TYPES_H +#define SPA_ENUM_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define SPA_TYPE_INFO_Direction SPA_TYPE_INFO_ENUM_BASE "Direction" +#define SPA_TYPE_INFO_DIRECTION_BASE SPA_TYPE_INFO_Direction ":" + +static const struct spa_type_info spa_type_direction[] = { + { SPA_DIRECTION_INPUT, SPA_TYPE_Int, SPA_TYPE_INFO_DIRECTION_BASE "Input", NULL }, + { SPA_DIRECTION_OUTPUT, SPA_TYPE_Int, SPA_TYPE_INFO_DIRECTION_BASE "Output", NULL }, + { 0, 0, NULL, NULL } +}; + +#include + +#define SPA_TYPE_INFO_Choice SPA_TYPE_INFO_ENUM_BASE "Choice" +#define SPA_TYPE_INFO_CHOICE_BASE SPA_TYPE_INFO_Choice ":" + +static const struct spa_type_info spa_type_choice[] = { + { SPA_CHOICE_None, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "None", NULL }, + { SPA_CHOICE_Range, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Range", NULL }, + { SPA_CHOICE_Step, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Step", NULL }, + { SPA_CHOICE_Enum, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Enum", NULL }, + { SPA_CHOICE_Flags, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Flags", NULL }, + { 0, 0, NULL, NULL } +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_TYPE_INFO_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/hook.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/hook.h new file mode 100644 index 0000000000000..81fc07b8a05d9 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/hook.h @@ -0,0 +1,452 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_HOOK_H +#define SPA_HOOK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** \defgroup spa_interfaces Interfaces + * + * \brief Generic implementation of implementation-independent interfaces + * + * A SPA Interface is a generic struct that, together with a few macros, + * provides a generic way of invoking methods on objects without knowing the + * details of the implementation. + * + * The primary interaction with interfaces is through macros that expand into + * the right method call. For the implementation of an interface, we need two + * structs and a macro to invoke the `bar` method: + * + * \code{.c} + * // this struct must be public and defines the interface to a + * // struct foo + * struct foo_methods { + * uint32_t version; + * void (*bar)(void *object, const char *msg); + * }; + * + * // this struct does not need to be public + * struct foo { + * struct spa_interface iface; // must be first element, see foo_bar() + * int some_other_field; + * ... + * }; + * + * // if struct foo is private, we need to cast to a + * // generic spa_interface object + * #define foo_bar(obj, ...) ({ \ + * struct foo *f = obj; + * spa_interface_call((struct spa_interface *)f, // pointer to spa_interface in foo + * struct foo_methods, // type of callbacks + * bar, // name of methods + * 0, // hardcoded version to match foo_methods->version + * __VA_ARGS__ // pass rest of args through + * );/ + * }) + * \endcode + * + * The `struct foo_methods` and the invocation macro `foo_bar()` must be + * available to the caller. The implementation of `struct foo` can be private. + * + * \code{.c} + * void main(void) { + * struct foo *myfoo = get_foo_from_somewhere(); + * foo_bar(myfoo, "Invoking bar() on myfoo"); + * } + * \endcode + * The expansion of `foo_bar()` resolves roughly into this code: + * \code{.c} + * void main(void) { + * struct foo *myfoo = get_foo_from_somewhere(); + * // foo_bar(myfoo, "Invoking bar() on myfoo"); + * const struct foo_methods *methods = ((struct spa_interface*)myfoo)->cb; + * if (0 >= methods->version && // version check + * methods->bar) // compile error if this function does not exist, + * methods->bar(myfoo, "Invoking bar() on myfoo"); + * } + * \endcode + * + * The typecast used in `foo_bar()` allows `struct foo` to be opaque to the + * caller. The implementation may assign the callback methods at object + * instantiation, and the caller will transparently invoke the method on the + * given object. For example, the following code assigns a different `bar()` method on + * Mondays - the caller does not need to know this. + * \code{.c} + * + * static void bar_stdout(struct foo *f, const char *msg) { + * printf(msg); + * } + * static void bar_stderr(struct foo *f, const char *msg) { + * fprintf(stderr, msg); + * } + * + * struct foo* get_foo_from_somewhere() { + * struct foo *f = calloc(sizeof struct foo); + * // illustrative only, use SPA_INTERFACE_INIT() + * f->iface->cb = (struct foo_methods*) { .bar = bar_stdout }; + * if (today_is_monday) + * f->iface->cb = (struct foo_methods*) { .bar = bar_stderr }; + * return f; + * } + * \endcode + */ + +/** + * \addtogroup spa_interfaces + * \{ + */ + +/** \struct spa_callbacks + * Callbacks, contains the structure with functions and the data passed + * to the functions. The structure should also contain a version field that + * is checked. */ +struct spa_callbacks { + const void *funcs; + void *data; +}; + +/** Check if a callback \a c is of at least version \a v */ +#define SPA_CALLBACK_VERSION_MIN(c,v) ((c) && ((v) == 0 || (c)->version > (v)-1)) + +/** Check if a callback \a c has method \a m of version \a v */ +#define SPA_CALLBACK_CHECK(c,m,v) (SPA_CALLBACK_VERSION_MIN(c,v) && (c)->m) + +/** + * Initialize the set of functions \a funcs as a \ref spa_callbacks, together + * with \a _data. + */ +#define SPA_CALLBACKS_INIT(_funcs,_data) ((struct spa_callbacks){ (_funcs), (_data), }) + +/** \struct spa_interface + */ +struct spa_interface { + const char *type; + uint32_t version; + struct spa_callbacks cb; +}; + +/** + * Initialize a \ref spa_interface. + * + * \code{.c} + * const static struct foo_methods foo_funcs = { + * .bar = some_bar_implementation, + * }; + * + * struct foo *f = malloc(...); + * f->iface = SPA_INTERFACE_INIT("foo type", 0, foo_funcs, NULL); + * \endcode + * + */ +#define SPA_INTERFACE_INIT(_type,_version,_funcs,_data) \ + ((struct spa_interface){ (_type), (_version), SPA_CALLBACKS_INIT(_funcs,_data), }) + +/** + * Invoke method named \a method in the \a callbacks. + * The \a method_type defines the type of the method struct. + * Returns true if the method could be called, false otherwise. + */ +#define spa_callbacks_call(callbacks,type,method,vers,...) \ +({ \ + const type *_f = (const type *) (callbacks)->funcs; \ + bool _res = SPA_CALLBACK_CHECK(_f,method,vers); \ + if (SPA_LIKELY(_res)) \ + _f->method((callbacks)->data, ## __VA_ARGS__); \ + _res; \ +}) + +/** + * True if the \a callbacks are of version \a vers, false otherwise + */ +#define spa_callback_version_min(callbacks,type,vers) \ +({ \ + const type *_f = (const type *) (callbacks)->funcs; \ + SPA_CALLBACK_VERSION_MIN(_f,vers); \ +}) + +/** + * True if the \a callbacks contains \a method of version + * \a vers, false otherwise + */ +#define spa_callback_check(callbacks,type,method,vers) \ +({ \ + const type *_f = (const type *) (callbacks)->funcs; \ + SPA_CALLBACK_CHECK(_f,method,vers); \ +}) + +/** + * Invoke method named \a method in the \a callbacks. + * The \a method_type defines the type of the method struct. + * + * The return value is stored in \a res. + */ +#define spa_callbacks_call_res(callbacks,type,res,method,vers,...) \ +({ \ + const type *_f = (const type *) (callbacks)->funcs; \ + if (SPA_LIKELY(SPA_CALLBACK_CHECK(_f,method,vers))) \ + res = _f->method((callbacks)->data, ## __VA_ARGS__); \ + res; \ +}) + +/** + * True if the \a iface's callbacks are of version \a vers, false otherwise + */ +#define spa_interface_callback_version_min(iface,method_type,vers) \ + spa_callback_version_min(&(iface)->cb, method_type, vers) + +/** + * True if the \a iface's callback \a method is of version \a vers + * and exists, false otherwise + */ +#define spa_interface_callback_check(iface,method_type,method,vers) \ + spa_callback_check(&(iface)->cb, method_type, method, vers) + +/** + * Invoke method named \a method in the callbacks on the given interface object. + * The \a method_type defines the type of the method struct, not the interface + * itself. + */ +#define spa_interface_call(iface,method_type,method,vers,...) \ + spa_callbacks_call(&(iface)->cb,method_type,method,vers,##__VA_ARGS__) + +/** + * Invoke method named \a method in the callbacks on the given interface object. + * The \a method_type defines the type of the method struct, not the interface + * itself. + * + * The return value is stored in \a res. + */ +#define spa_interface_call_res(iface,method_type,res,method,vers,...) \ + spa_callbacks_call_res(&(iface)->cb,method_type,res,method,vers,##__VA_ARGS__) + +/** + * \} + */ + +/** \defgroup spa_hooks Hooks + * + * A SPA Hook is a data structure to keep track of callbacks. It is similar to + * the \ref spa_interfaces and typically used where an implementation allows + * for multiple external callback functions. For example, an implementation may + * use a hook list to implement signals with each caller using a hook to + * register callbacks to be invoked on those signals. + * + * The below (pseudo)code is a minimal example outlining the use of hooks: + * \code{.c} + * // the public interface + * #define VERSION_BAR_EVENTS 0 // version of the vtable + * struct bar_events { + * uint32_t version; // NOTE: an integral member named `version` + * // must be present in the vtable + * void (*boom)(void *data, const char *msg); + * }; + * + * // private implementation + * struct party { + * struct spa_hook_list bar_list; + * }; + * + * void party_add_event_listener(struct party *p, struct spa_hook *listener, + * const struct bar_events *events, void *data) + * { + * spa_hook_list_append(&p->bar_list, listener, events, data); + * } + * + * static void party_on(struct party *p) + * { + * // NOTE: this is a macro, it evaluates to an integer, + * // which is the number of hooks called + * spa_hook_list_call(&p->list, + * struct bar_events, // vtable type + * boom, // function name + * 0, // hardcoded version, + * // usually the version in which `boom` + * // has been added to the vtable + * "party on, wayne" // function argument(s) + * ); + * } + * \endcode + * + * In the caller, the hooks can be used like this: + * \code{.c} + * static void boom_cb(void *data, const char *msg) { + * // data is userdata from main() + * printf("%s", msg); + * } + * + * static const struct bar_events events = { + * .version = VERSION_BAR_EVENTS, // version of the implemented interface + * .boom = boom_cb, + * }; + * + * void main(void) { + * void *userdata = whatever; + * struct spa_hook hook; + * struct party *p = start_the_party(); + * + * party_add_event_listener(p, &hook, &events, userdata); + * + * mainloop(); + * return 0; + * } + * + * \endcode + */ + +/** + * \addtogroup spa_hooks + * \{ + */ + +/** \struct spa_hook_list + * A list of hooks. This struct is primarily used by + * implementation that use multiple caller-provided \ref spa_hook. */ +struct spa_hook_list { + struct spa_list list; +}; + + +/** \struct spa_hook + * A hook, contains the structure with functions and the data passed + * to the functions. + * + * A hook should be treated as opaque by the caller. + */ +struct spa_hook { + struct spa_list link; + struct spa_callbacks cb; + /** callback and data for the hook list, private to the + * hook_list implementor */ + void (*removed) (struct spa_hook *hook); + void *priv; +}; + +/** Initialize a hook list to the empty list*/ +static inline void spa_hook_list_init(struct spa_hook_list *list) +{ + spa_list_init(&list->list); +} + +static inline bool spa_hook_list_is_empty(struct spa_hook_list *list) +{ + return spa_list_is_empty(&list->list); +} + +/** Append a hook. */ +static inline void spa_hook_list_append(struct spa_hook_list *list, + struct spa_hook *hook, + const void *funcs, void *data) +{ + spa_zero(*hook); + hook->cb = SPA_CALLBACKS_INIT(funcs, data); + spa_list_append(&list->list, &hook->link); +} + +/** Prepend a hook */ +static inline void spa_hook_list_prepend(struct spa_hook_list *list, + struct spa_hook *hook, + const void *funcs, void *data) +{ + spa_zero(*hook); + hook->cb = SPA_CALLBACKS_INIT(funcs, data); + spa_list_prepend(&list->list, &hook->link); +} + +/** Remove a hook */ +static inline void spa_hook_remove(struct spa_hook *hook) +{ + if (spa_list_is_initialized(&hook->link)) + spa_list_remove(&hook->link); + if (hook->removed) + hook->removed(hook); +} + +/** Remove all hooks from the list */ +static inline void spa_hook_list_clean(struct spa_hook_list *list) +{ + struct spa_hook *h; + spa_list_consume(h, &list->list, link) + spa_hook_remove(h); +} + +static inline void +spa_hook_list_isolate(struct spa_hook_list *list, + struct spa_hook_list *save, + struct spa_hook *hook, + const void *funcs, void *data) +{ + /* init save list and move hooks to it */ + spa_hook_list_init(save); + spa_list_insert_list(&save->list, &list->list); + /* init hooks and add single hook */ + spa_hook_list_init(list); + spa_hook_list_append(list, hook, funcs, data); +} + +static inline void +spa_hook_list_join(struct spa_hook_list *list, + struct spa_hook_list *save) +{ + spa_list_insert_list(&list->list, &save->list); +} + +#define spa_hook_list_call_simple(l,type,method,vers,...) \ +({ \ + struct spa_hook_list *_l = l; \ + struct spa_hook *_h, *_t; \ + spa_list_for_each_safe(_h, _t, &_l->list, link) \ + spa_callbacks_call(&_h->cb,type,method,vers, ## __VA_ARGS__); \ +}) + +/** Call all hooks in a list, starting from the given one and optionally stopping + * after calling the first non-NULL function, returns the number of methods + * called */ +#define spa_hook_list_do_call(l,start,type,method,vers,once,...) \ +({ \ + struct spa_hook_list *_list = l; \ + struct spa_list *_s = start ? (struct spa_list *)start : &_list->list; \ + struct spa_hook _cursor = { 0 }, *_ci; \ + int _count = 0; \ + spa_list_cursor_start(_cursor, _s, link); \ + spa_list_for_each_cursor(_ci, _cursor, &_list->list, link) { \ + if (spa_callbacks_call(&_ci->cb,type,method,vers, ## __VA_ARGS__)) { \ + _count++; \ + if (once) \ + break; \ + } \ + } \ + spa_list_cursor_end(_cursor, link); \ + _count; \ +}) + +/** + * Call the method named \a m for each element in list \a l. + * \a t specifies the type of the callback struct. + */ +#define spa_hook_list_call(l,t,m,v,...) spa_hook_list_do_call(l,NULL,t,m,v,false,##__VA_ARGS__) +/** + * Call the method named \a m for each element in list \a l, stopping after + * the first invocation. + * \a t specifies the type of the callback struct. + */ +#define spa_hook_list_call_once(l,t,m,v,...) spa_hook_list_do_call(l,NULL,t,m,v,true,##__VA_ARGS__) + +#define spa_hook_list_call_start(l,s,t,m,v,...) spa_hook_list_do_call(l,s,t,m,v,false,##__VA_ARGS__) +#define spa_hook_list_call_once_start(l,s,t,m,v,...) spa_hook_list_do_call(l,s,t,m,v,true,##__VA_ARGS__) + +/** + * \} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* SPA_HOOK_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/list.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/list.h new file mode 100644 index 0000000000000..5d4169217b52f --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/list.h @@ -0,0 +1,146 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_LIST_H +#define SPA_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup spa_list List + * Doubly linked list data structure + */ + +/** + * \addtogroup spa_list List + * \{ + */ + +struct spa_list { + struct spa_list *next; + struct spa_list *prev; +}; + +#define SPA_LIST_INIT(list) ((struct spa_list){ (list), (list) }) + +static inline void spa_list_init(struct spa_list *list) +{ + *list = SPA_LIST_INIT(list); +} + +static inline int spa_list_is_initialized(struct spa_list *list) +{ + return !!list->prev; +} + +#define spa_list_is_empty(l) ((l)->next == (l)) + +static inline void spa_list_insert(struct spa_list *list, struct spa_list *elem) +{ + elem->prev = list; + elem->next = list->next; + list->next = elem; + elem->next->prev = elem; +} + +static inline void spa_list_insert_list(struct spa_list *list, struct spa_list *other) +{ + if (spa_list_is_empty(other)) + return; + other->next->prev = list; + other->prev->next = list->next; + list->next->prev = other->prev; + list->next = other->next; +} + +static inline void spa_list_remove(struct spa_list *elem) +{ + elem->prev->next = elem->next; + elem->next->prev = elem->prev; +} + +#define spa_list_first(head, type, member) \ + SPA_CONTAINER_OF((head)->next, type, member) + +#define spa_list_last(head, type, member) \ + SPA_CONTAINER_OF((head)->prev, type, member) + +#define spa_list_append(list, item) \ + spa_list_insert((list)->prev, item) + +#define spa_list_prepend(list, item) \ + spa_list_insert(list, item) + +#define spa_list_is_end(pos, head, member) \ + (&(pos)->member == (head)) + +#define spa_list_next(pos, member) \ + SPA_CONTAINER_OF((pos)->member.next, __typeof__(*(pos)), member) + +#define spa_list_prev(pos, member) \ + SPA_CONTAINER_OF((pos)->member.prev, __typeof__(*(pos)), member) + +#define spa_list_consume(pos, head, member) \ + for ((pos) = spa_list_first(head, __typeof__(*(pos)), member); \ + !spa_list_is_empty(head); \ + (pos) = spa_list_first(head, __typeof__(*(pos)), member)) + +#define spa_list_for_each_next(pos, head, curr, member) \ + for ((pos) = spa_list_first(curr, __typeof__(*(pos)), member); \ + !spa_list_is_end(pos, head, member); \ + (pos) = spa_list_next(pos, member)) + +#define spa_list_for_each_prev(pos, head, curr, member) \ + for ((pos) = spa_list_last(curr, __typeof__(*(pos)), member); \ + !spa_list_is_end(pos, head, member); \ + (pos) = spa_list_prev(pos, member)) + +#define spa_list_for_each(pos, head, member) \ + spa_list_for_each_next(pos, head, head, member) + +#define spa_list_for_each_reverse(pos, head, member) \ + spa_list_for_each_prev(pos, head, head, member) + +#define spa_list_for_each_safe_next(pos, tmp, head, curr, member) \ + for ((pos) = spa_list_first(curr, __typeof__(*(pos)), member); \ + (tmp) = spa_list_next(pos, member), \ + !spa_list_is_end(pos, head, member); \ + (pos) = (tmp)) + +#define spa_list_for_each_safe_prev(pos, tmp, head, curr, member) \ + for ((pos) = spa_list_last(curr, __typeof__(*(pos)), member); \ + (tmp) = spa_list_prev(pos, member), \ + !spa_list_is_end(pos, head, member); \ + (pos) = (tmp)) + +#define spa_list_for_each_safe(pos, tmp, head, member) \ + spa_list_for_each_safe_next(pos, tmp, head, head, member) + +#define spa_list_for_each_safe_reverse(pos, tmp, head, member) \ + spa_list_for_each_safe_prev(pos, tmp, head, head, member) + +#define spa_list_cursor_start(cursor, head, member) \ + spa_list_prepend(head, &(cursor).member) + +#define spa_list_for_each_cursor(pos, cursor, head, member) \ + for((pos) = spa_list_first(&(cursor).member, __typeof__(*(pos)), member); \ + spa_list_remove(&(pos)->member), \ + spa_list_append(&(cursor).member, &(pos)->member), \ + !spa_list_is_end(pos, head, member); \ + (pos) = spa_list_next(&(cursor), member)) + +#define spa_list_cursor_end(cursor, member) \ + spa_list_remove(&(cursor).member) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_LIST_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/string.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/string.h new file mode 100644 index 0000000000000..529b8fa38cdc6 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/string.h @@ -0,0 +1,394 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2021 Red Hat, Inc. */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_UTILS_STRING_H +#define SPA_UTILS_STRING_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +#include + +/** + * \defgroup spa_string String handling + * String handling utilities + */ + +/** + * \addtogroup spa_string + * \{ + */ + +/** + * \return true if the two strings are equal, false otherwise + * + * If both \a a and \a b are NULL, the two are considered equal. + * + */ +static inline bool spa_streq(const char *s1, const char *s2) +{ + return SPA_LIKELY(s1 && s2) ? strcmp(s1, s2) == 0 : s1 == s2; +} + +/** + * \return true if the two strings are equal, false otherwise + * + * If both \a a and \a b are NULL, the two are considered equal. + */ +static inline bool spa_strneq(const char *s1, const char *s2, size_t len) +{ + return SPA_LIKELY(s1 && s2) ? strncmp(s1, s2, len) == 0 : s1 == s2; +} + + +/** + * \return true if \a s starts with the \a prefix or false otherwise. + * A \a s is NULL, it never starts with the given \a prefix. A \a prefix of + * NULL is a bug in the caller. + */ +static inline bool spa_strstartswith(const char *s, const char *prefix) +{ + if (SPA_UNLIKELY(s == NULL)) + return false; + + spa_assert_se(prefix); + + return strncmp(s, prefix, strlen(prefix)) == 0; +} + + +/** + * \return true if \a s ends with the \a suffix or false otherwise. + * A \a s is NULL, it never ends with the given \a suffix. A \a suffix of + * NULL is a bug in the caller. + */ +static inline bool spa_strendswith(const char *s, const char *suffix) +{ + size_t l1, l2; + + if (SPA_UNLIKELY(s == NULL)) + return false; + + spa_assert_se(suffix); + + l1 = strlen(s); + l2 = strlen(suffix); + return l1 >= l2 && spa_streq(s + l1 - l2, suffix); +} + +/** + * Convert \a str to an int32_t with the given \a base and store the + * result in \a val. + * + * On failure, the value of \a val is unmodified. + * + * \return true on success, false otherwise + */ +static inline bool spa_atoi32(const char *str, int32_t *val, int base) +{ + char *endptr; + long v; + + if (!str || *str =='\0') + return false; + + errno = 0; + v = strtol(str, &endptr, base); + if (errno != 0 || *endptr != '\0') + return false; + + if (v != (int32_t)v) + return false; + + *val = v; + return true; +} + +/** + * Convert \a str to an uint32_t with the given \a base and store the + * result in \a val. + * + * On failure, the value of \a val is unmodified. + * + * \return true on success, false otherwise + */ +static inline bool spa_atou32(const char *str, uint32_t *val, int base) +{ + char *endptr; + unsigned long long v; + + if (!str || *str =='\0') + return false; + + errno = 0; + v = strtoull(str, &endptr, base); + if (errno != 0 || *endptr != '\0') + return false; + + if (v != (uint32_t)v) + return false; + + *val = v; + return true; +} + +/** + * Convert \a str to an int64_t with the given \a base and store the + * result in \a val. + * + * On failure, the value of \a val is unmodified. + * + * \return true on success, false otherwise + */ +static inline bool spa_atoi64(const char *str, int64_t *val, int base) +{ + char *endptr; + long long v; + + if (!str || *str =='\0') + return false; + + errno = 0; + v = strtoll(str, &endptr, base); + if (errno != 0 || *endptr != '\0') + return false; + + *val = v; + return true; +} + +/** + * Convert \a str to an uint64_t with the given \a base and store the + * result in \a val. + * + * On failure, the value of \a val is unmodified. + * + * \return true on success, false otherwise + */ +static inline bool spa_atou64(const char *str, uint64_t *val, int base) +{ + char *endptr; + unsigned long long v; + + if (!str || *str =='\0') + return false; + + errno = 0; + v = strtoull(str, &endptr, base); + if (errno != 0 || *endptr != '\0') + return false; + + *val = v; + return true; +} + +/** + * Convert \a str to a boolean. Allowed boolean values are "true" and a + * literal "1", anything else is false. + * + * \return true on success, false otherwise + */ +static inline bool spa_atob(const char *str) +{ + return spa_streq(str, "true") || spa_streq(str, "1"); +} + +/** + * "Safe" version of vsnprintf. Exactly the same as vsnprintf but the + * returned value is clipped to `size - 1` and a negative or zero size + * will abort() the program. + * + * \return The number of bytes printed, capped to `size-1`, or a negative + * number on error. + */ +SPA_PRINTF_FUNC(3, 0) +static inline int spa_vscnprintf(char *buffer, size_t size, const char *format, va_list args) +{ + int r; + + spa_assert_se((ssize_t)size > 0); + + r = vsnprintf(buffer, size, format, args); + if (SPA_UNLIKELY(r < 0)) + buffer[0] = '\0'; + if (SPA_LIKELY(r < (ssize_t)size)) + return r; + return size - 1; +} + +/** + * "Safe" version of snprintf. Exactly the same as snprintf but the + * returned value is clipped to `size - 1` and a negative or zero size + * will abort() the program. + * + * \return The number of bytes printed, capped to `size-1`, or a negative + * number on error. + */ +SPA_PRINTF_FUNC(3, 4) +static inline int spa_scnprintf(char *buffer, size_t size, const char *format, ...) +{ + int r; + va_list args; + + va_start(args, format); + r = spa_vscnprintf(buffer, size, format, args); + va_end(args); + + return r; +} + +/** + * Convert \a str to a float in the C locale. + * + * If \a endptr is not NULL, a pointer to the character after the last character + * used in the conversion is stored in the location referenced by endptr. + * + * \return the result float. + */ +static inline float spa_strtof(const char *str, char **endptr) +{ +#ifndef __LOCALE_C_ONLY + static locale_t locale = NULL; + locale_t prev; +#endif + float v; +#ifndef __LOCALE_C_ONLY + if (SPA_UNLIKELY(locale == NULL)) + locale = newlocale(LC_ALL_MASK, "C", NULL); + prev = uselocale(locale); +#endif + v = strtof(str, endptr); +#ifndef __LOCALE_C_ONLY + uselocale(prev); +#endif + return v; +} + +/** + * Convert \a str to a float and store the result in \a val. + * + * On failure, the value of \a val is unmodified. + * + * \return true on success, false otherwise + */ +static inline bool spa_atof(const char *str, float *val) +{ + char *endptr; + float v; + + if (!str || *str =='\0') + return false; + errno = 0; + v = spa_strtof(str, &endptr); + if (errno != 0 || *endptr != '\0') + return false; + + *val = v; + return true; +} + +/** + * Convert \a str to a double in the C locale. + * + * If \a endptr is not NULL, a pointer to the character after the last character + * used in the conversion is stored in the location referenced by endptr. + * + * \return the result float. + */ +static inline double spa_strtod(const char *str, char **endptr) +{ +#ifndef __LOCALE_C_ONLY + static locale_t locale = NULL; + locale_t prev; +#endif + double v; +#ifndef __LOCALE_C_ONLY + if (SPA_UNLIKELY(locale == NULL)) + locale = newlocale(LC_ALL_MASK, "C", NULL); + prev = uselocale(locale); +#endif + v = strtod(str, endptr); +#ifndef __LOCALE_C_ONLY + uselocale(prev); +#endif + return v; +} + +/** + * Convert \a str to a double and store the result in \a val. + * + * On failure, the value of \a val is unmodified. + * + * \return true on success, false otherwise + */ +static inline bool spa_atod(const char *str, double *val) +{ + char *endptr; + double v; + + if (!str || *str =='\0') + return false; + + errno = 0; + v = spa_strtod(str, &endptr); + if (errno != 0 || *endptr != '\0') + return false; + + *val = v; + return true; +} + +static inline char *spa_dtoa(char *str, size_t size, double val) +{ + int i, l; + l = spa_scnprintf(str, size, "%f", val); + for (i = 0; i < l; i++) + if (str[i] == ',') + str[i] = '.'; + return str; +} + +struct spa_strbuf { + char *buffer; + size_t maxsize; + size_t pos; +}; + +static inline void spa_strbuf_init(struct spa_strbuf *buf, char *buffer, size_t maxsize) +{ + buf->buffer = buffer; + buf->maxsize = maxsize; + buf->pos = 0; +} + +SPA_PRINTF_FUNC(2, 3) +static inline int spa_strbuf_append(struct spa_strbuf *buf, const char *fmt, ...) +{ + size_t remain = buf->maxsize - buf->pos; + ssize_t written; + va_list args; + va_start(args, fmt); + written = vsnprintf(&buf->buffer[buf->pos], remain, fmt, args); + va_end(args); + if (written > 0) + buf->pos += SPA_MIN(remain, (size_t)written); + return written; +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_UTILS_STRING_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/type-info.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/type-info.h new file mode 100644 index 0000000000000..86a6b87bc49c8 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/type-info.h @@ -0,0 +1,98 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_TYPE_INFO_H +#define SPA_TYPE_INFO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * \addtogroup spa_types + * \{ + */ + +#ifndef SPA_TYPE_ROOT +#define SPA_TYPE_ROOT spa_types +#endif + +static inline bool spa_type_is_a(const char *type, const char *parent) +{ + return type != NULL && parent != NULL && strncmp(type, parent, strlen(parent)) == 0; +} + +#include +#include + +#include +#include +#include +#include + +static const struct spa_type_info spa_types[] = { + /* Basic types */ + { SPA_TYPE_START, SPA_TYPE_START, SPA_TYPE_INFO_BASE, NULL }, + { SPA_TYPE_None, SPA_TYPE_None, SPA_TYPE_INFO_BASE "None", NULL }, + { SPA_TYPE_Bool, SPA_TYPE_Bool, SPA_TYPE_INFO_BASE "Bool", NULL }, + { SPA_TYPE_Id, SPA_TYPE_Int, SPA_TYPE_INFO_BASE "Id", NULL }, + { SPA_TYPE_Int, SPA_TYPE_Int, SPA_TYPE_INFO_BASE "Int", NULL }, + { SPA_TYPE_Long, SPA_TYPE_Long, SPA_TYPE_INFO_BASE "Long", NULL }, + { SPA_TYPE_Float, SPA_TYPE_Float, SPA_TYPE_INFO_BASE "Float", NULL }, + { SPA_TYPE_Double, SPA_TYPE_Double, SPA_TYPE_INFO_BASE "Double", NULL }, + { SPA_TYPE_String, SPA_TYPE_String, SPA_TYPE_INFO_BASE "String", NULL }, + { SPA_TYPE_Bytes, SPA_TYPE_Bytes, SPA_TYPE_INFO_BASE "Bytes", NULL }, + { SPA_TYPE_Rectangle, SPA_TYPE_Rectangle, SPA_TYPE_INFO_BASE "Rectangle", NULL }, + { SPA_TYPE_Fraction, SPA_TYPE_Fraction, SPA_TYPE_INFO_BASE "Fraction", NULL }, + { SPA_TYPE_Bitmap, SPA_TYPE_Bitmap, SPA_TYPE_INFO_BASE "Bitmap", NULL }, + { SPA_TYPE_Array, SPA_TYPE_Array, SPA_TYPE_INFO_BASE "Array", NULL }, + { SPA_TYPE_Pod, SPA_TYPE_Pod, SPA_TYPE_INFO_Pod, NULL }, + { SPA_TYPE_Struct, SPA_TYPE_Pod, SPA_TYPE_INFO_Struct, NULL }, + { SPA_TYPE_Object, SPA_TYPE_Pod, SPA_TYPE_INFO_Object, NULL }, + { SPA_TYPE_Sequence, SPA_TYPE_Pod, SPA_TYPE_INFO_POD_BASE "Sequence", NULL }, + { SPA_TYPE_Pointer, SPA_TYPE_Pointer, SPA_TYPE_INFO_Pointer, NULL }, + { SPA_TYPE_Fd, SPA_TYPE_Fd, SPA_TYPE_INFO_BASE "Fd", NULL }, + { SPA_TYPE_Choice, SPA_TYPE_Pod, SPA_TYPE_INFO_POD_BASE "Choice", NULL }, + + { SPA_TYPE_POINTER_START, SPA_TYPE_Pointer, SPA_TYPE_INFO_Pointer, NULL }, + { SPA_TYPE_POINTER_Buffer, SPA_TYPE_Pointer, SPA_TYPE_INFO_POINTER_BASE "Buffer", NULL }, + { SPA_TYPE_POINTER_Meta, SPA_TYPE_Pointer, SPA_TYPE_INFO_POINTER_BASE "Meta", NULL }, + { SPA_TYPE_POINTER_Dict, SPA_TYPE_Pointer, SPA_TYPE_INFO_POINTER_BASE "Dict", NULL }, + + { SPA_TYPE_EVENT_START, SPA_TYPE_Object, SPA_TYPE_INFO_Event, NULL }, + { SPA_TYPE_EVENT_Device, SPA_TYPE_Object, SPA_TYPE_INFO_EVENT_BASE "Device", spa_type_device_event }, + { SPA_TYPE_EVENT_Node, SPA_TYPE_Object, SPA_TYPE_INFO_EVENT_BASE "Node", spa_type_node_event }, + + { SPA_TYPE_COMMAND_START, SPA_TYPE_Object, SPA_TYPE_INFO_Command, NULL }, + { SPA_TYPE_COMMAND_Device, SPA_TYPE_Object, SPA_TYPE_INFO_COMMAND_BASE "Device", NULL }, + { SPA_TYPE_COMMAND_Node, SPA_TYPE_Object, SPA_TYPE_INFO_COMMAND_BASE "Node", spa_type_node_command }, + + { SPA_TYPE_OBJECT_START, SPA_TYPE_Object, SPA_TYPE_INFO_Object, NULL }, + { SPA_TYPE_OBJECT_PropInfo, SPA_TYPE_Object, SPA_TYPE_INFO_PropInfo, spa_type_prop_info, }, + { SPA_TYPE_OBJECT_Props, SPA_TYPE_Object, SPA_TYPE_INFO_Props, spa_type_props }, + { SPA_TYPE_OBJECT_Format, SPA_TYPE_Object, SPA_TYPE_INFO_Format, spa_type_format }, + { SPA_TYPE_OBJECT_ParamBuffers, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Buffers, spa_type_param_buffers, }, + { SPA_TYPE_OBJECT_ParamMeta, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Meta, spa_type_param_meta }, + { SPA_TYPE_OBJECT_ParamIO, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_IO, spa_type_param_io }, + { SPA_TYPE_OBJECT_ParamProfile, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Profile, spa_type_param_profile }, + { SPA_TYPE_OBJECT_ParamPortConfig, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_PortConfig, spa_type_param_port_config }, + { SPA_TYPE_OBJECT_ParamRoute, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Route, spa_type_param_route }, + { SPA_TYPE_OBJECT_Profiler, SPA_TYPE_Object, SPA_TYPE_INFO_Profiler, spa_type_profiler }, + { SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Latency, spa_type_param_latency }, + { SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_ProcessLatency, spa_type_param_process_latency }, + + { 0, 0, NULL, NULL } +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_TYPE_INFO_H */ diff --git a/src/java.desktop/unix/native/libpipewire/include/spa/utils/type.h b/src/java.desktop/unix/native/libpipewire/include/spa/utils/type.h new file mode 100644 index 0000000000000..fcadda93101a3 --- /dev/null +++ b/src/java.desktop/unix/native/libpipewire/include/spa/utils/type.h @@ -0,0 +1,133 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_TYPE_H +#define SPA_TYPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** \defgroup spa_types Types + * Data type information enumerations + */ + +/** + * \addtogroup spa_types + * \{ + */ + +enum { + /* Basic types */ + SPA_TYPE_START = 0x00000, + SPA_TYPE_None, + SPA_TYPE_Bool, + SPA_TYPE_Id, + SPA_TYPE_Int, + SPA_TYPE_Long, + SPA_TYPE_Float, + SPA_TYPE_Double, + SPA_TYPE_String, + SPA_TYPE_Bytes, + SPA_TYPE_Rectangle, + SPA_TYPE_Fraction, + SPA_TYPE_Bitmap, + SPA_TYPE_Array, + SPA_TYPE_Struct, + SPA_TYPE_Object, + SPA_TYPE_Sequence, + SPA_TYPE_Pointer, + SPA_TYPE_Fd, + SPA_TYPE_Choice, + SPA_TYPE_Pod, + _SPA_TYPE_LAST, /**< not part of ABI */ + + /* Pointers */ + SPA_TYPE_POINTER_START = 0x10000, + SPA_TYPE_POINTER_Buffer, + SPA_TYPE_POINTER_Meta, + SPA_TYPE_POINTER_Dict, + _SPA_TYPE_POINTER_LAST, /**< not part of ABI */ + + /* Events */ + SPA_TYPE_EVENT_START = 0x20000, + SPA_TYPE_EVENT_Device, + SPA_TYPE_EVENT_Node, + _SPA_TYPE_EVENT_LAST, /**< not part of ABI */ + + /* Commands */ + SPA_TYPE_COMMAND_START = 0x30000, + SPA_TYPE_COMMAND_Device, + SPA_TYPE_COMMAND_Node, + _SPA_TYPE_COMMAND_LAST, /**< not part of ABI */ + + /* Objects */ + SPA_TYPE_OBJECT_START = 0x40000, + SPA_TYPE_OBJECT_PropInfo, + SPA_TYPE_OBJECT_Props, + SPA_TYPE_OBJECT_Format, + SPA_TYPE_OBJECT_ParamBuffers, + SPA_TYPE_OBJECT_ParamMeta, + SPA_TYPE_OBJECT_ParamIO, + SPA_TYPE_OBJECT_ParamProfile, + SPA_TYPE_OBJECT_ParamPortConfig, + SPA_TYPE_OBJECT_ParamRoute, + SPA_TYPE_OBJECT_Profiler, + SPA_TYPE_OBJECT_ParamLatency, + SPA_TYPE_OBJECT_ParamProcessLatency, + _SPA_TYPE_OBJECT_LAST, /**< not part of ABI */ + + /* vendor extensions */ + SPA_TYPE_VENDOR_PipeWire = 0x02000000, + + SPA_TYPE_VENDOR_Other = 0x7f000000, +}; + +#define SPA_TYPE_INFO_BASE "Spa:" + +#define SPA_TYPE_INFO_Flags SPA_TYPE_INFO_BASE "Flags" +#define SPA_TYPE_INFO_FLAGS_BASE SPA_TYPE_INFO_Flags ":" + +#define SPA_TYPE_INFO_Enum SPA_TYPE_INFO_BASE "Enum" +#define SPA_TYPE_INFO_ENUM_BASE SPA_TYPE_INFO_Enum ":" + +#define SPA_TYPE_INFO_Pod SPA_TYPE_INFO_BASE "Pod" +#define SPA_TYPE_INFO_POD_BASE SPA_TYPE_INFO_Pod ":" + +#define SPA_TYPE_INFO_Struct SPA_TYPE_INFO_POD_BASE "Struct" +#define SPA_TYPE_INFO_STRUCT_BASE SPA_TYPE_INFO_Struct ":" + +#define SPA_TYPE_INFO_Object SPA_TYPE_INFO_POD_BASE "Object" +#define SPA_TYPE_INFO_OBJECT_BASE SPA_TYPE_INFO_Object ":" + +#define SPA_TYPE_INFO_Pointer SPA_TYPE_INFO_BASE "Pointer" +#define SPA_TYPE_INFO_POINTER_BASE SPA_TYPE_INFO_Pointer ":" + +#define SPA_TYPE_INFO_Interface SPA_TYPE_INFO_POINTER_BASE "Interface" +#define SPA_TYPE_INFO_INTERFACE_BASE SPA_TYPE_INFO_Interface ":" + +#define SPA_TYPE_INFO_Event SPA_TYPE_INFO_OBJECT_BASE "Event" +#define SPA_TYPE_INFO_EVENT_BASE SPA_TYPE_INFO_Event ":" + +#define SPA_TYPE_INFO_Command SPA_TYPE_INFO_OBJECT_BASE "Command" +#define SPA_TYPE_INFO_COMMAND_BASE SPA_TYPE_INFO_Command ":" + +struct spa_type_info { + uint32_t type; + uint32_t parent; + const char *name; + const struct spa_type_info *values; +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_TYPE_H */ diff --git a/src/java.desktop/unix/native/libsplashscreen/splashscreen_sys.c b/src/java.desktop/unix/native/libsplashscreen/splashscreen_sys.c index 929cfd313f453..8cab841c0daf7 100644 --- a/src/java.desktop/unix/native/libsplashscreen/splashscreen_sys.c +++ b/src/java.desktop/unix/native/libsplashscreen/splashscreen_sys.c @@ -387,7 +387,7 @@ HandleError(Display * disp, XErrorEvent * err) { XGetErrorText(disp, err->error_code, msg, sizeof(msg)); fprintf(stderr, "Xerror %s, XID %x, ser# %d\n", msg, err->resourceid, err->serial); - sprintf(buf, "%d", err->request_code); + snprintf(buf, sizeof(buf), "%d", err->request_code); XGetErrorDatabaseText(disp, "XRequest", buf, "Unknown", msg, sizeof(msg)); fprintf(stderr, "Major opcode %d (%s)\n", err->request_code, msg); if (err->request_code > 128) { diff --git a/src/java.desktop/windows/classes/sun/awt/PlatformGraphicsInfo.java b/src/java.desktop/windows/classes/sun/awt/PlatformGraphicsInfo.java index 4644a2e5f46ac..45b961b1b3d83 100644 --- a/src/java.desktop/windows/classes/sun/awt/PlatformGraphicsInfo.java +++ b/src/java.desktop/windows/classes/sun/awt/PlatformGraphicsInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,41 +28,20 @@ import java.awt.GraphicsEnvironment; import java.awt.Toolkit; -import sun.awt.windows.WToolkit; - public class PlatformGraphicsInfo { - private static final boolean hasDisplays; - - static { - loadAWTLibrary(); - hasDisplays = hasDisplays0(); - } - - @SuppressWarnings("removal") - private static void loadAWTLibrary() { - java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Void run() { - System.loadLibrary("awt"); - return null; - } - }); - } - - private static native boolean hasDisplays0(); - public static GraphicsEnvironment createGE() { return new Win32GraphicsEnvironment(); } public static Toolkit createToolkit() { - return new WToolkit(); + return new sun.awt.windows.WToolkit(); } public static boolean getDefaultHeadlessProperty() { - // If we don't find usable displays, we run headless. - return !hasDisplays; + // On Windows, we assume we can always create headful apps. + // Here is where we can add code that would actually check. + return false; } /* diff --git a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java index cb7ab363cdfca..d46b596976d1b 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,8 +60,7 @@ public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment { WToolkit.loadLibraries(); // setup flags before initializing native layer WindowsFlags.initFlags(); - - initDisplay(); + initDisplayWrapper(); // Install correct surface manager factory. SurfaceManagerFactory.setInstance(new WindowsSurfaceManagerFactory()); @@ -89,6 +88,14 @@ public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment { */ private static native void initDisplay(); + private static boolean displayInitialized; // = false; + public static void initDisplayWrapper() { + if (!displayInitialized) { + displayInitialized = true; + initDisplay(); + } + } + public Win32GraphicsEnvironment() { } diff --git a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java index c8fc3a4627e19..db3bde5889ad5 100644 --- a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java +++ b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,9 @@ package sun.awt.shell; +import java.awt.Graphics2D; import java.awt.Image; +import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.image.AbstractMultiResolutionImage; import java.awt.image.BufferedImage; @@ -1118,7 +1120,10 @@ public Image call() { if (hiResIconAvailable(getParentIShellFolder(), getRelativePIDL()) || newIcon == null) { int size = getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE; - newIcon = getIcon(size, size); + newIcon2 = getIcon(size, size); + if (newIcon2 != null) { + newIcon = newIcon2; + } } if (newIcon == null) { @@ -1131,6 +1136,14 @@ public Image call() { return icon; } + /** + * The data is not available yet. + * @see + * COM + * Error Codes. + */ + private static final long E_PENDING = 0x8000000AL; + /** * @return The icon image of specified size used to display this shell folder */ @@ -1156,10 +1169,10 @@ public Image getIcon(int width, int height) { getRelativePIDL(), s, false); // E_PENDING: loading can take time so get the default - if (hIcon <= 0) { + if (hIcon == E_PENDING || hIcon == 0) { hIcon = extractIcon(getParentIShellFolder(), getRelativePIDL(), s, true); - if (hIcon <= 0) { + if (hIcon == 0) { if (isDirectory()) { newIcon = getShell32Icon(FOLDER_ICON_ID, size); } else { @@ -1177,6 +1190,9 @@ public Image getIcon(int width, int height) { newIcon = makeIcon(hIcon); disposeIcon(hIcon); + if (newIcon == null) { + return null; + } multiResolutionIcon.put(s, newIcon); if (size < MIN_QUALITY_ICON || size > MAX_QUALITY_ICON) { break; @@ -1396,11 +1412,14 @@ static class MultiResolutionIconImage extends AbstractMultiResolutionImage { final Map resolutionVariants = new HashMap<>(); public MultiResolutionIconImage(int baseSize, Map resolutionVariants) { + assert !resolutionVariants.containsValue(null) + : "There are null icons in the MRI variants map"; this.baseSize = baseSize; this.resolutionVariants.putAll(resolutionVariants); } public MultiResolutionIconImage(int baseSize, Image image) { + assert image != null : "Null icon passed as the base image for MRI"; this.baseSize = baseSize; this.resolutionVariants.put(baseSize, image); } @@ -1424,7 +1443,7 @@ protected Image getBaseImage() { public Image getResolutionVariant(double width, double height) { int dist = 0; Image retVal = null; - // We only care about width since we don't support non-rectangular icons + // We only care about width since we don't support non-square icons int w = (int) width; int retindex = 0; for (Integer i : resolutionVariants.keySet()) { @@ -1438,6 +1457,15 @@ public Image getResolutionVariant(double width, double height) { } } } + if (retVal.getWidth(null) != w) { + BufferedImage newVariant = new BufferedImage(w, w, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = newVariant.createGraphics(); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g2d.drawImage(retVal, 0,0, w, w, null); + g2d.dispose(); + resolutionVariants.put(w, newVariant); + retVal = newVariant; + } return retVal; } diff --git a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java index c75d773814439..20df99eb7d6a8 100644 --- a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java +++ b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -522,7 +522,7 @@ static int compareShellFolders(Win32ShellFolder2 sf1, Win32ShellFolder2 sf2) { boolean special1 = sf1.isSpecial(); boolean special2 = sf2.isSpecial(); - if (special1 || special2) { + if (special1 && special2) { if (topFolderList == null) { ArrayList tmpTopFolderList = new ArrayList<>(); tmpTopFolderList.add(Win32ShellFolderManager2.getPersonal()); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDataTransferer.java b/src/java.desktop/windows/classes/sun/awt/windows/WDataTransferer.java index 16b6faf7ee714..c82fc91a4e1e9 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WDataTransferer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WDataTransferer.java @@ -25,19 +25,15 @@ package sun.awt.windows; -import java.awt.Image; import java.awt.Graphics2D; +import java.awt.Image; import java.awt.Transparency; - import java.awt.color.ColorSpace; - import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.FlavorTable; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; - import java.awt.geom.AffineTransform; - import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; @@ -48,18 +44,16 @@ import java.awt.image.ImageObserver; import java.awt.image.Raster; import java.awt.image.WritableRaster; - import java.io.BufferedInputStream; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.io.File; - import java.net.URL; - import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -69,13 +63,11 @@ import sun.awt.Mutex; import sun.awt.datatransfer.DataTransferer; import sun.awt.datatransfer.ToolkitThreadBlockedHandler; - import sun.awt.image.ImageRepresentation; import sun.awt.image.ToolkitImage; -import java.util.ArrayList; - -import java.io.ByteArrayOutputStream; +import static java.nio.charset.StandardCharsets.UTF_16LE; +import static java.nio.charset.StandardCharsets.UTF_8; /** * Platform-specific support for the data transfer subsystem. @@ -249,7 +241,7 @@ public Object translateBytes(byte[] bytes, DataFlavor flavor, long format, if (bytes == null || !DataFlavor.javaFileListFlavor.equals(flavor)) { throw new IOException("data translation failed"); } - String st = new String(bytes, 0, bytes.length, "UTF-16LE"); + String st = new String(bytes, 0, bytes.length, UTF_16LE); String[] filenames = st.split("\0"); if( 0 == filenames.length ){ return null; @@ -275,7 +267,7 @@ public Object translateBytes(byte[] bytes, DataFlavor flavor, long format, { try { charset = new String((byte[])localeTransferable. - getTransferData(javaTextEncodingFlavor), "UTF-8"); + getTransferData(javaTextEncodingFlavor), UTF_8); } catch (UnsupportedFlavorException cannotHappen) { } } @@ -548,8 +540,6 @@ public static EHTMLReadMode getEHTMLReadMode (DataFlavor df) { * on encode: static convertToHTMLFormat is responsible for HTML clipboard header creation */ class HTMLCodec extends InputStream { - //static section - public static final String ENCODING = "UTF-8"; public static final String VERSION = "Version:"; public static final String START_HTML = "StartHTML:"; @@ -671,13 +661,8 @@ public static byte[] convertToHTMLFormat(byte[] bytes) { //HTML header.append(htmlPrefix); - byte[] headerBytes = null, trailerBytes = null; - - try { - headerBytes = header.toString().getBytes(ENCODING); - trailerBytes = htmlSuffix.getBytes(ENCODING); - } catch (UnsupportedEncodingException cannotHappen) { - } + byte[] headerBytes = header.toString().getBytes(UTF_8); + byte[] trailerBytes = htmlSuffix.getBytes(UTF_8); byte[] retval = new byte[headerBytes.length + bytes.length + trailerBytes.length]; @@ -786,7 +771,7 @@ private void parseDescription() throws IOException BufferedReader bufferedReader = new BufferedReader( new InputStreamReader( bufferedStream, - ENCODING + UTF_8 ), CHAR_BUFFER_LEN ); diff --git a/src/java.desktop/windows/native/libawt/java2d/d3d/D3DShaderGen.c b/src/java.desktop/windows/native/libawt/java2d/d3d/D3DShaderGen.c index 88b38cd258240..e7e9f859116a4 100644 --- a/src/java.desktop/windows/native/libawt/java2d/d3d/D3DShaderGen.c +++ b/src/java.desktop/windows/native/libawt/java2d/d3d/D3DShaderGen.c @@ -79,7 +79,7 @@ D3DShaderGen_WriteShader(char *source, char *target, char *name, int flags) PROCESS_INFORMATION pi; STARTUPINFO si; char pargs[300]; - sprintf(pargs, + snprintf(pargs, sizeof(pargs), "c:\\progra~1\\mi5889~1\\utilit~1\\bin\\x86\\fxc.exe " "/T %s /Vn %s%d /Fh tmp.h tmp.hlsl", // uncomment the following line to generate debug @@ -144,13 +144,13 @@ D3DShaderGen_WriteShaderArray(char *name, int num) char elem[30]; int i; - sprintf(array, "const DWORD *%sShaders[] =\n{\n", name); + snprintf(array, sizeof(array), "const DWORD *%sShaders[] =\n{\n", name); for (i = 0; i < num; i++) { if (num == 32 && EXTRACT_CYCLE_METHOD(i) == 3) { // REMIND: what a hack! - sprintf(elem, " NULL,\n"); + snprintf(elem, sizeof(elem), " NULL,\n"); } else { - sprintf(elem, " %s%d,\n", name, i); + snprintf(elem, sizeof(elem), " %s%d,\n", name, i); } strcat(array, elem); } @@ -225,7 +225,7 @@ D3DShaderGen_GenerateConvolveShader(int flags) } // compose the final source code string from the various pieces - sprintf(finalSource, convolveShaderSource, + snprintf(finalSource, sizeof(finalSource), convolveShaderSource, kernelMax, edge, kernelMax); D3DShaderGen_WritePixelShader(finalSource, "convolve", flags); @@ -283,7 +283,7 @@ D3DShaderGen_GenerateRescaleShader(int flags) } // compose the final source code string from the various pieces - sprintf(finalSource, rescaleShaderSource, + snprintf(finalSource, sizeof(finalSource), rescaleShaderSource, preRescale, postRescale); D3DShaderGen_WritePixelShader(finalSource, "rescale", flags); @@ -357,7 +357,7 @@ D3DShaderGen_GenerateLookupShader(int flags) } // compose the final source code string from the various pieces - sprintf(finalSource, lookupShaderSource, + snprintf(finalSource, sizeof(finalSource), lookupShaderSource, preLookup, alpha, postLookup); D3DShaderGen_WritePixelShader(finalSource, "lookup", flags); @@ -452,7 +452,7 @@ D3DShaderGen_GenerateBasicGradShader(int flags) } // compose the final source code string from the various pieces - sprintf(finalSource, basicGradientShaderSource, + snprintf(finalSource, sizeof(finalSource), basicGradientShaderSource, maskVars, maskInput, colorSampler, cycleCode, maskCode); D3DShaderGen_WritePixelShader(finalSource, "grad", flags); @@ -665,15 +665,15 @@ D3DShaderGen_GenerateMultiGradShader(int flags, char *name, } if (cycleMethod == CYCLE_NONE) { - sprintf(cycleCode, noCycleCode, texCoordCalcCode); + snprintf(cycleCode, sizeof(cycleCode), noCycleCode, texCoordCalcCode); } else if (cycleMethod == CYCLE_REFLECT) { - sprintf(cycleCode, reflectCode, texCoordCalcCode); + snprintf(cycleCode, sizeof(cycleCode), reflectCode, texCoordCalcCode); } else { // (cycleMethod == CYCLE_REPEAT) - sprintf(cycleCode, repeatCode, texCoordCalcCode); + snprintf(cycleCode, sizeof(cycleCode), repeatCode, texCoordCalcCode); } // compose the final source code string from the various pieces - sprintf(finalSource, multiGradientShaderSource, + snprintf(finalSource, sizeof(finalSource), multiGradientShaderSource, MAX_COLORS, maxFractions, colorSampler, maskVars, paintVars, maskInput, colorSampler, distCode, cycleCode, colorSpaceCode, maskCode); diff --git a/src/java.desktop/windows/native/libawt/windows/Devices.cpp b/src/java.desktop/windows/native/libawt/windows/Devices.cpp index abf459c9149cf..9738bd057e123 100644 --- a/src/java.desktop/windows/native/libawt/windows/Devices.cpp +++ b/src/java.desktop/windows/native/libawt/windows/Devices.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -85,75 +85,60 @@ #include "Trace.h" #include "D3DPipelineManager.h" -typedef struct { - int monitorCounter; - int monitorLimit; - HMONITOR* hmpMonitors; -} MonitorData; +/* Some helper functions (from awt_MMStub.h/cpp) */ -// Only monitors where CreateDC does not fail are valid -static BOOL IsValidMonitor(HMONITOR hMon) -{ - MONITORINFOEX mieInfo; - memset((void*)(&mieInfo), 0, sizeof(MONITORINFOEX)); - mieInfo.cbSize = sizeof(MONITORINFOEX); - if (!::GetMonitorInfo(hMon, (LPMONITORINFOEX)(&mieInfo))) { - J2dTraceLn1(J2D_TRACE_INFO, "Devices::IsValidMonitor: GetMonitorInfo failed for monitor with handle %p", hMon); - return FALSE; - } - - HDC hDC = CreateDC(mieInfo.szDevice, NULL, NULL, NULL); - if (NULL == hDC) { - J2dTraceLn2(J2D_TRACE_INFO, "Devices::IsValidMonitor: CreateDC failed for monitor with handle %p, device: %S", hMon, mieInfo.szDevice); - return FALSE; - } - - ::DeleteDC(hDC); - return TRUE; -} +int g_nMonitorCounter; +int g_nMonitorLimit; +HMONITOR* g_hmpMonitors; // Callback for CountMonitors below -static BOOL WINAPI clb_fCountMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lpMonitorCounter) +BOOL WINAPI clb_fCountMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP) { - if (IsValidMonitor(hMon)) { - (*((int *)lpMonitorCounter))++; - } - + g_nMonitorCounter ++; return TRUE; } int WINAPI CountMonitors(void) { - int monitorCounter = 0; - ::EnumDisplayMonitors(NULL, NULL, clb_fCountMonitors, (LPARAM)&monitorCounter); - return monitorCounter; + g_nMonitorCounter = 0; + ::EnumDisplayMonitors(NULL, NULL, clb_fCountMonitors, 0L); + return g_nMonitorCounter; + } // Callback for CollectMonitors below -static BOOL WINAPI clb_fCollectMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lpMonitorData) +BOOL WINAPI clb_fCollectMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP) { - MonitorData* pMonitorData = (MonitorData *)lpMonitorData; - if ((pMonitorData->monitorCounter < pMonitorData->monitorLimit) && (IsValidMonitor(hMon))) { - pMonitorData->hmpMonitors[pMonitorData->monitorCounter] = hMon; - pMonitorData->monitorCounter++; + + if ((g_nMonitorCounter < g_nMonitorLimit) && (NULL != g_hmpMonitors)) { + g_hmpMonitors[g_nMonitorCounter] = hMon; + g_nMonitorCounter ++; } return TRUE; } -static int WINAPI CollectMonitors(HMONITOR* hmpMonitors, int nNum) +int WINAPI CollectMonitors(HMONITOR* hmpMonitors, int nNum) { + int retCode = 0; + if (NULL != hmpMonitors) { - MonitorData monitorData; - monitorData.monitorCounter = 0; - monitorData.monitorLimit = nNum; - monitorData.hmpMonitors = hmpMonitors; - ::EnumDisplayMonitors(NULL, NULL, clb_fCollectMonitors, (LPARAM)&monitorData); - return monitorData.monitorCounter; - } else { - return 0; + + g_nMonitorCounter = 0; + g_nMonitorLimit = nNum; + g_hmpMonitors = hmpMonitors; + + ::EnumDisplayMonitors(NULL, NULL, clb_fCollectMonitors, 0L); + + retCode = g_nMonitorCounter; + + g_nMonitorCounter = 0; + g_nMonitorLimit = 0; + g_hmpMonitors = NULL; + } + return retCode; } BOOL WINAPI MonitorBounds(HMONITOR hmMonitor, RECT* rpBounds) diff --git a/src/java.desktop/windows/native/libawt/windows/Devices.h b/src/java.desktop/windows/native/libawt/windows/Devices.h index 0972ef1414e5d..b6fd56a777f61 100644 --- a/src/java.desktop/windows/native/libawt/windows/Devices.h +++ b/src/java.desktop/windows/native/libawt/windows/Devices.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,6 +74,4 @@ static CriticalSection arrayLock; BOOL WINAPI MonitorBounds (HMONITOR, RECT*); -int WINAPI CountMonitors (void); - #endif // _DEVICES_H_ diff --git a/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp index cc2f567dc680b..2d0745f763fb8 100644 --- a/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp +++ b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -300,7 +300,7 @@ JNIEXPORT void JNICALL Java_sun_awt_shell_Win32ShellFolderManager2_initializeCom HRESULT hr = ::CoInitialize(NULL); if (FAILED(hr)) { char c[64]; - sprintf(c, "Could not initialize COM: HRESULT=0x%08X", hr); + snprintf(c, sizeof(c), "Could not initialize COM: HRESULT=0x%08X", hr); JNU_ThrowInternalError(env, c); } } @@ -974,7 +974,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_extractIcon return 0; } - HICON hIcon = NULL; + HICON hIcon; HRESULT hres; IExtractIconW* pIcon; @@ -987,13 +987,29 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_extractIcon UINT uFlags = getDefaultIcon ? GIL_DEFAULTICON : GIL_FORSHELL | GIL_ASYNC; hres = pIcon->GetIconLocation(uFlags, szBuf, MAX_PATH, &index, &flags); if (SUCCEEDED(hres)) { + UINT iconSize; + HICON hIconSmall; if (size < 24) { - size = 16; + iconSize = (size << 16) + 32; + } else { + iconSize = (16 << 16) + size; + } + hres = pIcon->Extract(szBuf, index, &hIcon, &hIconSmall, iconSize); + if (SUCCEEDED(hres)) { + if (size < 24) { + fn_DestroyIcon((HICON)hIcon); + hIcon = hIconSmall; + } else { + fn_DestroyIcon((HICON)hIconSmall); + } + } else { + hIcon = NULL; } - hres = pIcon->Extract(szBuf, index, &hIcon, NULL, size); } else if (hres == E_PENDING) { pIcon->Release(); - return E_PENDING; + return (unsigned) E_PENDING; + } else { + hIcon = NULL; } pIcon->Release(); } diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Button.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Button.cpp index 1a7a89fdef425..714becbca20e7 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Button.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Button.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -242,7 +242,7 @@ AwtButton::OwnerDrawItem(UINT /*ctrlId*/, DRAWITEMSTRUCT& drawInfo) RECT focusRect; VERIFY(::CopyRect(&focusRect, &rect)); VERIFY(::InflateRect(&focusRect,-inf,-inf)); - if(::DrawFocusRect(hDC, &focusRect) == 0) + if (!::IsRectEmpty(&focusRect) && (::DrawFocusRect(hDC, &focusRect) == 0)) VERIFY(::GetLastError() == 0); } diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Checkbox.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Checkbox.cpp index d77ebb74a3e34..f50ddbb5d57da 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Checkbox.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Checkbox.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -290,13 +290,13 @@ AwtCheckbox::OwnerDrawItem(UINT /*ctrlId*/, DRAWITEMSTRUCT& drawInfo) if ((drawInfo.itemState & ODS_FOCUS) && ((drawInfo.itemAction & ODA_FOCUS)|| (drawInfo.itemAction &ODA_DRAWENTIRE))) { - if(::DrawFocusRect(hDC, &focusRect) == 0) + if (!::IsRectEmpty(&focusRect) && (::DrawFocusRect(hDC, &focusRect) == 0)) VERIFY(::GetLastError() == 0); } /* erase focus rect */ else if (!(drawInfo.itemState & ODS_FOCUS) && (drawInfo.itemAction & ODA_FOCUS)) { - if(::DrawFocusRect(hDC, &focusRect) == 0) + if (!::IsRectEmpty(&focusRect) && (::DrawFocusRect(hDC, &focusRect) == 0)) VERIFY(::GetLastError() == 0); } diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp index 32ead20707e18..7c0ac107e561f 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1334,7 +1334,7 @@ void SpyWinMessage(HWND hwnd, UINT message, LPCTSTR szComment) { WIN_MSG(WM_AWT_CREATE_PRINTED_PIXELS) WIN_MSG(WM_AWT_OBJECTLISTCLEANUP) default: - sprintf(szBuf, "0x%8.8x(%s):Unknown message 0x%8.8x\n", + snprintf(szBuf, sizeof(szBuf), "0x%8.8x(%s):Unknown message 0x%8.8x\n", hwnd, szComment, message); break; } @@ -4499,7 +4499,7 @@ void AwtComponent::DrawListItem(JNIEnv *env, DRAWITEMSTRUCT &drawInfo) if ((drawInfo.itemState & ODS_FOCUS) && (drawInfo.itemAction & (ODA_FOCUS | ODA_DRAWENTIRE))) { if (!unfocusableChoice){ - if(::DrawFocusRect(hDC, &rect) == 0) + if (!::IsRectEmpty(&rect) && (::DrawFocusRect(hDC, &rect) == 0)) VERIFY(::GetLastError() == 0); } } diff --git a/src/java.desktop/windows/native/libawt/windows/awt_PlatformGraphicsInfo.cpp b/src/java.desktop/windows/native/libawt/windows/awt_PlatformGraphicsInfo.cpp deleted file mode 100644 index 638cd100b1fac..0000000000000 --- a/src/java.desktop/windows/native/libawt/windows/awt_PlatformGraphicsInfo.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2024 SAP SE. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include -#include "Devices.h" - -/* - * Class: sun_awt_PlatformGraphicsInfo - * Method: hasDisplays0 - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL -Java_sun_awt_PlatformGraphicsInfo_hasDisplays0(JNIEnv *env, jclass thisClass) { - return CountMonitors() > 0 ? JNI_TRUE : JNI_FALSE; -} diff --git a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiIn.cpp b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiIn.cpp index 706ab62177b2e..4d1f4fee19e9e 100644 --- a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiIn.cpp +++ b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiIn.cpp @@ -282,7 +282,7 @@ INT32 MIDI_IN_GetDeviceVersion(INT32 deviceID, char *name, UINT32 nameLength) { memset(&midiInCaps, 0, sizeof(midiInCaps)); if (getMidiInCaps(deviceID, &midiInCaps, &err) && (nameLength>7)) { - sprintf(name, "%d.%d", (midiInCaps.vDriverVersion & 0xFF00) >> 8, midiInCaps.vDriverVersion & 0xFF); + snprintf(name, nameLength + 1, "%d.%d", (midiInCaps.vDriverVersion & 0xFF00) >> 8, midiInCaps.vDriverVersion & 0xFF); return MIDI_SUCCESS; } MIDIIN_CHECK_ERROR; diff --git a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiOut.c b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiOut.c index 572d7427ddbc0..d48798b0d3fb4 100644 --- a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiOut.c +++ b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiOut.c @@ -139,7 +139,7 @@ INT32 MIDI_OUT_GetDeviceVersion(INT32 deviceID, char *name, UINT32 nameLength) { memset(&midiOutCaps, 0, sizeof(midiOutCaps)); if (getMidiOutCaps(deviceID, &midiOutCaps, &err) && nameLength>7) { - sprintf(name, "%d.%d", (midiOutCaps.vDriverVersion & 0xFF00) >> 8, midiOutCaps.vDriverVersion & 0xFF); + snprintf(name, nameLength + 1, "%d.%d", (midiOutCaps.vDriverVersion & 0xFF00) >> 8, midiOutCaps.vDriverVersion & 0xFF); return MIDI_SUCCESS; } MIDIOUT_CHECK_ERROR; diff --git a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_Ports.c b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_Ports.c index c0c26b28f718b..55dcb6e28827a 100644 --- a/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_Ports.c +++ b/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_Ports.c @@ -113,8 +113,9 @@ char* getLineFlags(DWORD flags) { } if (flags!=0) { UINT_PTR r = (UINT_PTR) ret; - r += strlen(ret); - sprintf((char*) r, "%d", flags); + size_t usedLen = strlen(ret); + r += usedLen; + snprintf((char*) r, sizeof(ret) - usedLen, "%d", flags); } return ret; } @@ -219,8 +220,9 @@ char* getControlState(DWORD controlState) { } if (controlState!=0) { UINT_PTR r = (UINT_PTR) ret; - r += strlen(ret); - sprintf((char*) r, "%d", controlState); + size_t usedLen = strlen(ret); + r += usedLen; + snprintf((char*) r, sizeof(ret) - usedLen, "%d", controlState); } return ret; } @@ -359,7 +361,7 @@ INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* descr MIXERCAPSW mixerCaps; if (mixerGetDevCapsW(mixerIndex, &mixerCaps, sizeof(MIXERCAPSW)) == MMSYSERR_NOERROR) { UnicodeToUTF8AndCopy(description->name, mixerCaps.szPname, PORT_STRING_LENGTH); - sprintf(description->version, "%d.%d", (mixerCaps.vDriverVersion & 0xFF00) >> 8, mixerCaps.vDriverVersion & 0xFF); + snprintf(description->version, sizeof(description->version), "%d.%d", (mixerCaps.vDriverVersion & 0xFF00) >> 8, mixerCaps.vDriverVersion & 0xFF); strncpy(description->description, "Port Mixer", PORT_STRING_LENGTH-1); return TRUE; } diff --git a/src/java.management/share/native/libmanagement/VMManagementImpl.c b/src/java.management/share/native/libmanagement/VMManagementImpl.c index 38a5b6af0cc0c..46f3b03b18ee6 100644 --- a/src/java.management/share/native/libmanagement/VMManagementImpl.c +++ b/src/java.management/share/native/libmanagement/VMManagementImpl.c @@ -44,7 +44,7 @@ Java_sun_management_VMManagementImpl_getVersion0 // for internal use unsigned int micro = (unsigned int) jmm_version & 0xFF; - sprintf(buf, "%d.%d", major, minor); + snprintf(buf, sizeof(buf), "%d.%d", major, minor); version_string = (*env)->NewStringUTF(env, buf); return version_string; } diff --git a/src/java.management/share/native/libmanagement/management.c b/src/java.management/share/native/libmanagement/management.c index 281e3d0ffac32..4553a02c08686 100644 --- a/src/java.management/share/native/libmanagement/management.c +++ b/src/java.management/share/native/libmanagement/management.c @@ -57,6 +57,6 @@ JNIEXPORT jint JNICALL void throw_internal_error(JNIEnv* env, const char* msg) { char errmsg[128]; - sprintf(errmsg, "errno: %d error: %s\n", errno, msg); + snprintf(errmsg, sizeof(errmsg), "errno: %d error: %s\n", errno, msg); JNU_ThrowInternalError(env, errmsg); } diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/Obj.java b/src/java.naming/share/classes/com/sun/jndi/ldap/Obj.java index 5c4b9ab0f6ca4..dc69b35585044 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/Obj.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/Obj.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -239,6 +239,10 @@ static Object decodeObject(Attributes attrs) ClassLoader cl = helper.getURLClassLoader(codebases); return deserializeObject((byte[])attr.get(), cl); } else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) { + // javaRemoteLocation attribute (RMI stub will be created) + if (!VersionHelper.isSerialDataAllowed()) { + throw new NamingException("Object deserialization is not allowed"); + } // For backward compatibility only return decodeRmiObject( (String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(), diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/VersionHelper.java b/src/java.naming/share/classes/com/sun/jndi/ldap/VersionHelper.java index 4d7ce28a841ac..7d11ead19643c 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/VersionHelper.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/VersionHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,8 +44,8 @@ public final class VersionHelper { private static final boolean trustURLCodebase; /** - * Determines whether objects may be deserialized from the content of - * 'javaSerializedData' attribute. + * Determines whether objects may be deserialized or reconstructed from a content of + * 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' LDAP attributes. */ private static final boolean trustSerialData; @@ -56,10 +56,10 @@ public final class VersionHelper { "com.sun.jndi.ldap.object.trustURLCodebase", "false"); trustURLCodebase = "true".equalsIgnoreCase(trust); - // System property to control whether classes is allowed to be loaded from - // 'javaSerializedData' attribute + // System property to control whether classes are allowed to be loaded from + // 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' attributes. String trustSerialDataSp = getPrivilegedProperty( - "com.sun.jndi.ldap.object.trustSerialData", "true"); + "com.sun.jndi.ldap.object.trustSerialData", "false"); trustSerialData = "true".equalsIgnoreCase(trustSerialDataSp); } @@ -81,8 +81,9 @@ static VersionHelper getVersionHelper() { } /** - * Returns true if deserialization of objects from 'javaSerializedData' - * and 'javaReferenceAddress' LDAP attributes is allowed. + * Returns true if deserialization or reconstruction of objects from + * 'javaSerializedData', 'javaRemoteLocation' and 'javaReferenceAddress' + * LDAP attributes is allowed. * * @return true if deserialization is allowed; false - otherwise */ diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/sasl/SaslInputStream.java b/src/java.naming/share/classes/com/sun/jndi/ldap/sasl/SaslInputStream.java index f0746c103bc25..534f7dac766e3 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/sasl/SaslInputStream.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/sasl/SaslInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,7 +78,7 @@ public int read() throws IOException { byte[] inBuf = new byte[1]; int count = read(inBuf, 0, 1); if (count > 0) { - return inBuf[0]; + return inBuf[0] & 0xff; } else { return -1; } diff --git a/src/java.naming/share/classes/module-info.java b/src/java.naming/share/classes/module-info.java index 09e1093c13a35..b354dad89d5d5 100644 --- a/src/java.naming/share/classes/module-info.java +++ b/src/java.naming/share/classes/module-info.java @@ -91,11 +91,16 @@ *
      *
    • {@systemProperty com.sun.jndi.ldap.object.trustSerialData}: *
      The value of this system property is the string representation of a boolean value - * which allows to control the deserialization of java objects from the 'javaSerializedData' - * LDAP attribute. To prevent the deserialization of java objects from the 'javaSerializedData' - * attribute, the system property value can be set to 'false'. - *
      If the property is not specified then the deserialization of java objects - * from the 'javaSerializedData' attribute is allowed. + * that controls the deserialization of java objects from the {@code javaSerializedData} LDAP + * attribute, reconstruction of RMI references from the {@code javaRemoteLocation} LDAP attribute, and + * reconstruction of {@linkplain javax.naming.BinaryRefAddr binary reference addresses} from + * the {@code javaReferenceAddress} LDAP attribute. + * To allow the deserialization or reconstruction of java objects from {@code javaSerializedData}, + * {@code javaRemoteLocation} or {@code javaReferenceAddress} attributes, the system property value + * can be set to {@code true} (case insensitive). + *
      If the property is not specified the deserialization of java objects + * from the {@code javaSerializedData}, the {@code javaRemoteLocation}, or {@code javaReferenceAddress} + * attributes is not allowed. *
    • *
    • {@systemProperty jdk.jndi.object.factoriesFilter}: *
      The value of this system property defines a filter used by diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java index 36af300503283..60d2b2b410acc 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ import java.io.IOException; import java.lang.System.Logger.Level; import java.net.InetSocketAddress; +import java.net.ProtocolException; import java.net.ProxySelector; import java.net.URI; import java.net.URISyntaxException; @@ -39,6 +40,7 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.net.http.HttpClient; import java.net.http.HttpHeaders; @@ -67,6 +69,8 @@ */ final class Exchange { + static final int MAX_NON_FINAL_RESPONSES = + Utils.getIntegerNetProperty("jdk.httpclient.maxNonFinalResponses", 8); final Logger debug = Utils.getDebugLogger(this::dbgString, Utils.DEBUG); final HttpRequestImpl request; @@ -91,6 +95,8 @@ final class Exchange { // exchange so that it can be aborted/timed out mid setup. final ConnectionAborter connectionAborter = new ConnectionAborter(); + final AtomicInteger nonFinalResponses = new AtomicInteger(); + Exchange(HttpRequestImpl request, MultiExchange multi) { this.request = request; this.upgrading = false; @@ -314,7 +320,7 @@ CompletableFuture checkCancelled(CompletableFuture cf, HttpConnection public void h2Upgrade() { upgrading = true; - request.setH2Upgrade(client.client2()); + request.setH2Upgrade(this); } synchronized IOException getCancelCause() { @@ -415,6 +421,7 @@ private CompletableFuture expectContinue(ExchangeImpl ex) { Log.logResponse(r1::toString); int rcode = r1.statusCode(); if (rcode == 100) { + nonFinalResponses.incrementAndGet(); Log.logTrace("Received 100-Continue: sending body"); if (debug.on()) debug.log("Received 100-Continue for %s", r1); CompletableFuture cf = @@ -447,10 +454,70 @@ private CompletableFuture sendRequestBody(ExchangeImpl ex) { CompletableFuture cf = ex.sendBodyAsync() .thenCompose(exIm -> exIm.getResponseAsync(parentExecutor)); cf = wrapForUpgrade(cf); + // after 101 is handled we check for other 1xx responses + cf = cf.thenCompose(this::ignore1xxResponse); cf = wrapForLog(cf); return cf; } + /** + * Checks whether the passed Response has a status code between 102 and 199 (both inclusive). + * If so, then that {@code Response} is considered intermediate informational response and is + * ignored by the client. This method then creates a new {@link CompletableFuture} which + * completes when a subsequent response is sent by the server. Such newly constructed + * {@link CompletableFuture} will not complete till a "final" response (one which doesn't have + * a response code between 102 and 199 inclusive) is sent by the server. The returned + * {@link CompletableFuture} is thus capable of handling multiple subsequent intermediate + * informational responses from the server. + *

      + * If the passed Response doesn't have a status code between 102 and 199 (both inclusive) then + * this method immediately returns back a completed {@link CompletableFuture} with the passed + * {@code Response}. + *

      + * + * @param rsp The response + * @return A {@code CompletableFuture} with the final response from the server + */ + private CompletableFuture ignore1xxResponse(final Response rsp) { + final int statusCode = rsp.statusCode(); + // we ignore any response code which is 1xx. + // For 100 (with the request configured to expect-continue) and 101, we handle it + // specifically as defined in the RFC-9110, outside of this method. + // As noted in RFC-9110, section 15.2.1, if response code is 100 and if the request wasn't + // configured with expectContinue, then we ignore the 100 response and wait for the final + // response (just like any other 1xx response). + // Any other response code between 102 and 199 (both inclusive) aren't specified in the + // "HTTP semantics" RFC-9110. The spec states that these 1xx response codes are informational + // and interim and the client can choose to ignore them and continue to wait for the + // final response (headers) + if ((statusCode >= 102 && statusCode <= 199) + || (statusCode == 100 && !request.expectContinue)) { + Log.logTrace("Ignoring (1xx informational) response code {0}", rsp.statusCode()); + if (debug.on()) { + debug.log("Ignoring (1xx informational) response code " + + rsp.statusCode()); + } + assert exchImpl != null : "Illegal state - current exchange isn't set"; + int count = nonFinalResponses.incrementAndGet(); + if (MAX_NON_FINAL_RESPONSES > 0 && (count < 0 || count > MAX_NON_FINAL_RESPONSES)) { + return MinimalFuture.failedFuture( + new ProtocolException(String.format( + "Too many interim responses received: %s > %s", + count, MAX_NON_FINAL_RESPONSES))); + } else { + // ignore this Response and wait again for the subsequent response headers + final CompletableFuture cf = exchImpl.getResponseAsync(parentExecutor); + // we recompose the CF again into the ignore1xxResponse check/function because + // the 1xx response is allowed to be sent multiple times for a request, before + // a final response arrives + return cf.thenCompose(this::ignore1xxResponse); + } + } else { + // return the already completed future + return MinimalFuture.completedFuture(rsp); + } + } + CompletableFuture responseAsyncImpl0(HttpConnection connection) { Function, CompletableFuture> after407Check; bodyIgnored = null; @@ -481,7 +548,30 @@ private CompletableFuture wrapForUpgrade(CompletableFuture c if (upgrading) { return cf.thenCompose(r -> checkForUpgradeAsync(r, exchImpl)); } - return cf; + // websocket requests use "Connection: Upgrade" and "Upgrade: websocket" headers. + // however, the "upgrading" flag we maintain in this class only tracks a h2 upgrade + // that we internally triggered. So it will be false in the case of websocket upgrade, hence + // this additional check. If it's a websocket request we allow 101 responses and we don't + // require any additional checks when a response arrives. + if (request.isWebSocket()) { + return cf; + } + // not expecting an upgrade, but if the server sends a 101 response then we fail the + // request and also let the ExchangeImpl deal with it as a protocol error + return cf.thenCompose(r -> { + if (r.statusCode == 101) { + final ProtocolException protoEx = new ProtocolException("Unexpected 101 " + + "response, when not upgrading"); + assert exchImpl != null : "Illegal state - current exchange isn't set"; + try { + exchImpl.onProtocolError(protoEx); + } catch (Throwable ignore){ + // ignored + } + return MinimalFuture.failedFuture(protoEx); + } + return MinimalFuture.completedFuture(r); + }); } private CompletableFuture wrapForLog(CompletableFuture cf) { @@ -526,7 +616,7 @@ HttpResponse.BodySubscriber ignoreBody(HttpResponse.ResponseInfo hdrs) { .thenCompose((Http2Connection c) -> { boolean cached = c.offerConnection(); if (cached) connectionAborter.disable(); - Stream s = c.getStream(1); + Stream s = c.getInitialStream(); if (s == null) { // s can be null if an exception occurred @@ -683,6 +773,14 @@ HttpClient.Version version() { return multi.version(); } + boolean pushEnabled() { + return pushGroup != null; + } + + String h2cSettingsStrings() { + return client.client2().getSettingsString(pushEnabled()); + } + String dbgString() { return dbgTag; } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java index 07ba2f0ee60df..9f2a4008d1800 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java @@ -199,6 +199,16 @@ abstract CompletableFuture readBodyAsync(HttpResponse.BodyHandler handler, */ abstract void cancel(IOException cause); + /** + * Invoked whenever there is a (HTTP) protocol error when dealing with the response + * from the server. The implementations of {@code ExchangeImpl} are then expected to + * take necessary action that is expected by the corresponding specifications whenever + * a protocol error happens. For example, in HTTP/1.1, such protocol error would result + * in the connection being closed. + * @param cause The cause of the protocol violation + */ + abstract void onProtocolError(IOException cause); + /** * Called when the exchange is released, so that cleanup actions may be * performed - such as deregistering callbacks. diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http1Exchange.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http1Exchange.java index 6edfe99f13aec..efe0ccfb79dff 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http1Exchange.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http1Exchange.java @@ -427,6 +427,15 @@ void cancel(IOException cause) { cancelImpl(cause); } + @Override + void onProtocolError(final IOException cause) { + if (debug.on()) { + debug.log("cancelling exchange due to protocol error: %s", cause.getMessage()); + } + Log.logError("cancelling exchange due to protocol error: {0}\n", cause); + cancelImpl(cause); + } + private void cancelImpl(Throwable cause) { LinkedList> toComplete = null; int count = 0; diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java index 669c173e3f825..8c796193015d3 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java @@ -25,6 +25,7 @@ package jdk.internal.net.http; +import java.io.IOException; import java.net.ProtocolException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; @@ -53,6 +54,12 @@ class Http1HeaderParser { private int responseCode; private HttpHeaders headers; private Map> privateMap = new HashMap<>(); + private long size; + + private static final int K = 1024; + private static final int MAX_HTTP_HEADER_SIZE = Utils.getIntegerNetProperty( + "jdk.http.maxHeaderSize", + Integer.MIN_VALUE, Integer.MAX_VALUE, 384 * K, true); enum State { INITIAL, STATUS_LINE, @@ -164,11 +171,16 @@ private char get(ByteBuffer input) { return (char)(input.get() & 0xFF); } - private void readResumeStatusLine(ByteBuffer input) { + private void readResumeStatusLine(ByteBuffer input) throws ProtocolException { + final long max = MAX_HTTP_HEADER_SIZE - size - 32 - sb.length(); + int count = 0; char c = 0; while (input.hasRemaining() && (c = get(input)) != CR) { if (c == LF) break; sb.append(c); + if (++count > max) { + checkMaxHeaderSize(sb.length()); + } } if (c == CR) { state = State.STATUS_LINE_FOUND_CR; @@ -185,6 +197,7 @@ private void readStatusLineFeed(ByteBuffer input) throws ProtocolException { } statusLine = sb.toString(); + size = size + 32 + statusLine.length(); sb = new StringBuilder(); if (!statusLine.startsWith("HTTP/1.")) { throw protocolException("Invalid status line: \"%s\"", statusLine); @@ -205,7 +218,23 @@ private void readStatusLineFeed(ByteBuffer input) throws ProtocolException { state = State.STATUS_LINE_END; } - private void maybeStartHeaders(ByteBuffer input) { + private void checkMaxHeaderSize(int sz) throws ProtocolException { + long s = size + sz + 32; + if (MAX_HTTP_HEADER_SIZE > 0 && s > MAX_HTTP_HEADER_SIZE) { + throw new ProtocolException(String.format("Header size too big: %s > %s", + s, MAX_HTTP_HEADER_SIZE)); + } + } + static private long newSize(long size, int name, int value) throws ProtocolException { + long newSize = size + name + value + 32; + if (MAX_HTTP_HEADER_SIZE > 0 && newSize > MAX_HTTP_HEADER_SIZE) { + throw new ProtocolException(String.format("Header size too big: %s > %s", + newSize, MAX_HTTP_HEADER_SIZE)); + } + return newSize; + } + + private void maybeStartHeaders(ByteBuffer input) throws ProtocolException { assert state == State.STATUS_LINE_END; assert sb.length() == 0; char c = get(input); @@ -215,6 +244,7 @@ private void maybeStartHeaders(ByteBuffer input) { state = State.STATUS_LINE_END_LF; } else { sb.append(c); + checkMaxHeaderSize(sb.length()); state = State.HEADER; } } @@ -232,9 +262,11 @@ private void maybeEndHeaders(ByteBuffer input) throws ProtocolException { } } - private void readResumeHeader(ByteBuffer input) { + private void readResumeHeader(ByteBuffer input) throws ProtocolException { assert state == State.HEADER; assert input.hasRemaining(); + final long max = MAX_HTTP_HEADER_SIZE - size - 32 - sb.length(); + int count = 0; while (input.hasRemaining()) { char c = get(input); if (c == CR) { @@ -248,6 +280,9 @@ private void readResumeHeader(ByteBuffer input) { if (c == HT) c = SP; sb.append(c); + if (++count > max) { + checkMaxHeaderSize(sb.length()); + } } } @@ -268,12 +303,12 @@ private void addHeaderFromString(String headerString) throws ProtocolException { if (!Utils.isValidValue(value)) { throw protocolException("Invalid header value \"%s: %s\"", name, value); } - + size = newSize(size, name.length(), value.length()); privateMap.computeIfAbsent(name.toLowerCase(Locale.US), k -> new ArrayList<>()).add(value); } - private void resumeOrLF(ByteBuffer input) { + private void resumeOrLF(ByteBuffer input) throws ProtocolException { assert state == State.HEADER_FOUND_CR || state == State.HEADER_FOUND_LF; char c = state == State.HEADER_FOUND_LF ? LF : get(input); if (c == LF) { @@ -283,10 +318,12 @@ private void resumeOrLF(ByteBuffer input) { state = State.HEADER_FOUND_CR_LF; } else if (c == SP || c == HT) { sb.append(SP); // parity with MessageHeaders + checkMaxHeaderSize(sb.length()); state = State.HEADER; } else { sb = new StringBuilder(); sb.append(c); + checkMaxHeaderSize(1); state = State.HEADER; } } @@ -312,6 +349,7 @@ private void resumeOrSecondCR(ByteBuffer input) throws ProtocolException { } else if (c == SP || c == HT) { assert sb.length() != 0; sb.append(SP); // continuation line + checkMaxHeaderSize(sb.length()); state = State.HEADER; } else { if (sb.length() > 0) { @@ -322,6 +360,7 @@ private void resumeOrSecondCR(ByteBuffer input) throws ProtocolException { addHeaderFromString(headerString); } sb.append(c); + checkMaxHeaderSize(sb.length()); state = State.HEADER; } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java index 9f74a70d31894..0f2db7738fccf 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CompletableFuture; -import jdk.internal.net.http.common.Log; import jdk.internal.net.http.common.Logger; import jdk.internal.net.http.common.MinimalFuture; import jdk.internal.net.http.common.Utils; @@ -48,6 +47,7 @@ import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE; import static jdk.internal.net.http.frame.SettingsFrame.MAX_CONCURRENT_STREAMS; import static jdk.internal.net.http.frame.SettingsFrame.MAX_FRAME_SIZE; +import static jdk.internal.net.http.frame.SettingsFrame.MAX_HEADER_LIST_SIZE; /** * Http2 specific aspects of HttpClientImpl @@ -94,14 +94,19 @@ class Http2ClientImpl { CompletableFuture getConnectionFor(HttpRequestImpl req, Exchange exchange) { String key = Http2Connection.keyFor(req); + boolean pushEnabled = exchange.pushEnabled(); synchronized (this) { Http2Connection connection = connections.get(key); if (connection != null) { try { - if (connection.closed || !connection.reserveStream(true)) { + if (connection.closed + || !connection.reserveStream(true, pushEnabled)) { if (debug.on()) - debug.log("removing found closed or closing connection: %s", connection); + debug.log("removing connection from pool since " + + "it couldn't be reserved for use%s: %s", + pushEnabled ? " with server push enabled" : + "", connection); deleteConnection(connection); } else { // fast path if connection already exists @@ -128,7 +133,7 @@ CompletableFuture getConnectionFor(HttpRequestImpl req, synchronized (Http2ClientImpl.this) { if (conn != null) { try { - conn.reserveStream(true); + conn.reserveStream(true, exchange.pushEnabled()); } catch (IOException e) { throw new UncheckedIOException(e); // shouldn't happen } @@ -161,10 +166,21 @@ boolean offerConnection(Http2Connection c) { synchronized(this) { Http2Connection c1 = connections.putIfAbsent(key, c); if (c1 != null) { - c.setFinalStream(); - if (debug.on()) - debug.log("existing entry in connection pool for %s", key); - return false; + if (c.serverPushEnabled() && !c1.serverPushEnabled()) { + c1.setFinalStream(); + connections.remove(key, c1); + connections.put(key, c); + if (debug.on()) { + debug.log("Replacing %s with %s in connection pool", c1, c); + } + if (c1.shouldClose()) c1.close(); + return true; + } else { + c.setFinalStream(); + if (debug.on()) + debug.log("existing entry in connection pool for %s", key); + return false; + } } if (debug.on()) debug.log("put in the connection pool: %s", c); @@ -204,8 +220,8 @@ HttpClientImpl client() { } /** Returns the client settings as a base64 (url) encoded string */ - String getSettingsString() { - SettingsFrame sf = getClientSettings(); + String getSettingsString(boolean defaultServerPush) { + SettingsFrame sf = getClientSettings(defaultServerPush); byte[] settings = sf.toByteArray(); // without the header Base64.Encoder encoder = Base64.getUrlEncoder() .withoutPadding(); @@ -215,14 +231,7 @@ String getSettingsString() { private static final int K = 1024; private static int getParameter(String property, int min, int max, int defaultValue) { - int value = Utils.getIntegerNetProperty(property, defaultValue); - // use default value if misconfigured - if (value < min || value > max) { - Log.logError("Property value for {0}={1} not in [{2}..{3}]: " + - "using default={4}", property, value, min, max, defaultValue); - value = defaultValue; - } - return value; + return Utils.getIntegerNetProperty(property, min, max, defaultValue, true); } // used for the connection window, to have a connection window size @@ -243,7 +252,18 @@ int getConnectionWindowSize(SettingsFrame clientSettings) { streamWindow, Integer.MAX_VALUE, defaultValue); } - SettingsFrame getClientSettings() { + /** + * This method is used to test whether pushes are globally + * disabled on all connections. + * @return true if pushes are globally disabled on all connections + */ + boolean serverPushDisabled() { + return getParameter( + "jdk.httpclient.enablepush", + 0, 1, 1) == 0; + } + + SettingsFrame getClientSettings(boolean defaultServerPush) { SettingsFrame frame = new SettingsFrame(); // default defined for HTTP/2 is 4 K, we use 16 K. frame.setParameter(HEADER_TABLE_SIZE, getParameter( @@ -252,14 +272,15 @@ SettingsFrame getClientSettings() { // O: does not accept push streams. 1: accepts push streams. frame.setParameter(ENABLE_PUSH, getParameter( "jdk.httpclient.enablepush", - 0, 1, 1)); + 0, 1, defaultServerPush ? 1 : 0)); // HTTP/2 recommends to set the number of concurrent streams - // no lower than 100. We use 100. 0 means no stream would be - // accepted. That would render the client to be non functional, - // so we won't let 0 be configured for our Http2ClientImpl. + // no lower than 100. We use 100, unless push promises are + // disabled. + int initialServerStreams = frame.getParameter(ENABLE_PUSH) == 0 + ? 0 : 100; frame.setParameter(MAX_CONCURRENT_STREAMS, getParameter( "jdk.httpclient.maxstreams", - 1, Integer.MAX_VALUE, 100)); + 0, Integer.MAX_VALUE, initialServerStreams)); // Maximum size is 2^31-1. Don't allow window size to be less // than the minimum frame size as this is likely to be a // configuration error. HTTP/2 specify a default of 64 * K -1, @@ -272,6 +293,14 @@ SettingsFrame getClientSettings() { frame.setParameter(MAX_FRAME_SIZE, getParameter( "jdk.httpclient.maxframesize", 16 * K, 16 * K * K -1, 16 * K)); + // Maximum field section size we're prepared to accept + // This is the uncompressed name + value size + 32 per field line + int maxHeaderSize = getParameter( + "jdk.http.maxHeaderSize", + Integer.MIN_VALUE, Integer.MAX_VALUE, 384 * K); + // If the property is <= 0 the value is unlimited + if (maxHeaderSize <= 0) maxHeaderSize = -1; + frame.setParameter(MAX_HEADER_LIST_SIZE, maxHeaderSize); return frame; } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java index 1aa19eeb16d7f..f363231e1c82f 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.net.InetSocketAddress; +import java.net.ProtocolException; import java.net.URI; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -44,6 +45,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Supplier; import javax.net.ssl.SSLEngine; @@ -53,12 +56,14 @@ import jdk.internal.net.http.HttpConnection.HttpPublisher; import jdk.internal.net.http.common.FlowTube; import jdk.internal.net.http.common.FlowTube.TubeSubscriber; +import jdk.internal.net.http.common.HeaderDecoder; import jdk.internal.net.http.common.HttpHeadersBuilder; import jdk.internal.net.http.common.Log; import jdk.internal.net.http.common.Logger; import jdk.internal.net.http.common.MinimalFuture; import jdk.internal.net.http.common.SequentialScheduler; import jdk.internal.net.http.common.Utils; +import jdk.internal.net.http.common.ValidatingHeadersConsumer; import jdk.internal.net.http.frame.ContinuationFrame; import jdk.internal.net.http.frame.DataFrame; import jdk.internal.net.http.frame.ErrorFrame; @@ -243,6 +248,45 @@ void markPrefaceSent() { } } + private final class PushPromiseDecoder extends HeaderDecoder implements DecodingCallback { + + final int parentStreamId; + final int pushPromiseStreamId; + final Stream parent; + final AtomicReference errorRef = new AtomicReference<>(); + + PushPromiseDecoder(int parentStreamId, int pushPromiseStreamId, Stream parent) { + this.parentStreamId = parentStreamId; + this.pushPromiseStreamId = pushPromiseStreamId; + this.parent = parent; + } + + @Override + protected void addHeader(String name, String value) { + if (errorRef.get() == null) { + super.addHeader(name, value); + } + } + + @Override + public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws ProtocolException { + try { + DecodingCallback.super.onMaxHeaderListSizeReached(size, maxHeaderListSize); + } catch (ProtocolException pe) { + if (parent != null) { + if (errorRef.compareAndSet(null, pe)) { + // cancel the parent stream + resetStream(pushPromiseStreamId, ResetFrame.REFUSED_STREAM); + parent.onProtocolError(pe); + } + } else { + // interrupt decoding and closes the connection + throw pe; + } + } + } + } + volatile boolean closed; //------------------------------------- @@ -263,6 +307,8 @@ void markPrefaceSent() { private final Decoder hpackIn; final SettingsFrame clientSettings; private volatile SettingsFrame serverSettings; + private record PushContinuationState(PushPromiseDecoder pushContDecoder, PushPromiseFrame pushContFrame) {} + private volatile PushContinuationState pushContinuationState; private final String key; // for HttpClientImpl.connections map private final FramesDecoder framesDecoder; private final FramesEncoder framesEncoder = new FramesEncoder(); @@ -275,11 +321,24 @@ void markPrefaceSent() { private final FramesController framesController = new FramesController(); private final Http2TubeSubscriber subscriber; final ConnectionWindowUpdateSender windowUpdater; - private volatile Throwable cause; + private final AtomicReference cause = new AtomicReference<>(); private volatile Supplier initial; + private volatile Stream initialStream; + + private ValidatingHeadersConsumer orphanedConsumer; + private final AtomicInteger orphanedHeaders = new AtomicInteger(); static final int DEFAULT_FRAME_SIZE = 16 * 1024; + static final int MAX_LITERAL_WITH_INDEXING = + Utils.getIntegerNetProperty("jdk.httpclient.maxLiteralWithIndexing",512); + // The maximum number of HEADER frames, CONTINUATION frames, or PUSH_PROMISE frames + // referring to an already closed or non-existent stream that a client will accept to + // process. Receiving frames referring to non-existent or closed streams doesn't necessarily + // constitute an HTTP/2 protocol error, but receiving too many may indicate a problem + // with the connection. If this limit is reached, a {@link java.net.ProtocolException + // ProtocolException} will be raised and the connection will be closed. + static final int MAX_ORPHANED_HEADERS = 1024; // TODO: need list of control frames from other threads // that need to be sent @@ -287,19 +346,21 @@ void markPrefaceSent() { private Http2Connection(HttpConnection connection, Http2ClientImpl client2, int nextstreamid, - String key) { + String key, + boolean defaultServerPush) { this.connection = connection; this.client2 = client2; this.subscriber = new Http2TubeSubscriber(client2.client()); this.nextstreamid = nextstreamid; this.key = key; - this.clientSettings = this.client2.getClientSettings(); + this.clientSettings = this.client2.getClientSettings(defaultServerPush); this.framesDecoder = new FramesDecoder(this::processFrame, clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE)); // serverSettings will be updated by server this.serverSettings = SettingsFrame.defaultRFCSettings(); this.hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE)); - this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE)); + this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE), + clientSettings.getParameter(MAX_HEADER_LIST_SIZE), MAX_LITERAL_WITH_INDEXING); if (debugHpack.on()) { debugHpack.log("For the record:" + super.toString()); debugHpack.log("Decoder created: %s", hpackIn); @@ -318,18 +379,21 @@ private Http2Connection(HttpConnection connection, private Http2Connection(HttpConnection connection, Http2ClientImpl client2, Exchange exchange, - Supplier initial) + Supplier initial, + boolean defaultServerPush) throws IOException, InterruptedException { this(connection, client2, 3, // stream 1 is registered during the upgrade - keyFor(connection)); - reserveStream(true); + keyFor(connection), + defaultServerPush); + reserveStream(true, clientSettings.getFlag(ENABLE_PUSH)); Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize()); Stream initialStream = createStream(exchange); boolean opened = initialStream.registerStream(1, true); + this.initialStream = initialStream; if (debug.on() && !opened) { debug.log("Initial stream was cancelled - but connection is maintained: " + "reset frame will need to be sent later"); @@ -344,7 +408,7 @@ private Http2Connection(HttpConnection connection, sendConnectionPreface(); if (!opened) { debug.log("ensure reset frame is sent to cancel initial stream"); - initialStream.sendCancelStreamFrame(); + initialStream.sendResetStreamFrame(ResetFrame.CANCEL); } } @@ -357,7 +421,8 @@ static CompletableFuture createAsync(HttpConnection connection, Exchange exchange, Supplier initial) { - return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial)); + return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial, + exchange.pushEnabled())); } // Requires TLS handshake. So, is really async @@ -381,7 +446,8 @@ static CompletableFuture createAsync(HttpRequestImpl request, .thenCompose(notused-> { CompletableFuture cf = new MinimalFuture<>(); try { - Http2Connection hc = new Http2Connection(request, h2client, connection); + Http2Connection hc = new Http2Connection(request, h2client, + connection, exchange.pushEnabled()); cf.complete(hc); } catch (IOException e) { cf.completeExceptionally(e); @@ -396,13 +462,15 @@ static CompletableFuture createAsync(HttpRequestImpl request, */ private Http2Connection(HttpRequestImpl request, Http2ClientImpl h2client, - HttpConnection connection) + HttpConnection connection, + boolean defaultServerPush) throws IOException { this(connection, h2client, 1, - keyFor(request)); + keyFor(request), + defaultServerPush); Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize()); @@ -425,15 +493,21 @@ final HttpClientImpl client() { // if false returned then a new Http2Connection is required // if true, the stream may be assigned to this connection // for server push, if false returned, then the stream should be cancelled - synchronized boolean reserveStream(boolean clientInitiated) throws IOException { + synchronized boolean reserveStream(boolean clientInitiated, boolean pushEnabled) throws IOException { if (finalStream) { return false; } - if (clientInitiated && (lastReservedClientStreamid + 2) >= MAX_CLIENT_STREAM_ID) { + // If requesting to reserve a stream for an exchange for which push is enabled, + // we will reserve the stream in this connection only if this connection is also + // push enabled, unless pushes are globally disabled. + boolean pushCompatible = !clientInitiated || !pushEnabled + || this.serverPushEnabled() + || client2.serverPushDisabled(); + if (clientInitiated && (lastReservedClientStreamid >= MAX_CLIENT_STREAM_ID -2 || !pushCompatible)) { setFinalStream(); client2.deleteConnection(this); return false; - } else if (!clientInitiated && (lastReservedServerStreamid + 2) >= MAX_SERVER_STREAM_ID) { + } else if (!clientInitiated && (lastReservedServerStreamid >= MAX_SERVER_STREAM_ID - 2)) { setFinalStream(); client2.deleteConnection(this); return false; @@ -458,6 +532,10 @@ synchronized boolean reserveStream(boolean clientInitiated) throws IOException { return true; } + synchronized boolean shouldClose() { + return finalStream() && streams.isEmpty(); + } + /** * Throws an IOException if h2 was not negotiated */ @@ -585,6 +663,10 @@ String key() { return this.key; } + public boolean serverPushEnabled() { + return clientSettings.getParameter(SettingsFrame.ENABLE_PUSH) == 1; + } + boolean offerConnection() { return client2.offerConnection(this); } @@ -683,7 +765,7 @@ final void asyncReceive(ByteBuffer buffer) { } Throwable getRecordedCause() { - return cause; + return cause.get(); } void shutdown(Throwable t) { @@ -693,6 +775,7 @@ void shutdown(Throwable t) { if (closed == true) return; closed = true; } + cause.compareAndSet(null, t); if (Log.errors()) { if (!(t instanceof EOFException) || isActive()) { Log.logError(t); @@ -700,9 +783,8 @@ void shutdown(Throwable t) { Log.logError("Shutting down connection: {0}", t.getMessage()); } } - Throwable initialCause = this.cause; - if (initialCause == null) this.cause = t; client2.deleteConnection(this); + subscriber.stop(cause.get()); for (Stream s : streams.values()) { try { s.connectionClosing(t); @@ -756,25 +838,47 @@ void processFrame(Http2Frame frame) throws IOException { return; } + if (frame instanceof PushPromiseFrame && !serverPushEnabled()) { + String protocolError = "received a PUSH_PROMISE when SETTINGS_ENABLE_PUSH is 0"; + protocolError(ResetFrame.PROTOCOL_ERROR, protocolError); + return; + } + Stream stream = getStream(streamid); - if (stream == null) { + var nextstreamid = this.nextstreamid; + if (stream == null && (streamid & 0x01) == 0x01 && streamid >= nextstreamid) { + String protocolError = String.format( + "received a frame for a non existing streamid(%s) >= nextstreamid(%s)", + streamid, nextstreamid); + protocolError(ResetFrame.PROTOCOL_ERROR, protocolError); + return; + } + if (stream == null && pushContinuationState == null) { // Should never receive a frame with unknown stream id - if (frame instanceof HeaderFrame) { + if (frame instanceof HeaderFrame hf) { + String protocolError = checkMaxOrphanedHeadersExceeded(hf); + if (protocolError != null) { + protocolError(ResetFrame.PROTOCOL_ERROR, protocolError); + return; + } // always decode the headers as they may affect // connection-level HPACK decoding state - DecodingCallback decoder = new ValidatingHeadersConsumer(); + if (orphanedConsumer == null || frame.getClass() != ContinuationFrame.class) { + orphanedConsumer = new ValidatingHeadersConsumer(); + } + DecodingCallback decoder = orphanedConsumer::onDecoded; try { - decodeHeaders((HeaderFrame) frame, decoder); - } catch (UncheckedIOException e) { + decodeHeaders(hf, decoder); + } catch (IOException | UncheckedIOException e) { protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage()); return; } } if (!(frame instanceof ResetFrame)) { - if (frame instanceof DataFrame) { - dropDataFrame((DataFrame)frame); + if (frame instanceof DataFrame df) { + dropDataFrame(df); } if (isServerInitiatedStream(streamid)) { if (streamid < nextPushStream) { @@ -791,30 +895,74 @@ void processFrame(Http2Frame frame) throws IOException { } return; } - if (frame instanceof PushPromiseFrame) { - PushPromiseFrame pp = (PushPromiseFrame)frame; - try { - handlePushPromise(stream, pp); - } catch (UncheckedIOException e) { - protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage()); - return; - } - } else if (frame instanceof HeaderFrame) { - // decode headers (or continuation) - try { - decodeHeaders((HeaderFrame) frame, stream.rspHeadersConsumer()); - } catch (UncheckedIOException e) { - debug.log("Error decoding headers: " + e.getMessage(), e); - protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage()); + + // While push frame is not null, the only acceptable frame on this + // stream is a Continuation frame + PushContinuationState pcs = pushContinuationState; + if (pcs != null) { + if (frame instanceof ContinuationFrame cf) { + if (stream == null) { + String protocolError = checkMaxOrphanedHeadersExceeded(cf); + if (protocolError != null) { + protocolError(ResetFrame.PROTOCOL_ERROR, protocolError); + return; + } + } + try { + if (streamid == pcs.pushContFrame.streamid()) + handlePushContinuation(pcs, stream, cf); + else { + String protocolError = "Received a CONTINUATION with " + + "unexpected stream id: " + streamid + " != " + + pcs.pushContFrame.streamid(); + protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError); + } + } catch (IOException | UncheckedIOException e) { + debug.log("Error handling Push Promise with Continuation: " + e.getMessage(), e); + protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage()); + return; + } + } else { + pushContinuationState = null; + String protocolError = "Expected a CONTINUATION frame but received " + frame; + protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError); return; } - stream.incoming(frame); } else { - stream.incoming(frame); + if (frame instanceof PushPromiseFrame pp) { + try { + handlePushPromise(stream, pp); + } catch (IOException | UncheckedIOException e) { + protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage()); + return; + } + } else if (frame instanceof HeaderFrame hf) { + // decode headers + try { + decodeHeaders(hf, stream.rspHeadersConsumer()); + } catch (IOException | UncheckedIOException e) { + debug.log("Error decoding headers: " + e.getMessage(), e); + protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage()); + return; + } + stream.incoming(frame); + } else { + stream.incoming(frame); + } } } } + private String checkMaxOrphanedHeadersExceeded(HeaderFrame hf) { + if (MAX_ORPHANED_HEADERS > 0 ) { + int orphaned = orphanedHeaders.incrementAndGet(); + if (orphaned < 0 || orphaned > MAX_ORPHANED_HEADERS) { + return "Too many orphaned header frames received on connection"; + } + } + return null; + } + final void dropDataFrame(DataFrame df) { if (closed) return; if (debug.on()) { @@ -839,24 +987,71 @@ final void ensureWindowUpdated(DataFrame df) { private void handlePushPromise(Stream parent, PushPromiseFrame pp) throws IOException { + int promisedStreamid = pp.getPromisedStream(); + if ((promisedStreamid & 0x01) != 0x00) { + throw new ProtocolException("Received PUSH_PROMISE for stream " + promisedStreamid); + } + int streamId = pp.streamid(); + if ((streamId & 0x01) != 0x01) { + throw new ProtocolException("Received PUSH_PROMISE on stream " + streamId); + } // always decode the headers as they may affect connection-level HPACK // decoding state - HeaderDecoder decoder = new HeaderDecoder(); + assert pushContinuationState == null; + PushPromiseDecoder decoder = new PushPromiseDecoder(streamId, promisedStreamid, parent); decodeHeaders(pp, decoder); + if (pp.endHeaders()) { + if (decoder.errorRef.get() == null) { + completePushPromise(promisedStreamid, parent, decoder.headers()); + } + } else { + pushContinuationState = new PushContinuationState(decoder, pp); + } + } + + private void handlePushContinuation(PushContinuationState pcs, Stream parent, ContinuationFrame cf) + throws IOException { + assert pcs.pushContFrame.streamid() == cf.streamid() : String.format( + "Received CONTINUATION on a different stream %s != %s", + cf.streamid(), pcs.pushContFrame.streamid()); + decodeHeaders(cf, pcs.pushContDecoder); + // if all continuations are sent, set pushWithContinuation to null + if (cf.endHeaders()) { + if (pcs.pushContDecoder.errorRef.get() == null) { + completePushPromise(pcs.pushContFrame.getPromisedStream(), parent, + pcs.pushContDecoder.headers()); + } + pushContinuationState = null; + } + } + private void completePushPromise(int promisedStreamid, Stream parent, HttpHeaders headers) + throws IOException { + if (parent == null) { + resetStream(promisedStreamid, ResetFrame.REFUSED_STREAM); + return; + } HttpRequestImpl parentReq = parent.request; - int promisedStreamid = pp.getPromisedStream(); + if (promisedStreamid < nextPushStream) { + // From RFC 9113 section 5.1.1: + // The identifier of a newly established stream MUST be numerically + // greater than all streams that the initiating endpoint has + // opened or reserved. + protocolError(ResetFrame.PROTOCOL_ERROR, String.format( + "Unexpected stream identifier: %s < %s", promisedStreamid, nextPushStream)); + return; + } if (promisedStreamid != nextPushStream) { + // we don't support skipping stream ids; resetStream(promisedStreamid, ResetFrame.PROTOCOL_ERROR); return; - } else if (!reserveStream(false)) { + } else if (!reserveStream(false, true)) { resetStream(promisedStreamid, ResetFrame.REFUSED_STREAM); return; } else { nextPushStream += 2; } - HttpHeaders headers = decoder.headers(); HttpRequestImpl pushReq = HttpRequestImpl.createPushRequest(parentReq, headers); Exchange pushExch = new Exchange<>(pushReq, parent.exchange.multi); Stream.PushedStream pushStream = createPushStream(parent, pushExch); @@ -971,9 +1166,15 @@ private void protocolError(int errorCode) private void protocolError(int errorCode, String msg) throws IOException { + String protocolError = "protocol error" + (msg == null?"":(": " + msg)); + ProtocolException protocolException = + new ProtocolException(protocolError); + framesDecoder.close(protocolError); + subscriber.stop(protocolException); + if (debug.on()) debug.log("Sending GOAWAY due to " + protocolException); GoAwayFrame frame = new GoAwayFrame(0, errorCode); sendFrame(frame); - shutdown(new IOException("protocol error" + (msg == null?"":(": " + msg)))); + shutdown(protocolException); } private void handleSettings(SettingsFrame frame) @@ -1080,6 +1281,21 @@ private void sendConnectionPreface() throws IOException { subscriber.onNext(List.of(EMPTY_TRIGGER)); } + /** + * Called to get the initial stream after a connection upgrade. + * If the stream was cancelled, it might no longer be in the + * stream map. Therefore - we use the initialStream field + * instead, and reset it to null after returning it. + * @param the response type + * @return the initial stream created during the upgrade. + */ + @SuppressWarnings("unchecked") + Stream getInitialStream() { + var s = (Stream) initialStream; + initialStream = null; + return s; + } + /** * Returns an existing Stream with given id, or null if doesn't exist */ @@ -1098,7 +1314,7 @@ final Stream createStream(Exchange exchange) { Stream.PushedStream createPushStream(Stream parent, Exchange pushEx) { PushGroup pg = parent.exchange.getPushGroup(); - return new Stream.PushedStream<>(pg, this, pushEx); + return new Stream.PushedStream<>(parent, pg, this, pushEx); } void putStream(Stream stream, int streamid) { @@ -1160,16 +1376,18 @@ private ByteBuffer getHeaderBuffer(int size) { private List encodeHeadersImpl(int bufferSize, HttpHeaders... headers) { ByteBuffer buffer = getHeaderBuffer(bufferSize); List buffers = new ArrayList<>(); - for(HttpHeaders header : headers) { + for (HttpHeaders header : headers) { for (Map.Entry> e : header.map().entrySet()) { String lKey = e.getKey().toLowerCase(Locale.US); List values = e.getValue(); for (String value : values) { hpackOut.header(lKey, value); while (!hpackOut.encode(buffer)) { - buffer.flip(); - buffers.add(buffer); - buffer = getHeaderBuffer(bufferSize); + if (!buffer.hasRemaining()) { + buffer.flip(); + buffers.add(buffer); + buffer = getHeaderBuffer(bufferSize); + } } } } @@ -1232,6 +1450,8 @@ void sendFrame(Http2Frame frame) { Stream stream = registerNewStream(oh); // provide protection from inserting unordered frames between Headers and Continuation if (stream != null) { + // we are creating a new stream: reset orphaned header count + orphanedHeaders.set(0); publisher.enqueue(encodeHeaders(oh, stream)); } } else { @@ -1290,7 +1510,7 @@ final class Http2TubeSubscriber implements TubeSubscriber { private volatile Flow.Subscription subscription; private volatile boolean completed; private volatile boolean dropped; - private volatile Throwable error; + private final AtomicReference errorRef = new AtomicReference<>(); private final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); private final SequentialScheduler scheduler = @@ -1311,10 +1531,9 @@ final void processQueue() { asyncReceive(buffer); } } catch (Throwable t) { - Throwable x = error; - if (x == null) error = t; + errorRef.compareAndSet(null, t); } finally { - Throwable x = error; + Throwable x = errorRef.get(); if (x != null) { if (debug.on()) debug.log("Stopping scheduler", x); scheduler.stop(); @@ -1349,6 +1568,7 @@ public void onSubscribe(Flow.Subscription subscription) { @Override public void onNext(List item) { + if (completed) return; if (debug.on()) debug.log(() -> "onNext: got " + Utils.remaining(item) + " bytes in " + item.size() + " buffers"); queue.addAll(item); @@ -1357,19 +1577,21 @@ public void onNext(List item) { @Override public void onError(Throwable throwable) { + if (completed) return; if (debug.on()) debug.log(() -> "onError: " + throwable); - error = throwable; + errorRef.compareAndSet(null, throwable); completed = true; runOrSchedule(); } @Override public void onComplete() { + if (completed) return; String msg = isActive() ? "EOF reached while reading" : "Idle connection closed by HTTP/2 peer"; if (debug.on()) debug.log(msg); - error = new EOFException(msg); + errorRef.compareAndSet(null, new EOFException(msg)); completed = true; runOrSchedule(); } @@ -1381,6 +1603,18 @@ public void dropSubscription() { // then we might not need the 'dropped' boolean? dropped = true; } + + void stop(Throwable error) { + if (errorRef.compareAndSet(null, error)) { + completed = true; + scheduler.stop(); + queue.clear(); + if (subscription != null) { + subscription.cancel(); + } + queue.clear(); + } + } } synchronized boolean isActive() { @@ -1397,76 +1631,6 @@ final String dbgString() { + connection.getConnectionFlow() + ")"; } - static class HeaderDecoder extends ValidatingHeadersConsumer { - - HttpHeadersBuilder headersBuilder; - - HeaderDecoder() { - this.headersBuilder = new HttpHeadersBuilder(); - } - - @Override - public void onDecoded(CharSequence name, CharSequence value) { - String n = name.toString(); - String v = value.toString(); - super.onDecoded(n, v); - headersBuilder.addHeader(n, v); - } - - HttpHeaders headers() { - return headersBuilder.build(); - } - } - - /* - * Checks RFC 7540 rules (relaxed) compliance regarding pseudo-headers. - */ - static class ValidatingHeadersConsumer implements DecodingCallback { - - private static final Set PSEUDO_HEADERS = - Set.of(":authority", ":method", ":path", ":scheme", ":status"); - - /** Used to check that if there are pseudo-headers, they go first */ - private boolean pseudoHeadersEnded; - - /** - * Called when END_HEADERS was received. This consumer may be invoked - * again after reset() is called, but for a whole new set of headers. - */ - void reset() { - pseudoHeadersEnded = false; - } - - @Override - public void onDecoded(CharSequence name, CharSequence value) - throws UncheckedIOException - { - String n = name.toString(); - if (n.startsWith(":")) { - if (pseudoHeadersEnded) { - throw newException("Unexpected pseudo-header '%s'", n); - } else if (!PSEUDO_HEADERS.contains(n)) { - throw newException("Unknown pseudo-header '%s'", n); - } - } else { - pseudoHeadersEnded = true; - if (!Utils.isValidName(n)) { - throw newException("Bad header name '%s'", n); - } - } - String v = value.toString(); - if (!Utils.isValidValue(v)) { - throw newException("Bad header value '%s'", v); - } - } - - private UncheckedIOException newException(String message, String header) - { - return new UncheckedIOException( - new IOException(String.format(message, header))); - } - } - static final class ConnectionWindowUpdateSender extends WindowUpdateSender { final int initialWindowSize; diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java index 28c30bbf2c3bb..bb0dd914afb86 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java @@ -36,6 +36,7 @@ import java.net.Authenticator; import java.net.ConnectException; import java.net.CookieHandler; +import java.net.ProtocolException; import java.net.ProxySelector; import java.net.http.HttpConnectTimeoutException; import java.net.http.HttpTimeoutException; @@ -582,6 +583,10 @@ private void debugCompleted(String tag, long startNanos, HttpRequest req) { // any other SSLException is wrapped in a plain // SSLException throw new SSLException(msg, throwable); + } else if (throwable instanceof ProtocolException) { + ProtocolException pe = new ProtocolException(msg); + pe.initCause(throwable); + throw pe; } else if (throwable instanceof IOException) { throw new IOException(msg, throwable); } else { diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestImpl.java index d2b908dffa525..27e65446844f4 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestImpl.java @@ -287,10 +287,10 @@ public HttpHeaders headers() { InetSocketAddress authority() { return authority; } - void setH2Upgrade(Http2ClientImpl h2client) { + void setH2Upgrade(Exchange exchange) { systemHeadersBuilder.setHeader("Connection", "Upgrade, HTTP2-Settings"); systemHeadersBuilder.setHeader("Upgrade", "h2c"); - systemHeadersBuilder.setHeader("HTTP2-Settings", h2client.getSettingsString()); + systemHeadersBuilder.setHeader("HTTP2-Settings", exchange.h2cSettingsStrings()); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java index 66d89ae1fc5f9..22e03238d2140 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ import java.security.AccessControlContext; import java.security.AccessController; import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; @@ -137,16 +138,21 @@ public void applyPushPromise( if (!initiatingURI.getHost().equalsIgnoreCase(pushRequestURI.getHost())) return; + String initiatingScheme = initiatingURI.getScheme(); + String pushRequestScheme = pushRequestURI.getScheme(); + + if (!initiatingScheme.equalsIgnoreCase(pushRequestScheme)) return; + int initiatingPort = initiatingURI.getPort(); if (initiatingPort == -1 ) { - if ("https".equalsIgnoreCase(initiatingURI.getScheme())) + if ("https".equalsIgnoreCase(initiatingScheme)) initiatingPort = 443; else initiatingPort = 80; } int pushPort = pushRequestURI.getPort(); if (pushPort == -1 ) { - if ("https".equalsIgnoreCase(pushRequestURI.getScheme())) + if ("https".equalsIgnoreCase(pushRequestScheme)) pushPort = 443; else pushPort = 80; diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java b/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java index 5fc8eb3677222..0b9cf64d49050 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.io.UncheckedIOException; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; +import java.net.ProtocolException; import java.net.URI; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -41,6 +42,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.Flow.Subscription; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiPredicate; import java.net.http.HttpClient; @@ -48,10 +50,13 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodySubscriber; + import jdk.internal.net.http.common.*; import jdk.internal.net.http.frame.*; import jdk.internal.net.http.hpack.DecodingCallback; +import static jdk.internal.net.http.Exchange.MAX_NON_FINAL_RESPONSES; + /** * Http/2 Stream handling. * @@ -135,6 +140,10 @@ class Stream extends ExchangeImpl { private volatile boolean remotelyClosed; private volatile boolean closed; private volatile boolean endStreamSent; + private volatile boolean finalResponseCodeReceived; + private volatile boolean trailerReceived; + private AtomicInteger nonFinalResponseCount = new AtomicInteger(); + // Indicates the first reason that was invoked when sending a ResetFrame // to the server. A streamState of 0 indicates that no reset was sent. // (see markStream(int code) @@ -414,7 +423,7 @@ CompletableFuture> sendBodyAsync() { private boolean checkRequestCancelled() { if (exchange.multi.requestCancelled()) { if (errorRef.get() == null) cancel(); - else sendCancelStreamFrame(); + else sendResetStreamFrame(ResetFrame.CANCEL); return true; } return false; @@ -462,32 +471,89 @@ DecodingCallback rspHeadersConsumer() { return rspHeadersConsumer; } + String checkInterimResponseCountExceeded() { + // this is also checked by Exchange - but tracking it here too provides + // a more informative message. + int count = nonFinalResponseCount.incrementAndGet(); + if (MAX_NON_FINAL_RESPONSES > 0 && (count < 0 || count > MAX_NON_FINAL_RESPONSES)) { + return String.format( + "Stream %s PROTOCOL_ERROR: too many interim responses received: %s > %s", + streamid, count, MAX_NON_FINAL_RESPONSES); + } + return null; + } + protected void handleResponse() throws IOException { HttpHeaders responseHeaders = responseHeadersBuilder.build(); - responseCode = (int)responseHeaders - .firstValueAsLong(":status") - .orElseThrow(() -> new IOException("no statuscode in response")); - response = new Response( - request, exchange, responseHeaders, connection(), - responseCode, HttpClient.Version.HTTP_2); + if (!finalResponseCodeReceived) { + try { + responseCode = (int) responseHeaders + .firstValueAsLong(":status") + .orElseThrow(() -> new ProtocolException(String.format( + "Stream %s PROTOCOL_ERROR: no status code in response", + streamid))); + } catch (ProtocolException cause) { + cancelImpl(cause, ResetFrame.PROTOCOL_ERROR); + rspHeadersConsumer.reset(); + return; + } + + String protocolErrorMsg = null; + // If informational code, response is partially complete + if (responseCode < 100 || responseCode > 199) { + this.finalResponseCodeReceived = true; + } else { + protocolErrorMsg = checkInterimResponseCountExceeded(); + } - /* TODO: review if needs to be removed - the value is not used, but in case `content-length` doesn't parse as - long, there will be NumberFormatException. If left as is, make sure - code up the stack handles NFE correctly. */ - responseHeaders.firstValueAsLong("content-length"); + if (protocolErrorMsg != null) { + if (debug.on()) { + debug.log(protocolErrorMsg); + } + cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR); + rspHeadersConsumer.reset(); + return; + } - if (Log.headers()) { - StringBuilder sb = new StringBuilder("RESPONSE HEADERS:\n"); - Log.dumpHeaders(sb, " ", responseHeaders); - Log.logHeaders(sb.toString()); - } + response = new Response( + request, exchange, responseHeaders, connection(), + responseCode, HttpClient.Version.HTTP_2); - // this will clear the response headers - rspHeadersConsumer.reset(); + /* TODO: review if needs to be removed + the value is not used, but in case `content-length` doesn't parse as + long, there will be NumberFormatException. If left as is, make sure + code up the stack handles NFE correctly. */ + responseHeaders.firstValueAsLong("content-length"); + + if (Log.headers()) { + StringBuilder sb = new StringBuilder("RESPONSE HEADERS:\n"); + Log.dumpHeaders(sb, " ", responseHeaders); + Log.logHeaders(sb.toString()); + } + + // this will clear the response headers + rspHeadersConsumer.reset(); + + completeResponse(response); + } else { + if (Log.headers()) { + StringBuilder sb = new StringBuilder("TRAILING HEADERS:\n"); + Log.dumpHeaders(sb, " ", responseHeaders); + Log.logHeaders(sb.toString()); + } + if (trailerReceived) { + String protocolErrorMsg = String.format( + "Stream %s PROTOCOL_ERROR: trailers already received", streamid); + if (debug.on()) { + debug.log(protocolErrorMsg); + } + cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR); + } + trailerReceived = true; + rspHeadersConsumer.reset(); + } - completeResponse(response); } void incoming_reset(ResetFrame frame) { @@ -1042,7 +1108,7 @@ private DataFrame getEmptyEndStreamDataFrame() { /** * A List of responses relating to this stream. Normally there is only - * one response, but intermediate responses like 100 are allowed + * one response, but interim responses like 100 are allowed * and must be passed up to higher level before continuing. Deals with races * such as if responses are returned before the CFs get created by * getResponseAsync() @@ -1203,6 +1269,16 @@ void cancel(IOException cause) { cancelImpl(cause); } + @Override + void onProtocolError(final IOException cause) { + if (debug.on()) { + debug.log("cancelling exchange on stream %d due to protocol error: %s", streamid, cause.getMessage()); + } + Log.logError("cancelling exchange on stream {0} due to protocol error: {1}\n", streamid, cause); + // send a RESET frame and close the stream + cancelImpl(cause, ResetFrame.PROTOCOL_ERROR); + } + void connectionClosing(Throwable cause) { Flow.Subscriber subscriber = responseSubscriber == null ? pendingResponseSubscriber : responseSubscriber; @@ -1214,6 +1290,10 @@ void connectionClosing(Throwable cause) { // This method sends a RST_STREAM frame void cancelImpl(Throwable e) { + cancelImpl(e, ResetFrame.CANCEL); + } + + void cancelImpl(final Throwable e, final int resetFrameErrCode) { errorRef.compareAndSet(null, e); if (debug.on()) { if (streamid == 0) debug.log("cancelling stream: %s", (Object)e); @@ -1245,14 +1325,14 @@ void cancelImpl(Throwable e) { try { // will send a RST_STREAM frame if (streamid != 0 && streamState == 0) { - e = Utils.getCompletionCause(e); - if (e instanceof EOFException) { + final Throwable cause = Utils.getCompletionCause(e); + if (cause instanceof EOFException) { // read EOF: no need to try & send reset connection.decrementStreamsCount(streamid); connection.closeStream(streamid); } else { // no use to send CANCEL if already closed. - sendCancelStreamFrame(); + sendResetStreamFrame(resetFrameErrCode); } } } catch (Throwable ex) { @@ -1260,10 +1340,10 @@ void cancelImpl(Throwable e) { } } - void sendCancelStreamFrame() { + void sendResetStreamFrame(final int resetFrameErrCode) { // do not reset a stream until it has a streamid. - if (streamid > 0 && markStream(ResetFrame.CANCEL) == 0) { - connection.resetStream(streamid, ResetFrame.CANCEL); + if (streamid > 0 && markStream(resetFrameErrCode) == 0) { + connection.resetStream(streamid, resetFrameErrCode); } close(); } @@ -1282,6 +1362,7 @@ void close() { } static class PushedStream extends Stream { + final Stream parent; final PushGroup pushGroup; // push streams need the response CF allocated up front as it is // given directly to user via the multi handler callback function. @@ -1289,17 +1370,19 @@ static class PushedStream extends Stream { CompletableFuture> responseCF; final HttpRequestImpl pushReq; HttpResponse.BodyHandler pushHandler; + private volatile boolean finalPushResponseCodeReceived; - PushedStream(PushGroup pushGroup, + PushedStream(Stream parent, + PushGroup pushGroup, Http2Connection connection, Exchange pushReq) { // ## no request body possible, null window controller super(connection, pushReq, null); + this.parent = parent; this.pushGroup = pushGroup; this.pushReq = pushReq.request(); this.pushCF = new MinimalFuture<>(); this.responseCF = new MinimalFuture<>(); - } CompletableFuture> responseCF() { @@ -1385,35 +1468,57 @@ void completeResponseExceptionally(Throwable t) { @Override protected void handleResponse() { HttpHeaders responseHeaders = responseHeadersBuilder.build(); - responseCode = (int)responseHeaders - .firstValueAsLong(":status") - .orElse(-1); - if (responseCode == -1) { - completeResponseExceptionally(new IOException("No status code")); - } + if (!finalPushResponseCodeReceived) { + responseCode = (int)responseHeaders + .firstValueAsLong(":status") + .orElse(-1); + + if (responseCode == -1) { + cancelImpl(new ProtocolException("No status code"), ResetFrame.PROTOCOL_ERROR); + rspHeadersConsumer.reset(); + return; + } else if (responseCode >= 100 && responseCode < 200) { + String protocolErrorMsg = checkInterimResponseCountExceeded(); + if (protocolErrorMsg != null) { + cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR); + rspHeadersConsumer.reset(); + return; + } + } - this.response = new Response( - pushReq, exchange, responseHeaders, connection(), - responseCode, HttpClient.Version.HTTP_2); + this.finalPushResponseCodeReceived = true; - /* TODO: review if needs to be removed - the value is not used, but in case `content-length` doesn't parse - as long, there will be NumberFormatException. If left as is, make - sure code up the stack handles NFE correctly. */ - responseHeaders.firstValueAsLong("content-length"); + this.response = new Response( + pushReq, exchange, responseHeaders, connection(), + responseCode, HttpClient.Version.HTTP_2); - if (Log.headers()) { - StringBuilder sb = new StringBuilder("RESPONSE HEADERS"); - sb.append(" (streamid=").append(streamid).append("):\n"); - Log.dumpHeaders(sb, " ", responseHeaders); - Log.logHeaders(sb.toString()); - } + /* TODO: review if needs to be removed + the value is not used, but in case `content-length` doesn't parse + as long, there will be NumberFormatException. If left as is, make + sure code up the stack handles NFE correctly. */ + responseHeaders.firstValueAsLong("content-length"); - rspHeadersConsumer.reset(); + if (Log.headers()) { + StringBuilder sb = new StringBuilder("RESPONSE HEADERS"); + sb.append(" (streamid=").append(streamid).append("):\n"); + Log.dumpHeaders(sb, " ", responseHeaders); + Log.logHeaders(sb.toString()); + } - // different implementations for normal streams and pushed streams - completeResponse(response); + rspHeadersConsumer.reset(); + + // different implementations for normal streams and pushed streams + completeResponse(response); + } else { + if (Log.headers()) { + StringBuilder sb = new StringBuilder("TRAILING HEADERS"); + sb.append(" (streamid=").append(streamid).append("):\n"); + Log.dumpHeaders(sb, " ", responseHeaders); + Log.logHeaders(sb.toString()); + } + rspHeadersConsumer.reset(); + } } } @@ -1461,9 +1566,12 @@ final String dbgString() { return connection.dbgString() + "/Stream("+streamid+")"; } - private class HeadersConsumer extends Http2Connection.ValidatingHeadersConsumer { + private class HeadersConsumer extends ValidatingHeadersConsumer implements DecodingCallback { + + boolean maxHeaderListSizeReached; - void reset() { + @Override + public void reset() { super.reset(); responseHeadersBuilder.clear(); debug.log("Response builder cleared, ready to receive new headers."); @@ -1473,13 +1581,46 @@ void reset() { public void onDecoded(CharSequence name, CharSequence value) throws UncheckedIOException { - String n = name.toString(); - String v = value.toString(); - super.onDecoded(n, v); - responseHeadersBuilder.addHeader(n, v); - if (Log.headers() && Log.trace()) { - Log.logTrace("RECEIVED HEADER (streamid={0}): {1}: {2}", - streamid, n, v); + if (maxHeaderListSizeReached) { + return; + } + try { + String n = name.toString(); + String v = value.toString(); + super.onDecoded(n, v); + responseHeadersBuilder.addHeader(n, v); + if (Log.headers() && Log.trace()) { + Log.logTrace("RECEIVED HEADER (streamid={0}): {1}: {2}", + streamid, n, v); + } + } catch (UncheckedIOException uio) { + // reset stream: From RFC 9113, section 8.1 + // Malformed requests or responses that are detected MUST be + // treated as a stream error (Section 5.4.2) of type + // PROTOCOL_ERROR. + onProtocolError(uio.getCause()); + } + } + + @Override + protected String formatMessage(String message, String header) { + return "malformed response: " + super.formatMessage(message, header); + } + + @Override + public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws ProtocolException { + if (maxHeaderListSizeReached) return; + try { + DecodingCallback.super.onMaxHeaderListSizeReached(size, maxHeaderListSize); + } catch (ProtocolException cause) { + maxHeaderListSizeReached = true; + // If this is a push stream: cancel the parent. + if (Stream.this instanceof Stream.PushedStream ps) { + ps.parent.onProtocolError(cause); + } + // cancel the stream, continue processing + onProtocolError(cause); + reset(); } } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/HeaderDecoder.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/HeaderDecoder.java new file mode 100644 index 0000000000000..d81f52e6630ef --- /dev/null +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/HeaderDecoder.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.net.http.common; + +import java.net.http.HttpHeaders; + +public class HeaderDecoder extends ValidatingHeadersConsumer { + + private final HttpHeadersBuilder headersBuilder; + + public HeaderDecoder() { + this.headersBuilder = new HttpHeadersBuilder(); + } + + @Override + public void onDecoded(CharSequence name, CharSequence value) { + String n = name.toString(); + String v = value.toString(); + super.onDecoded(n, v); + addHeader(n, v); + } + + protected void addHeader(String name, String value) { + headersBuilder.addHeader(name, value); + } + + public HttpHeaders headers() { + return headersBuilder.build(); + } +} diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java index 3f3e4ff339a46..aefae6507dc30 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/SSLFlowDelegate.java @@ -53,17 +53,27 @@ import java.util.function.IntBinaryOperator; /** - * Implements SSL using two SubscriberWrappers. + * Implements SSL using two {@link SubscriberWrapper}s. * - *

      Constructor takes two Flow.Subscribers: one that receives the network - * data (after it has been encrypted by SSLFlowDelegate) data, and one that - * receives the application data (before it has been encrypted by SSLFlowDelegate). + *

      Constructor takes two {@linkplain Flow.Subscriber subscribers} - {@code downReader} + * and {@code downWriter}. {@code downReader} receives the application data (after it has + * been decrypted by SSLFlowDelegate). {@code downWriter} receives the network data (after it has + * been encrypted by SSLFlowDelegate). * - *

      Methods upstreamReader() and upstreamWriter() return the corresponding - * Flow.Subscribers containing Flows for the encrypted/decrypted upstream data. - * See diagram below. + *

      Method {@link #upstreamWriter()} returns a {@linkplain Subscriber subscriber} which should + * be subscribed with a {@linkplain Flow.Publisher publisher} which publishes application data + * that can then be encrypted into network data by this SSLFlowDelegate and handed off to the + * {@code downWriter}. * - *

      How Flow.Subscribers are used in this class, and where they come from: + *

      Method {@link #upstreamReader()} returns a {@link Subscriber subscriber} which should be + * subscribed with a {@linkplain Flow.Publisher publisher} which publishes encrypted network data + * that can then be decrypted into application data by this SSLFlowDelegate and handed off to the + * {@code downReader}. + * + *

      Errors are reported to the {@code downReader} subscriber. + * + *

      The diagram below illustrates how the Flow.Subscribers are used in this class, and where + * they come from: *

        * {@code
        *
      @@ -72,17 +82,21 @@
        * --------->  data flow direction
        *
        *
      - *                         +------------------+
      - *        upstreamWriter   |                  | downWriter
      - *        ---------------> |                  | ------------>
      - *  obtained from this     |                  | supplied to constructor
      - *                         | SSLFlowDelegate  |
      - *        downReader       |                  | upstreamReader
      - *        <--------------- |                  | <--------------
      - * supplied to constructor |                  | obtained from this
      - *                         +------------------+
      - *
      - * Errors are reported to the downReader Flow.Subscriber
      + *                  |                                   ^
      + *  upstreamWriter  |                                   | downReader
      + *  obtained from   |                                   | supplied to
      + * upstreamWriter() |                                   | constructor
      + *                  v                                   |
      + *      +-----------------------------------------------------------+
      + *      *                                            decrypts       *
      + *      *                       SSLFlowDelegate                     *
      + *      *        encrypts                                           *
      + *      +-----------------------------------------------------------+
      + *                  |                                   ^
      + *    downWriter    |                                   | upstreamReader
      + *    supplied to   |                                   | obtained from
      + *    constructor   |                                   | upstreamReader()
      + *                  v                                   |
        *
        * }
        * 
      @@ -467,7 +481,7 @@ else if (this.completing) { } } // request more data and return. - requestMore(); + requestMoreDataIfNeeded(); return; } if (complete && result.status() == Status.CLOSED) { diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java index a3f6a11aa3ef4..323042de3d75c 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java @@ -392,16 +392,21 @@ public static URLPermission permissionForServer(URI uri, } + private static final boolean[] LOWER_CASE_CHARS = new boolean[128]; + // ABNF primitives defined in RFC 7230 private static final boolean[] tchar = new boolean[256]; private static final boolean[] fieldvchar = new boolean[256]; static { - char[] allowedTokenChars = - ("!#$%&'*+-.^_`|~0123456789" + - "abcdefghijklmnopqrstuvwxyz" + - "ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray(); - for (char c : allowedTokenChars) { + char[] lcase = ("!#$%&'*+-.^_`|~0123456789" + + "abcdefghijklmnopqrstuvwxyz").toCharArray(); + for (char c : lcase) { + tchar[c] = true; + LOWER_CASE_CHARS[c] = true; + } + char[] ucase = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray(); + for (char c : ucase) { tchar[c] = true; } for (char c = 0x21; c <= 0xFF; c++) { @@ -410,6 +415,16 @@ public static URLPermission permissionForServer(URI uri, fieldvchar[0x7F] = false; // a little hole (DEL) in the range } + public static boolean isValidLowerCaseName(String token) { + for (int i = 0; i < token.length(); i++) { + char c = token.charAt(i); + if (c > 255 || !LOWER_CASE_CHARS[c]) { + return false; + } + } + return !token.isEmpty(); + } + /* * Validates a RFC 7230 field-name. */ @@ -526,6 +541,19 @@ public static int getIntegerProperty(String name, int defaultValue) { Integer.parseInt(System.getProperty(name, String.valueOf(defaultValue)))); } + public static int getIntegerNetProperty(String property, int min, int max, int defaultValue, boolean log) { + int value = Utils.getIntegerNetProperty(property, defaultValue); + // use default value if misconfigured + if (value < min || value > max) { + if (log && Log.errors()) { + Log.logError("Property value for {0}={1} not in [{2}..{3}]: " + + "using default={4}", property, value, min, max, defaultValue); + } + value = defaultValue; + } + return value; + } + public static SSLParameters copySSLParameters(SSLParameters p) { SSLParameters p1 = new SSLParameters(); p1.setAlgorithmConstraints(p.getAlgorithmConstraints()); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/ValidatingHeadersConsumer.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/ValidatingHeadersConsumer.java new file mode 100644 index 0000000000000..db873cdb05e18 --- /dev/null +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/ValidatingHeadersConsumer.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.net.http.common; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Set; + +/* + * Checks RFC 9113 rules (relaxed) compliance regarding pseudo-headers. + */ +public class ValidatingHeadersConsumer { + + private static final Set PSEUDO_HEADERS = + Set.of(":authority", ":method", ":path", ":scheme", ":status"); + + /** Used to check that if there are pseudo-headers, they go first */ + private boolean pseudoHeadersEnded; + + /** + * Called when END_HEADERS was received. This consumer may be invoked + * again after reset() is called, but for a whole new set of headers. + */ + public void reset() { + pseudoHeadersEnded = false; + } + + /** + * Called when a header field (name, value) pair has been decoded + * @param name the decoded name + * @param value the decoded value + * @throws UncheckedIOException if the name or value are illegal + */ + public void onDecoded(CharSequence name, CharSequence value) + throws UncheckedIOException + { + String n = name.toString(); + if (n.startsWith(":")) { + if (pseudoHeadersEnded) { + throw newException("Unexpected pseudo-header '%s'", n); + } else if (!PSEUDO_HEADERS.contains(n)) { + throw newException("Unknown pseudo-header '%s'", n); + } + } else { + pseudoHeadersEnded = true; + // RFC-9113, section 8.2.1 for HTTP/2 and RFC-9114, section 4.2 state that + // header name MUST be lowercase (and allowed characters) + if (!Utils.isValidLowerCaseName(n)) { + throw newException("Bad header name '%s'", n); + } + } + String v = value.toString(); + if (!Utils.isValidValue(v)) { + throw newException("Bad header value '%s'", v); + } + } + + protected String formatMessage(String message, String header) { + return String.format(message, header); + } + + protected UncheckedIOException newException(String message, String header) + { + return new UncheckedIOException( + new IOException(formatMessage(message, header))); + } +} diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Decoder.java b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Decoder.java index 9d57a734ac304..881be12c67cee 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Decoder.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Decoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import jdk.internal.net.http.hpack.HPACK.Logger; import java.io.IOException; +import java.net.ProtocolException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicLong; @@ -107,12 +108,16 @@ public final class Decoder { private final StringReader stringReader; private final StringBuilder name; private final StringBuilder value; + private final int maxHeaderListSize; + private final int maxIndexed; private int intValue; private boolean firstValueRead; private boolean firstValueIndex; private boolean nameHuffmanEncoded; private boolean valueHuffmanEncoded; private int capacity; + private long size; + private int indexed; /** * Constructs a {@code Decoder} with the specified initial capacity of the @@ -129,6 +134,31 @@ public final class Decoder { * if capacity is negative */ public Decoder(int capacity) { + this(capacity, 0, 0); + } + + /** + * Constructs a {@code Decoder} with the specified initial capacity of the + * header table, a max header list size, and a maximum number of literals + * with indexing per header section. + * + *

      The value of the capacity has to be agreed between decoder and encoder out-of-band, + * e.g. by a protocol that uses HPACK + * (see 4.2. Maximum Table Size). + * + * @param capacity + * a non-negative integer + * @param maxHeaderListSize + * a maximum value for the header list size. This is the uncompressed + * names size + uncompressed values size + 32 bytes per field line + * @param maxIndexed + * the maximum number of literal with indexing we're prepared to handle + * for a header field section + * + * @throws IllegalArgumentException + * if capacity is negative + */ + public Decoder(int capacity, int maxHeaderListSize, int maxIndexed) { id = DECODERS_IDS.incrementAndGet(); logger = HPACK.getLogger().subLogger("Decoder#" + id); if (logger.isLoggable(NORMAL)) { @@ -145,6 +175,8 @@ public Decoder(int capacity) { toString(), hashCode); }); } + this.maxHeaderListSize = maxHeaderListSize; + this.maxIndexed = maxIndexed; setMaxCapacity0(capacity); table = new SimpleHeaderTable(capacity, logger.subLogger("HeaderTable")); integerReader = new IntegerReader(); @@ -242,22 +274,25 @@ public void decode(ByteBuffer headerBlock, requireNonNull(consumer, "consumer"); if (logger.isLoggable(NORMAL)) { logger.log(NORMAL, () -> format("reading %s, end of header block? %s", - headerBlock, endOfHeaderBlock)); + headerBlock, endOfHeaderBlock)); } while (headerBlock.hasRemaining()) { proceed(headerBlock, consumer); } if (endOfHeaderBlock && state != State.READY) { logger.log(NORMAL, () -> format("unexpected end of %s representation", - state)); + state)); throw new IOException("Unexpected end of header block"); } + if (endOfHeaderBlock) { + size = indexed = 0; + } } private void proceed(ByteBuffer input, DecodingCallback action) throws IOException { switch (state) { - case READY -> resumeReady(input); + case READY -> resumeReady(input, action); case INDEXED -> resumeIndexed(input, action); case LITERAL -> resumeLiteral(input, action); case LITERAL_WITH_INDEXING -> resumeLiteralWithIndexing(input, action); @@ -268,7 +303,7 @@ private void proceed(ByteBuffer input, DecodingCallback action) } } - private void resumeReady(ByteBuffer input) { + private void resumeReady(ByteBuffer input, DecodingCallback action) throws IOException { int b = input.get(input.position()) & 0xff; // absolute read State s = states.get(b); if (logger.isLoggable(EXTRA)) { @@ -289,6 +324,9 @@ private void resumeReady(ByteBuffer input) { } break; case LITERAL_WITH_INDEXING: + if (maxIndexed > 0 && ++indexed > maxIndexed) { + action.onMaxLiteralWithIndexingReached(indexed, maxIndexed); + } state = State.LITERAL_WITH_INDEXING; firstValueIndex = (b & 0b0011_1111) != 0; if (firstValueIndex) { @@ -315,6 +353,12 @@ private void resumeReady(ByteBuffer input) { } } + private void checkMaxHeaderListSize(long sz, DecodingCallback consumer) throws ProtocolException { + if (maxHeaderListSize > 0 && sz > maxHeaderListSize) { + consumer.onMaxHeaderListSizeReached(sz, maxHeaderListSize); + } + } + // 0 1 2 3 4 5 6 7 // +---+---+---+---+---+---+---+---+ // | 1 | Index (7+) | @@ -332,6 +376,8 @@ private void resumeIndexed(ByteBuffer input, DecodingCallback action) } try { SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue); + size = size + 32 + f.name.length() + f.value.length(); + checkMaxHeaderListSize(size, action); action.onIndexed(intValue, f.name, f.value); } finally { state = State.READY; @@ -374,7 +420,7 @@ private SimpleHeaderTable.HeaderField getHeaderFieldAt(int index) // private void resumeLiteral(ByteBuffer input, DecodingCallback action) throws IOException { - if (!completeReading(input)) { + if (!completeReading(input, action)) { return; } try { @@ -385,6 +431,8 @@ private void resumeLiteral(ByteBuffer input, DecodingCallback action) intValue, value, valueHuffmanEncoded)); } SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue); + size = size + 32 + f.name.length() + value.length(); + checkMaxHeaderListSize(size, action); action.onLiteral(intValue, f.name, value, valueHuffmanEncoded); } else { if (logger.isLoggable(NORMAL)) { @@ -392,6 +440,8 @@ private void resumeLiteral(ByteBuffer input, DecodingCallback action) "literal without indexing ('%s', huffman=%b, '%s', huffman=%b)", name, nameHuffmanEncoded, value, valueHuffmanEncoded)); } + size = size + 32 + name.length() + value.length(); + checkMaxHeaderListSize(size, action); action.onLiteral(name, nameHuffmanEncoded, value, valueHuffmanEncoded); } } finally { @@ -425,7 +475,7 @@ private void resumeLiteral(ByteBuffer input, DecodingCallback action) private void resumeLiteralWithIndexing(ByteBuffer input, DecodingCallback action) throws IOException { - if (!completeReading(input)) { + if (!completeReading(input, action)) { return; } try { @@ -445,6 +495,8 @@ private void resumeLiteralWithIndexing(ByteBuffer input, } SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue); n = f.name; + size = size + 32 + n.length() + v.length(); + checkMaxHeaderListSize(size, action); action.onLiteralWithIndexing(intValue, n, v, valueHuffmanEncoded); } else { n = name.toString(); @@ -453,6 +505,8 @@ private void resumeLiteralWithIndexing(ByteBuffer input, "literal with incremental indexing ('%s', huffman=%b, '%s', huffman=%b)", n, nameHuffmanEncoded, value, valueHuffmanEncoded)); } + size = size + 32 + n.length() + v.length(); + checkMaxHeaderListSize(size, action); action.onLiteralWithIndexing(n, nameHuffmanEncoded, v, valueHuffmanEncoded); } table.put(n, v); @@ -486,7 +540,7 @@ private void resumeLiteralWithIndexing(ByteBuffer input, private void resumeLiteralNeverIndexed(ByteBuffer input, DecodingCallback action) throws IOException { - if (!completeReading(input)) { + if (!completeReading(input, action)) { return; } try { @@ -497,6 +551,8 @@ private void resumeLiteralNeverIndexed(ByteBuffer input, intValue, value, valueHuffmanEncoded)); } SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue); + size = size + 32 + f.name.length() + value.length(); + checkMaxHeaderListSize(size, action); action.onLiteralNeverIndexed(intValue, f.name, value, valueHuffmanEncoded); } else { if (logger.isLoggable(NORMAL)) { @@ -504,6 +560,8 @@ private void resumeLiteralNeverIndexed(ByteBuffer input, "literal never indexed ('%s', huffman=%b, '%s', huffman=%b)", name, nameHuffmanEncoded, value, valueHuffmanEncoded)); } + size = size + 32 + name.length() + value.length(); + checkMaxHeaderListSize(size, action); action.onLiteralNeverIndexed(name, nameHuffmanEncoded, value, valueHuffmanEncoded); } } finally { @@ -541,7 +599,7 @@ private void resumeSizeUpdate(ByteBuffer input, } } - private boolean completeReading(ByteBuffer input) throws IOException { + private boolean completeReading(ByteBuffer input, DecodingCallback action) throws IOException { if (!firstValueRead) { if (firstValueIndex) { if (!integerReader.read(input)) { @@ -551,6 +609,8 @@ private boolean completeReading(ByteBuffer input) throws IOException { integerReader.reset(); } else { if (!stringReader.read(input, name)) { + long sz = size + 32 + name.length(); + checkMaxHeaderListSize(sz, action); return false; } nameHuffmanEncoded = stringReader.isHuffmanEncoded(); @@ -560,6 +620,8 @@ private boolean completeReading(ByteBuffer input) throws IOException { return false; } else { if (!stringReader.read(input, value)) { + long sz = size + 32 + name.length() + value.length(); + checkMaxHeaderListSize(sz, action); return false; } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java index 5e9df860febeb..228f9bf02061e 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java @@ -24,6 +24,7 @@ */ package jdk.internal.net.http.hpack; +import java.net.ProtocolException; import java.nio.ByteBuffer; /** @@ -292,4 +293,17 @@ default void onLiteralWithIndexing(CharSequence name, * new capacity of the header table */ default void onSizeUpdate(int capacity) { } + + default void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) + throws ProtocolException { + throw new ProtocolException(String + .format("Size exceeds MAX_HEADERS_LIST_SIZE: %s > %s", + size, maxHeaderListSize)); + } + + default void onMaxLiteralWithIndexingReached(long indexed, int maxIndexed) + throws ProtocolException { + throw new ProtocolException(String.format("Too many literal with indexing: %s > %s", + indexed, maxIndexed)); + } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java index 4188937b1ad93..c603e917ca437 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -258,9 +258,10 @@ public void header(CharSequence name, } } } + assert encoding : "encoding is false"; } - private boolean isHuffmanBetterFor(CharSequence value) { + protected final boolean isHuffmanBetterFor(CharSequence value) { // prefer Huffman encoding only if it is strictly smaller than Latin-1 return huffmanWriter.lengthOf(value) < value.length(); } @@ -340,6 +341,10 @@ protected int calculateCapacity(int maxCapacity) { return 0; } + protected final int tableIndexOf(CharSequence name, CharSequence value) { + return getHeaderTable().indexOf(name, value); + } + /** * Encodes the {@linkplain #header(CharSequence, CharSequence) set up} * header into the given buffer. @@ -380,6 +385,7 @@ public final boolean encode(ByteBuffer headerBlock) { writer.reset(); // FIXME: WHY? encoding = false; } + assert done || encoding : "done: " + done + ", encoding: " + encoding; return done; } @@ -542,4 +548,8 @@ protected final void checkEncoding() { // TODO: better name e.g. checkIfEncoding "Previous encoding operation hasn't finished yet"); } } + + protected final Logger logger() { + return logger; + } } diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java index 492915f638e4a..9ca1272dfb0eb 100644 --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -168,7 +168,7 @@ public String toString() { if (destroyed) { return "Destroyed EncryptionKey"; } - return "key " + key.toString(); + return "EncryptionKey: " + key.toString(); } /** diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java index c39ae01d913a4..08b67e0abaf0a 100644 --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ import javax.security.auth.Destroyable; import java.util.Arrays; -import java.util.Base64; import java.util.Objects; /** @@ -140,8 +139,7 @@ public String toString() { if (destroyed) { return "Destroyed KerberosCredMessage"; } else { - return "KRB_CRED from " + sender + " to " + recipient + ":\n" - + Base64.getUrlEncoder().encodeToString(message); + return "KRB_CRED from " + sender + " to " + recipient; } } diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java index b5874f5637e96..43d74c37a6baf 100644 --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -270,9 +270,9 @@ public String toString() { if (destroyed) { return "Destroyed KerberosKey"; } - return "Kerberos Principal " + principal + - "Key Version " + versionNum + - "key " + key.toString(); + return "KerberosKey: principal " + principal + + ", version " + versionNum + + ", key " + key.toString(); } /** diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java index 59c1a4458f8ef..caa702c2ed483 100644 --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,8 @@ import javax.crypto.SecretKey; import javax.security.auth.Destroyable; import javax.security.auth.DestroyFailedException; -import sun.security.util.HexDumpEncoder; + +import sun.security.jgss.krb5.Krb5Util; import sun.security.krb5.Asn1Exception; import sun.security.krb5.PrincipalName; import sun.security.krb5.EncryptionKey; @@ -222,15 +223,8 @@ private void readObject(ObjectInputStream ois) } public String toString() { - HexDumpEncoder hd = new HexDumpEncoder(); - return "EncryptionKey: keyType=" + keyType - + " keyBytes (hex dump)=" - + (keyBytes == null || keyBytes.length == 0 ? - " Empty Key" : - '\n' + hd.encodeBuffer(keyBytes) - + '\n'); - - + return "keyType=" + keyType + + ", " + Krb5Util.keyInfo(keyBytes); } public int hashCode() { diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java index 3cb0bf46cb8c2..1f89d73300632 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java @@ -33,8 +33,10 @@ import sun.security.jgss.TokenTracker; import sun.security.krb5.*; import java.io.InputStream; -import java.io.OutputStream; +import java.io.InvalidObjectException; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.OutputStream; import java.security.Provider; import java.security.AccessController; import java.security.AccessControlContext; @@ -914,15 +916,11 @@ public final int getWrapSizeLimit(int qop, boolean confReq, public final byte[] wrap(byte[] inBuf, int offset, int len, MessageProp msgProp) throws GSSException { - if (DEBUG) { - System.out.println("Krb5Context.wrap: data=[" - + getHexBytes(inBuf, offset, len) - + "]"); - } - if (state != STATE_DONE) - throw new GSSException(GSSException.NO_CONTEXT, -1, - "Wrap called in invalid state!"); + if (state != STATE_DONE) { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Wrap called in invalid state!"); + } byte[] encToken = null; try { @@ -1067,12 +1065,6 @@ public final byte[] unwrap(byte[] inBuf, int offset, int len, setSequencingAndReplayProps(token, msgProp); } - if (DEBUG) { - System.out.println("Krb5Context.unwrap: data=[" - + getHexBytes(data, 0, data.length) - + "]"); - } - return data; } @@ -1423,8 +1415,22 @@ public byte[] getEncoded() { @Override public String toString() { - return "Kerberos session key: etype: " + key.getEType() + "\n" + - new HexDumpEncoder().encodeBuffer(key.getBytes()); + return "Kerberos session key: etype=" + key.getEType() + + ", " + Krb5Util.keyInfo(key.getBytes()); + } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException + ("KerberosSessionKey not directly deserializable"); } } @@ -1495,5 +1501,4 @@ public void setAuthTime(String authTime) { public void setAuthzData(AuthorizationData authzData) { this.authzData = authzData; } - } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java index 7b7893ef3a3c4..5db92f210bba8 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,9 @@ import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.kerberos.KerberosPrincipal; import java.net.InetAddress; +import java.io.InvalidObjectException; import java.io.IOException; +import java.io.ObjectInputStream; import java.util.Date; import java.security.AccessController; import java.security.AccessControlContext; @@ -405,4 +407,17 @@ public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException { throw ge; } } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("Krb5InitCredential not deserializable"); + } } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java index 24353cceeca9b..5410220502687 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java @@ -201,4 +201,19 @@ public static EncryptionKey[] keysFromJavaxKeyTab( KeyTab ktab, PrincipalName cname) { return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname); } + + public static String keyInfo(byte[] data) { + if (data == null) { + return "null key"; + } else if (data.length == 0) { + return "empty key"; + } else { + for (byte b : data) { + if (b != 0) { + return data.length + "-byte key"; + } + } + return data.length + "-byte zero key"; + } + } } diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java b/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java index c7dc72fdebe66..d812fd63d2c31 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java @@ -31,6 +31,7 @@ package sun.security.krb5; +import sun.security.jgss.krb5.Krb5Util; import sun.security.util.*; import sun.security.krb5.internal.*; import sun.security.krb5.internal.crypto.*; @@ -498,12 +499,7 @@ public synchronized void writeKey(CCacheOutputStream cos) public String toString() { return new String("EncryptionKey: keyType=" + keyType - + " kvno=" + kvno - + " keyValue (hex dump)=" - + (keyValue == null || keyValue.length == 0 ? - " Empty Key" : '\n' - + Krb5.hexDumper.encodeBuffer(keyValue) - + '\n')); + + ", kvno=" + kvno + ", " + Krb5Util.keyInfo(keyValue)); } /** diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java index 9350521609034..653786928f7e0 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java @@ -79,30 +79,38 @@ * * http://www.ietf.org/rfc/rfc4120.txt. */ -// The instance fields not statically typed as Serializable are ASN.1 -// encoded and written by the writeObject method. -@SuppressWarnings("serial") + public class KRBError implements java.io.Serializable { static final long serialVersionUID = 3643809337475284503L; - private int pvno; - private int msgType; - private KerberosTime cTime; //optional - private Integer cuSec; //optional - private KerberosTime sTime; - private Integer suSec; - private int errorCode; - private Realm crealm; //optional - private PrincipalName cname; //optional - private PrincipalName sname; - private String eText; //optional - private byte[] eData; //optional - private Checksum eCksum; //optional - - private PAData[] pa; // PA-DATA in eData + private transient int pvno; + private transient int msgType; + private transient KerberosTime cTime; //optional + private transient Integer cuSec; //optional + private transient KerberosTime sTime; + private transient Integer suSec; + private transient int errorCode; + private transient Realm crealm; //optional + private transient PrincipalName cname; //optional + private transient PrincipalName sname; + private transient String eText; //optional + private transient byte[] eData; //optional + private transient Checksum eCksum; //optional + + private transient PAData[] pa; // PA-DATA in eData + + private static boolean DEBUG = Krb5.DEBUG; + /** + * Restores the state of this object from the stream. + * + * @param is the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { try { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java index fabff57ae6427..93831f5a3e6c3 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java @@ -315,9 +315,6 @@ public static String getErrorMessage(int i) { public static final boolean DEBUG = GetBooleanAction .privilegedGetProperty("sun.security.krb5.debug"); - public static final sun.security.util.HexDumpEncoder hexDumper = - new sun.security.util.HexDumpEncoder(); - static { errMsgList = new Hashtable (); errMsgList.put(KDC_ERR_NONE, "No error"); diff --git a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java index 813939643c3cf..4f124771d5170 100644 --- a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java +++ b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Kinit.java @@ -192,10 +192,6 @@ private void acquire() System.out.print("Password for " + princName + ":"); System.out.flush(); psswd = Password.readPassword(System.in); - if (DEBUG) { - System.out.println(">>> Kinit console input " + - new String(psswd)); - } } builder = new KrbAsReqBuilder(principal, psswd); } else { diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java index f81b7cf41ee33..85d4ac0a50c9e 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java @@ -53,7 +53,7 @@ public abstract class SchemaDVFactory { * @exception DVFactoryException cannot create an instance of the specified * class name or the default class name */ - public static synchronized final SchemaDVFactory getInstance() throws DVFactoryException { + public static final SchemaDVFactory getInstance() throws DVFactoryException { return getInstance(DEFAULT_FACTORY_CLASS); } //getInstance(): SchemaDVFactory @@ -66,7 +66,7 @@ public static synchronized final SchemaDVFactory getInstance() throws DVFactoryE * @exception DVFactoryException cannot create an instance of the specified * class name or the default class name */ - public static synchronized final SchemaDVFactory getInstance(String factoryClass) throws DVFactoryException { + public static final SchemaDVFactory getInstance(String factoryClass) throws DVFactoryException { try { // if the class name is not specified, use the default one @@ -78,7 +78,7 @@ public static synchronized final SchemaDVFactory getInstance(String factoryClass } // can't create a new object of this class - protected SchemaDVFactory(){} + protected SchemaDVFactory() {} /** * Get a built-in simple type of the given name diff --git a/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java b/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java index 6818a89b7bd51..7c5651f8a9b13 100644 --- a/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java +++ b/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -3851,6 +3851,8 @@ private int controlCode(KeyStroke keyStroke) { return 0; int code = keyStroke.getKeyCode(); switch (code) { + case KeyEvent.VK_TAB: + case KeyEvent.VK_SPACE: case KeyEvent.VK_BACK_SPACE: case KeyEvent.VK_DELETE: case KeyEvent.VK_DOWN: @@ -3893,15 +3895,10 @@ private char getKeyChar(KeyStroke keyStroke) { debugString("[INFO]: Shortcut is control character: " + Integer.toHexString(keyCode)); return (char)keyCode; } - String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode()); - debugString("[INFO]: Shortcut is: " + keyText); - if (keyText != null || keyText.length() > 0) { - CharSequence seq = keyText.subSequence(0, 1); - if (seq != null || seq.length() > 0) { - return seq.charAt(0); - } - } - return 0; + + keyCode = keyStroke.getKeyCode(); + debugString("[INFO]: Shortcut is: " + Integer.toHexString(keyCode)); + return (char)keyCode; } /* diff --git a/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.cpp b/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.cpp index f2dabc232e09d..448fc8a631fe7 100644 --- a/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.cpp +++ b/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -125,7 +125,7 @@ char *printError(char *msg) { va_list argprt; va_start(argprt, msg); // set up argptr - vsprintf(buf, msg, argprt); + vsnprintf(buf, sizeof(buf), msg, argprt); #ifdef SEND_TO_OUTPUT_DEBUG_STRING OutputDebugString(buf); #endif @@ -153,7 +153,7 @@ char *printError(char *msg) { va_list argprt; va_start(argprt, msg); // set up argptr - vsprintf(buf, msg, argprt); + vsnprintf(buf, sizeof(buf), msg, argprt); #ifdef SEND_TO_OUTPUT_DEBUG_STRING OutputDebugString(buf); #endif @@ -181,8 +181,8 @@ char *printError(char *msg) { va_list argprt; va_start(argprt, msg); // set up argptr - sprintf(charmsg, "%ls", msg); // convert format string to multi-byte - vsprintf(buf, charmsg, argprt); + snprintf(charmsg, sizeof(charmsg), "%ls", msg); // convert format string to multi-byte + vsnprintf(buf, sizeof(buf), charmsg, argprt); #ifdef SEND_TO_OUTPUT_DEBUG_STRING OutputDebugString(buf); #endif @@ -211,8 +211,8 @@ char *printError(char *msg) { va_list argprt; va_start(argprt, msg); // set up argptr - sprintf(charmsg, "%ls", msg); // convert format string to multi-byte - vsprintf(buf, charmsg, argprt); + snprintf(charmsg, sizeof(charmsg), "%ls", msg); // convert format string to multi-byte + vsnprintf(buf, sizeof(buf), charmsg, argprt); #ifdef SEND_TO_OUTPUT_DEBUG_STRING OutputDebugString(buf); #endif diff --git a/src/jdk.accessibility/windows/native/include/bridge/AccessBridgePackages.h b/src/jdk.accessibility/windows/native/include/bridge/AccessBridgePackages.h index 27c31be09a8a6..232cab4b21bf3 100644 --- a/src/jdk.accessibility/windows/native/include/bridge/AccessBridgePackages.h +++ b/src/jdk.accessibility/windows/native/include/bridge/AccessBridgePackages.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1108,6 +1108,8 @@ typedef long ABHWND64; #define ACCESSIBLE_CONTROLCODE_KEYSTROKE 512 // Control code key pressed, character contains control code. // The supported control code keys are: +#define ACCESSIBLE_VK_TAB 9 +#define ACCESSIBLE_VK_SPACE 32 #define ACCESSIBLE_VK_BACK_SPACE 8 #define ACCESSIBLE_VK_DELETE 127 #define ACCESSIBLE_VK_DOWN 40 diff --git a/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp b/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp index 5cfec9f2f63a0..de673a9408f39 100644 --- a/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp +++ b/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -609,7 +609,7 @@ void HandlePropertyChildChange( long vmID, PropertyChangeEvent event, char buffer[HUGE_BUFSIZE]; char *bufOffset; - sprintf( buffer, + snprintf( buffer, sizeof(buffer), "Child property changed event:\r\n=======================\r\n\r\n" ); if (oldChild != 0) { @@ -647,7 +647,7 @@ void HandlePropertyActiveDescendentChange( long vmID, PropertyChangeEvent event, JOBJECT64 newActiveDescendent ) { char buffer[HUGE_BUFSIZE]; - sprintf( buffer, + snprintf( buffer, sizeof(buffer), "ActiveDescendent property changed event:\r\n=======================\r\n\r\n" ); #ifdef _notdef diff --git a/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp b/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp index 3a5353812a484..726ed4010bd06 100644 --- a/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp +++ b/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -542,7 +542,7 @@ void Jaccesswalker::addComponentNodes(long vmID, AccessibleContext context, } } else { char s[LINE_BUFSIZE]; - sprintf( s, + snprintf( s, sizeof(s), "ERROR calling GetAccessibleContextInfo; vmID = %X, context = %p", vmID, (void*)context ); diff --git a/src/jdk.accessibility/windows/native/libwindowsaccessbridge/AccessBridgeEventHandler.cpp b/src/jdk.accessibility/windows/native/libwindowsaccessbridge/AccessBridgeEventHandler.cpp index d5600ef5cc1fa..b9841d409b494 100644 --- a/src/jdk.accessibility/windows/native/libwindowsaccessbridge/AccessBridgeEventHandler.cpp +++ b/src/jdk.accessibility/windows/native/libwindowsaccessbridge/AccessBridgeEventHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -161,9 +161,9 @@ AccessBridgeEventHandler::firePropertyChange(long vmID, wchar_t *newName) { DEBUG_CODE(char debugBuf[255]); #ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer) - DEBUG_CODE(sprintf(debugBuf, "\r\nCalling firePropertyChange(%p, %p):\r\n", event, source)); + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), "\r\nCalling firePropertyChange(%p, %p):\r\n", event, source)); #else // JOBJECT64 is jlong (64 bit) - DEBUG_CODE(sprintf(debugBuf, "\r\nCalling firePropertyChange(%016I64X, %016I64X):\r\n", event, source)); + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), "\r\nCalling firePropertyChange(%016I64X, %016I64X):\r\n", event, source)); #endif DEBUG_CODE(AppendToCallInfo(debugBuf)); @@ -194,7 +194,7 @@ const char fireEventDebugString[] = "[INFO]: In AccessBridgeEventHandler::%s(%01 #define FIRE_EVENT(method, FPprototype, eventFP) \ void AccessBridgeEventHandler::method(long vmID, JOBJECT64 event, JOBJECT64 source) { \ DEBUG_CODE(char debugBuf[255]); \ - DEBUG_CODE(sprintf(debugBuf, fireEventDebugString, #method, event, source, vmID)); \ + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), fireEventDebugString, #method, event, source, vmID)); \ DEBUG_CODE(AppendToCallInfo(debugBuf)); \ if (eventFP != (FPprototype) 0) { \ eventFP(vmID, event, source); \ @@ -205,7 +205,7 @@ const char fireEventDebugString[] = "[INFO]: In AccessBridgeEventHandler::%s(%01 void AccessBridgeEventHandler::fireJavaShutdown(long vmID) { DEBUG_CODE(char debugBuf[255]); - DEBUG_CODE(sprintf(debugBuf, "[INFO]: Calling fireJavaShutdown; vmID = %X\r\n", vmID)); + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), "[INFO]: Calling fireJavaShutdown; vmID = %X\r\n", vmID)); DEBUG_CODE(AppendToCallInfo(debugBuf)); if (javaShutdownFP != (AccessBridge_JavaShutdownFP) 0) { javaShutdownFP(vmID); @@ -249,7 +249,7 @@ const char firePropertyChangeDebugString[] = "[INFO]: In AccessBridgeEventHandle #define FIRE_PROPERTY_CHANGE(method, FPprototype, eventFP) \ void AccessBridgeEventHandler::method(long vmID, JOBJECT64 event, JOBJECT64 source) { \ DEBUG_CODE(char debugBuf[255]); \ - DEBUG_CODE(sprintf(debugBuf, firePropertyChangeDebugString, #method, event, source)); \ + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), firePropertyChangeDebugString, #method, event, source)); \ DEBUG_CODE(AppendToCallInfo(debugBuf)); \ if (eventFP != (FPprototype) 0) { \ eventFP(vmID, event, source); \ @@ -278,7 +278,7 @@ const char fireStringPropertyChangeDebugString[] = "[INFO]: In AccessBridgeEvent void AccessBridgeEventHandler::method(long vmID, JOBJECT64 event, JOBJECT64 source, \ wchar_t *oldValue, wchar_t *newValue) { \ DEBUG_CODE(char debugBuf[255]); \ - DEBUG_CODE(sprintf(debugBuf, fireStringPropertyChangeDebugString, #method, event, source, oldValue, newValue)); \ + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), fireStringPropertyChangeDebugString, #method, event, source, oldValue, newValue)); \ DEBUG_CODE(AppendToCallInfo(debugBuf)); \ if (eventFP != (FPprototype) 0) { \ eventFP(vmID, event, source, oldValue, newValue); \ @@ -307,7 +307,7 @@ const char fireIntPropertyChangeDebugString[] = "[INFO]: In AccessBridgeEventHan void AccessBridgeEventHandler::method(long vmID, JOBJECT64 event, JOBJECT64 source, \ int oldValue, int newValue) { \ DEBUG_CODE(char debugBuf[255]); \ - DEBUG_CODE(sprintf(debugBuf, fireIntPropertyChangeDebugString, #method, event, source, oldValue, newValue)); \ + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), fireIntPropertyChangeDebugString, #method, event, source, oldValue, newValue)); \ DEBUG_CODE(AppendToCallInfo(debugBuf)); \ if (eventFP != (FPprototype) 0) { \ eventFP(vmID, event, source, oldValue, newValue); \ @@ -336,7 +336,7 @@ const char fireACPropertyChangeDebugString[] = "[INFO]: In AccessBridgeEventHand void AccessBridgeEventHandler::method(long vmID, JOBJECT64 event, JOBJECT64 source, \ JOBJECT64 oldValue, JOBJECT64 newValue) { \ DEBUG_CODE(char debugBuf[255]); \ - DEBUG_CODE(sprintf(debugBuf, fireACPropertyChangeDebugString, #method, event, source, oldValue, newValue)); \ + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), fireACPropertyChangeDebugString, #method, event, source, oldValue, newValue)); \ DEBUG_CODE(AppendToCallInfo(debugBuf)); \ if (eventFP != (FPprototype) 0) { \ eventFP(vmID, event, source, oldValue, newValue); \ diff --git a/src/jdk.accessibility/windows/native/libwindowsaccessbridge/AccessBridgeJavaVMInstance.cpp b/src/jdk.accessibility/windows/native/libwindowsaccessbridge/AccessBridgeJavaVMInstance.cpp index 3d189600b42c9..3c00459df4872 100644 --- a/src/jdk.accessibility/windows/native/libwindowsaccessbridge/AccessBridgeJavaVMInstance.cpp +++ b/src/jdk.accessibility/windows/native/libwindowsaccessbridge/AccessBridgeJavaVMInstance.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,7 +68,7 @@ AccessBridgeJavaVMInstance::AccessBridgeJavaVMInstance(HWND ourABWindow, nextJVMInstance = next; memoryMappedFileMapHandle = (HANDLE) 0; memoryMappedView = (char *) 0; - sprintf(memoryMappedFileName, "AccessBridge-%p-%p.mmf", + snprintf(memoryMappedFileName, sizeof(memoryMappedFileName), "AccessBridge-%p-%p.mmf", ourAccessBridgeWindow, javaAccessBridgeWindow); } @@ -85,14 +85,14 @@ AccessBridgeJavaVMInstance::~AccessBridgeJavaVMInstance() { // if IPC memory mapped file view is valid, unmap it goingAway = TRUE; if (memoryMappedView != (char *) 0) { - DEBUG_CODE(sprintf(buffer, " unmapping memoryMappedView; view = %p\r\n", memoryMappedView)); + DEBUG_CODE(snprintf(buffer, sizeof(buffer), " unmapping memoryMappedView; view = %p\r\n", memoryMappedView)); DEBUG_CODE(AppendToCallInfo(buffer)); UnmapViewOfFile(memoryMappedView); memoryMappedView = (char *) 0; } // if IPC memory mapped file handle map is open, close it if (memoryMappedFileMapHandle != (HANDLE) 0) { - DEBUG_CODE(sprintf(buffer, " closing memoryMappedFileMapHandle; handle = %p\r\n", memoryMappedFileMapHandle)); + DEBUG_CODE(snprintf(buffer, sizeof(buffer), " closing memoryMappedFileMapHandle; handle = %p\r\n", memoryMappedFileMapHandle)); DEBUG_CODE(AppendToCallInfo(buffer)); CloseHandle(memoryMappedFileMapHandle); memoryMappedFileMapHandle = (HANDLE) 0; @@ -131,11 +131,11 @@ AccessBridgeJavaVMInstance::initiateIPC() { memoryMappedFileName); if (memoryMappedFileMapHandle == NULL) { errorCode = GetLastError(); - DEBUG_CODE(sprintf(debugBuf, " Failed to CreateFileMapping for %s, error: %X", memoryMappedFileName, errorCode)); + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), " Failed to CreateFileMapping for %s, error: %X", memoryMappedFileName, errorCode)); DEBUG_CODE(AppendToCallInfo(debugBuf)); return errorCode; } else { - DEBUG_CODE(sprintf(debugBuf, " CreateFileMapping worked - filename: %s\r\n", memoryMappedFileName)); + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), " CreateFileMapping worked - filename: %s\r\n", memoryMappedFileName)); DEBUG_CODE(AppendToCallInfo(debugBuf)); } @@ -144,11 +144,11 @@ AccessBridgeJavaVMInstance::initiateIPC() { 0, 0, 0); if (memoryMappedView == NULL) { errorCode = GetLastError(); - DEBUG_CODE(sprintf(debugBuf, " Failed to MapViewOfFile for %s, error: %X", memoryMappedFileName, errorCode)); + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), " Failed to MapViewOfFile for %s, error: %X", memoryMappedFileName, errorCode)); DEBUG_CODE(AppendToCallInfo(debugBuf)); return errorCode; } else { - DEBUG_CODE(sprintf(debugBuf, " MapViewOfFile worked - view: %p\r\n", memoryMappedView)); + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), " MapViewOfFile worked - view: %p\r\n", memoryMappedView)); DEBUG_CODE(AppendToCallInfo(debugBuf)); } @@ -169,12 +169,12 @@ AccessBridgeJavaVMInstance::initiateIPC() { // look for the JavaDLL's answer to see if it could read the file if (strcmp(memoryMappedView, AB_MEMORY_MAPPED_FILE_OK_ANSWER) != 0) { - DEBUG_CODE(sprintf(debugBuf, " JavaVM failed to deal with memory mapped file %s\r\n", + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), " JavaVM failed to deal with memory mapped file %s\r\n", memoryMappedFileName)); DEBUG_CODE(AppendToCallInfo(debugBuf)); return -1; } else { - DEBUG_CODE(sprintf(debugBuf, " Success! JavaVM accpeted our file\r\n")); + DEBUG_CODE(snprintf(debugBuf, sizeof(debugBuf), " Success! JavaVM accpeted our file\r\n")); DEBUG_CODE(AppendToCallInfo(debugBuf)); } @@ -245,16 +245,16 @@ AccessBridgeJavaVMInstance::sendMemoryPackage(char *buffer, long bufsize) { BOOL retval = FALSE; DEBUG_CODE(char outputBuf[256]); - DEBUG_CODE(sprintf(outputBuf, "AccessBridgeJavaVMInstance::sendMemoryPackage(, %d)", bufsize)); + DEBUG_CODE(snprintf(outputBuf, sizeof(outputBuf), "AccessBridgeJavaVMInstance::sendMemoryPackage(, %d)", bufsize)); DEBUG_CODE(AppendToCallInfo(outputBuf)); DEBUG_CODE(PackageType *type = (PackageType *) buffer); DEBUG_CODE(if (*type == cGetAccessibleTextRangePackage) {) DEBUG_CODE(AppendToCallInfo(" 'buffer' contains:")); DEBUG_CODE(GetAccessibleTextRangePackage *pkg = (GetAccessibleTextRangePackage *) (buffer + sizeof(PackageType))); - DEBUG_CODE(sprintf(outputBuf, " PackageType = %X", *type)); + DEBUG_CODE(snprintf(outputBuf, sizeof(outputBuf), " PackageType = %X", *type)); DEBUG_CODE(AppendToCallInfo(outputBuf)); - DEBUG_CODE(sprintf(outputBuf, " GetAccessibleTextRange: start = %d, end = %d, rText = %ls", + DEBUG_CODE(snprintf(outputBuf, sizeof(outputBuf), " GetAccessibleTextRange: start = %d, end = %d, rText = %ls", pkg->start, pkg->end, pkg->rText)); DEBUG_CODE(AppendToCallInfo(outputBuf)); DEBUG_CODE(}) @@ -269,7 +269,7 @@ AccessBridgeJavaVMInstance::sendMemoryPackage(char *buffer, long bufsize) { DEBUG_CODE(if (*type == cGetAccessibleTextItemsPackage) {) DEBUG_CODE(AppendToCallInfo(" 'memoryMappedView' now contains:")); DEBUG_CODE(GetAccessibleTextItemsPackage *pkg = (GetAccessibleTextItemsPackage *) (buffer + sizeof(PackageType))); - DEBUG_CODE(sprintf(outputBuf, " PackageType = %X", *type)); + DEBUG_CODE(snprintf(outputBuf, sizeof(outputBuf), " PackageType = %X", *type)); DEBUG_CODE(AppendToCallInfo(outputBuf)); DEBUG_CODE(}) } diff --git a/src/jdk.accessibility/windows/native/libwindowsaccessbridge/WinAccessBridge.cpp b/src/jdk.accessibility/windows/native/libwindowsaccessbridge/WinAccessBridge.cpp index 6c6ad58ecead5..4863581d2c810 100644 --- a/src/jdk.accessibility/windows/native/libwindowsaccessbridge/WinAccessBridge.cpp +++ b/src/jdk.accessibility/windows/native/libwindowsaccessbridge/WinAccessBridge.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -160,7 +160,7 @@ extern "C" { */ char buf[1024]; - sprintf(buf, "WinAccessBridge: %s", s); + snprintf(buf, sizeof(buf), "WinAccessBridge: %s", s); OutputDebugString(buf); } diff --git a/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.cpp b/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.cpp index 564c2e29bcef2..bf8d25f8c6a79 100644 --- a/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.cpp +++ b/src/jdk.accessibility/windows/native/toolscommon/AccessInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,7 +49,7 @@ char *getTimeAndDate() { if( newtime->tm_hour == 0 ) /*Set hour to 12 if midnight. */ newtime->tm_hour = 12; - sprintf(datebuf, "%.19s %s\n", asctime( newtime ), am_pm ); + snprintf(datebuf, sizeof(datebuf), "%.19s %s\n", asctime( newtime ), am_pm ); return (char *)datebuf; } @@ -67,7 +67,7 @@ void displayAndLog(HWND hDlg, int nIDDlgItem, FILE *logfile, char *msg, ...) { va_list argprt; va_start(argprt, msg); - vsprintf(tmpbuf, msg, argprt); + vsnprintf(tmpbuf, sizeof(tmpbuf), msg, argprt); SetDlgItemText(hDlg, nIDDlgItem, tmpbuf); @@ -95,7 +95,7 @@ void logString(FILE *logfile, char *msg, ...) { va_list argprt; va_start(argprt, msg); - vsprintf(tmpbuf, msg, argprt); + vsnprintf(tmpbuf, sizeof(tmpbuf), msg, argprt); fprintf(logfile, tmpbuf); fprintf(logfile, "\n"); @@ -119,7 +119,7 @@ BOOL appendToBuffer(char *buf, size_t buflen, char *msg, ...) { va_list argprt; va_start(argprt, msg); - vsprintf(tmpbuf, msg, argprt); + vsnprintf(tmpbuf, sizeof(tmpbuf), msg, argprt); // verify there's enough space in the buffer size_t spaceRemaining = buflen - strlen(buf) - 1; diff --git a/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c index f08a7c004b314..3e3118c327f2f 100644 --- a/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -209,7 +209,7 @@ JNIEXPORT jlong JNICALL Java_sun_tools_attach_VirtualMachineImpl_openProcess } else { char err_mesg[255]; /* include the last error in the default detail message */ - sprintf(err_mesg, "OpenProcess(pid=%d) failed; LastError=0x%x", + snprintf(err_mesg, sizeof(err_mesg), "OpenProcess(pid=%d) failed; LastError=0x%x", (int)pid, (int)GetLastError()); JNU_ThrowIOExceptionWithLastError(env, err_mesg); } @@ -492,7 +492,7 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue break; default : { char errmsg[128]; - sprintf(errmsg, "Remote thread failed for unknown reason (%d)", exitCode); + snprintf(errmsg, sizeof(errmsg), "Remote thread failed for unknown reason (%d)", exitCode); JNU_ThrowInternalError(env, errmsg); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index 1968d182fe06e..80b70ddd9579d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -1561,8 +1561,15 @@ public void visitSelect(JCFieldAccess tree) { public void visitVarDef(JCVariableDecl tree) { TranslationContext context = context(); if (context != null && context instanceof LambdaTranslationContext lambdaContext) { - if (frameStack.head.tree.hasTag(LAMBDA)) { - lambdaContext.addSymbol(tree.sym, LOCAL_VAR); + for (Frame frame : frameStack) { + if (frame.tree.hasTag(VARDEF)) { + //skip variable frames inside a lambda: + continue; + } else if (frame.tree.hasTag(LAMBDA)) { + lambdaContext.addSymbol(tree.sym, LOCAL_VAR); + } else { + break; + } } // Check for type variables (including as type arguments). // If they occur within class nested in a lambda, mark for erasure diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java index f11054d7302c3..8ccbe716b7876 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java @@ -142,25 +142,6 @@ private List collect(JCTree tree, List res) { return res.append(tree); } - /** - * If the type is not accessible from current context, try to figure out the - * sharpest accessible supertype. - * - * @param originalType type to sharpen - * @return sharped type - */ - Type sharpestAccessible(Type originalType) { - if (originalType.hasTag(ARRAY)) { - return types.makeArrayType(sharpestAccessible(types.elemtype(originalType))); - } - - Type type = originalType; - while (!rs.isAccessible(gen.getAttrEnv(), type.asElement())) { - type = types.supertype(type); - } - return type; - } - /** * "Legacy" bytecode flavor: emit the StringBuilder.append chains for string * concatenation. @@ -305,6 +286,14 @@ protected List> split(List args) { return splits.toList(); } + + /** + * Returns true if the argument should be converted to a string eagerly, to preserve + * possible side-effects. + */ + protected boolean shouldConvertToStringEagerly(Type argType) { + return !types.unboxedTypeOrType(argType).isPrimitive() && argType.tsym != syms.stringType.tsym; + } } /** @@ -333,14 +322,18 @@ protected void emit(JCDiagnostic.DiagnosticPosition pos, List args, bool for (JCTree arg : t) { Object constVal = arg.type.constValue(); if ("".equals(constVal)) continue; - if (arg.type == syms.botType) { - dynamicArgs.add(types.boxedClass(syms.voidType).type); - } else { - dynamicArgs.add(sharpestAccessible(arg.type)); + Type argType = arg.type; + if (argType == syms.botType) { + argType = types.boxedClass(syms.voidType).type; } if (!first || generateFirstArg) { gen.genExpr(arg, arg.type).load(); } + if (shouldConvertToStringEagerly(argType)) { + gen.callMethod(pos, syms.stringType, names.valueOf, List.of(syms.objectType), true); + argType = syms.stringType; + } + dynamicArgs.add(argType); first = false; } doCall(type, pos, dynamicArgs.toList()); @@ -439,10 +432,15 @@ protected void emit(JCDiagnostic.DiagnosticPosition pos, List args, bool } else { // Ordinary arguments come through the dynamic arguments. recipe.append(TAG_ARG); - dynamicArgs.add(sharpestAccessible(arg.type)); + Type argType = arg.type; if (!first || generateFirstArg) { gen.genExpr(arg, arg.type).load(); } + if (shouldConvertToStringEagerly(argType)) { + gen.callMethod(pos, syms.stringType, names.valueOf, List.of(syms.objectType), true); + argType = syms.stringType; + } + dynamicArgs.add(argType); first = false; } } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/Config.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/Config.java index 00fedc8d0e003..d383fa4498e02 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/Config.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/Config.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -127,6 +127,9 @@ private static void debug(Object o) { // whether to print debug info during startup private boolean showInfo = false; + // whether to allow legacy mechanisms + private boolean allowLegacy = false; + // template manager, initialized from parsed attributes private TemplateManager templateManager; @@ -257,6 +260,10 @@ boolean getShowInfo() { return (SunPKCS11.debug != null) || showInfo; } + boolean getAllowLegacy() { + return allowLegacy; + } + TemplateManager getTemplateManager() { if (templateManager == null) { templateManager = new TemplateManager(); @@ -450,6 +457,8 @@ private void parse() throws IOException { destroyTokenAfterLogout = parseBooleanEntry(word); } else if (word.equals("showInfo")) { showInfo = parseBooleanEntry(word); + } else if (word.equals("allowLegacy")) { + allowLegacy = parseBooleanEntry(word); } else if (word.equals("keyStoreCompatibilityMode")) { keyStoreCompatibilityMode = parseBooleanEntry(word); } else if (word.equals("explicitCancel")) { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java index eaac4f008c42f..26b4171d165cd 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -473,12 +473,25 @@ private void initialize() throws PKCS11Exception { if (session == null) { session = token.getOpSession(); } - if (encrypt) { - token.p11.C_EncryptInit(session.id(), mechWithParams, - p11KeyID); + + if (type == Transformation.AES_GCM) { + // JDK-8255409 allows using useNormativeMechFirst dependent on token.p11.getVersion(); + boolean useNormativeMechFirst = false; + if (encrypt) { + token.p11.C_GCMEncryptInitWithRetry(session.id(), mechWithParams, + p11KeyID, useNormativeMechFirst); + } else { + token.p11.C_GCMDecryptInitWithRetry(session.id(), mechWithParams, + p11KeyID, useNormativeMechFirst); + } } else { - token.p11.C_DecryptInit(session.id(), mechWithParams, - p11KeyID); + if (encrypt) { + token.p11.C_EncryptInit(session.id(), mechWithParams, + p11KeyID); + } else { + token.p11.C_DecryptInit(session.id(), mechWithParams, + p11KeyID); + } } } catch (PKCS11Exception e) { p11Key.releaseKeyID(); diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java index f2e8e4094c84e..cb40c3bf794f7 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java @@ -250,6 +250,19 @@ protected Object writeReplace() throws ObjectStreamException { return new KeyRep(type, getAlgorithm(), format, getEncodedInternal()); } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("P11Key not directly deserializable"); + } + public String toString() { token.ensureValid(); String s1 = token.provider.getName() + " " + algorithm + " " + type diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java index da8524e9465c9..41e6d344d4278 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -189,9 +189,23 @@ private void implNextBytes(byte[] bytes) { } } + /** + * Restores the state of this object from the stream. + * + * @param in the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); + if (token == null) { + throw new InvalidObjectException("token is null"); + } + if (mixBuffer != null) { + mixBuffer = mixBuffer.clone(); + } // assign default values to non-null transient fields iBuffer = new byte[IBUFFER_SIZE]; ibuffered = 0; diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java index 1855e5631bd1f..b5a30c6da4ea9 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -1267,25 +1267,6 @@ public Object run() { } } - private static boolean isLegacy(CK_MECHANISM_INFO mechInfo) - throws PKCS11Exception { - // assume full support if no mech info available - // For vendor-specific mechanisms, often no mech info is provided - boolean partialSupport = false; - - if (mechInfo != null) { - if ((mechInfo.flags & CKF_DECRYPT) != 0) { - // non-legacy cipher mechs should support encryption - partialSupport |= ((mechInfo.flags & CKF_ENCRYPT) == 0); - } - if ((mechInfo.flags & CKF_VERIFY) != 0) { - // non-legacy signature mechs should support signing - partialSupport |= ((mechInfo.flags & CKF_SIGN) == 0); - } - } - return partialSupport; - } - // test if a token is present and initialize this provider for it if so. // does nothing if no token is found // called from constructor and by poller @@ -1336,12 +1317,6 @@ private void initToken(CK_SLOT_INFO slotInfo) throws PKCS11Exception { } continue; } - if (isLegacy(mechInfo)) { - if (showInfo) { - System.out.println("DISABLED due to legacy"); - } - continue; - } // we do not know of mechs with the upper 32 bits set if (longMech >>> 32 != 0) { @@ -1356,6 +1331,7 @@ private void initToken(CK_SLOT_INFO slotInfo) throws PKCS11Exception { if (ds == null) { continue; } + boolean allowLegacy = config.getAllowLegacy(); descLoop: for (Descriptor d : ds) { Integer oldMech = supportedAlgs.get(d); @@ -1371,6 +1347,21 @@ private void initToken(CK_SLOT_INFO slotInfo) throws PKCS11Exception { } } } + + // assume full support if no mech info available + if (!allowLegacy && mechInfo != null) { + if ((d.type == CIP && + (mechInfo.flags & CKF_ENCRYPT) == 0) || + (d.type == SIG && + (mechInfo.flags & CKF_SIGN) == 0)) { + if (showInfo) { + System.out.println("DISABLED " + d.type + + " " + d.algorithm + + " due to partial support"); + } + continue; + } + } supportedAlgs.put(d, integerMech); continue; } @@ -1993,6 +1984,19 @@ private Object writeReplace() throws ObjectStreamException { return new SunPKCS11Rep(this); } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("SunPKCS11 not directly deserializable"); + } + /** * Serialized representation of the SunPKCS11 provider. */ diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java index a25fa1c39e5b7..d6c291ebc570c 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java @@ -127,11 +127,6 @@ public String toString() { sb.append(pPassword.length); sb.append(Constants.NEWLINE); - sb.append(Constants.INDENT); - sb.append("pPassword: "); - sb.append(pPassword); - sb.append(Constants.NEWLINE); - sb.append(Constants.INDENT); sb.append("ulSaltLen: "); sb.append(pSalt.length); diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java index d796aaa307506..4e6520e70a13c 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java @@ -782,6 +782,24 @@ public native long[] C_FindObjects(long hSession, long ulMaxObjectCount) public native void C_EncryptInit(long hSession, CK_MECHANISM pMechanism, long hKey) throws PKCS11Exception; + /** + * C_GCMEncryptInitWithRetry initializes a GCM encryption operation and retry + * with alternative param structure for max compatibility. + * (Encryption and decryption) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the encryption mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hKey the handle of the encryption key + * (PKCS#11 param: CK_OBJECT_HANDLE hKey) + * @param useNormativeVerFirst whether to use normative version of GCM parameter first + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_GCMEncryptInitWithRetry(long hSession, CK_MECHANISM pMechanism, + long hKey, boolean useNormativeVerFirst) throws PKCS11Exception; /** * C_Encrypt encrypts single-part data. @@ -877,6 +895,24 @@ public native int C_EncryptFinal(long hSession, long directOut, byte[] out, public native void C_DecryptInit(long hSession, CK_MECHANISM pMechanism, long hKey) throws PKCS11Exception; + /** + * C_GCMDecryptInitWithRetry initializes a GCM decryption operation + * with alternative param structure for max compatibility. + * (Encryption and decryption) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the decryption mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hKey the handle of the decryption key + * (PKCS#11 param: CK_OBJECT_HANDLE hKey) + * @param useNormativeVerFirst whether to use normative version of GCM parameter first + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_GCMDecryptInitWithRetry(long hSession, CK_MECHANISM pMechanism, + long hKey, boolean useNormativeVerFirst) throws PKCS11Exception; /** * C_Decrypt decrypts encrypted data in a single part. diff --git a/src/jdk.crypto.cryptoki/share/legal/pkcs11cryptotoken.md b/src/jdk.crypto.cryptoki/share/legal/pkcs11cryptotoken.md index 08d1e3c713d92..7877f54fe6e39 100644 --- a/src/jdk.crypto.cryptoki/share/legal/pkcs11cryptotoken.md +++ b/src/jdk.crypto.cryptoki/share/legal/pkcs11cryptotoken.md @@ -1,16 +1,16 @@ -## OASIS PKCS #11 Cryptographic Token Interface v3.0 +## OASIS PKCS #11 Cryptographic Token Interface v3.1 ### OASIS PKCS #11 Cryptographic Token Interface License

       
      -Copyright © OASIS Open 2020. All Rights Reserved.
      +Copyright © OASIS Open 2023. All Rights Reserved.
       
      -    All capitalized terms in the following text have the meanings
      +All capitalized terms in the following text have the meanings
       assigned to them in the OASIS Intellectual Property Rights Policy (the
       "OASIS IPR Policy"). The full Policy may be found at the OASIS website:
      -[http://www.oasis-open.org/policies-guidelines/ipr]
      +[https://www.oasis-open.org/policies-guidelines/ipr/].
       
      -    This document and translations of it may be copied and furnished to
      +This document and translations of it may be copied and furnished to
       others, and derivative works that comment on or otherwise explain it or
       assist in its implementation may be prepared, copied, published, and
       distributed, in whole or in part, without restriction of any kind,
      @@ -23,10 +23,10 @@ Committee (in which case the rules applicable to copyrights, as set
       forth in the OASIS IPR Policy, must be followed) or as required to
       translate it into languages other than English.
       
      -    The limited permissions granted above are perpetual and will not be
      +The limited permissions granted above are perpetual and will not be
       revoked by OASIS or its successors or assigns.
       
      -    This document and the information contained herein is provided on an
      +This document and the information contained herein is provided on an
       "AS IS" basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
       INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
       INFORMATION HEREIN WILL NOT INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED
      @@ -35,7 +35,11 @@ AND ITS MEMBERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
       CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THIS DOCUMENT OR ANY
       PART THEREOF.
       
      -    [OASIS requests that any OASIS Party or any other party that
      +As stated in the OASIS IPR Policy, the following three paragraphs in
      +brackets apply to OASIS Standards Final Deliverable documents (Committee
      +Specifications, OASIS Standards, or Approved Errata).
      +
      +[OASIS requests that any OASIS Party or any other party that
       believes it has patent claims that would necessarily be infringed by
       implementations of this OASIS Standards Final Deliverable, to notify
       OASIS TC Administrator and provide an indication of its willingness to
      @@ -43,7 +47,7 @@ grant patent licenses to such patent claims in a manner consistent with
       the IPR Mode of the OASIS Technical Committee that produced this
       deliverable.]
       
      -    [OASIS invites any party to contact the OASIS TC Administrator if it
      +[OASIS invites any party to contact the OASIS TC Administrator if it
       is aware of a claim of ownership of any patent claims that would
       necessarily be infringed by implementations of this OASIS Standards
       Final Deliverable by a patent holder that is not willing to provide a
      @@ -52,7 +56,7 @@ of the OASIS Technical Committee that produced this OASIS Standards
       Final Deliverable. OASIS may include such claims on its website, but
       disclaims any obligation to do so.]
       
      -    [OASIS takes no position regarding the validity or scope of any
      +[OASIS takes no position regarding the validity or scope of any
       intellectual property or other rights that might be claimed to pertain
       to the implementation or use of the technology described in this OASIS
       Standards Final Deliverable or the extent to which any license under
      diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c
      index e2de13648be47..26309f4f7b2d0 100644
      --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c
      +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
        */
       
       /* Copyright  (c) 2002 Graz University of Technology. All rights reserved.
      @@ -1014,18 +1014,19 @@ jAesCtrParamsToCKAesCtrParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength)
       }
       
       /*
      - * converts the Java CK_GCM_PARAMS object to a CK_GCM_PARAMS_NO_IVBITS pointer
      - * Note: Need to try NSS definition first to avoid SIGSEGV.
      + * converts the Java CK_GCM_PARAMS object to a CK_GCM_PARAMS pointer
      + * Note: Early NSS versions crash w/ CK_GCM_PARAMS and need to use
      + * CK_GCM_PARAMS_NO_IVBITS to avoid SIGSEGV.
        *
        * @param env - used to call JNI funktions to get the Java classes and objects
        * @param jParam - the Java CK_GCM_PARAMS object to convert
        * @param pLength - length of the allocated memory of the returned pointer
      - * @return pointer to the new CK_GCM_PARAMS_NO_IVBITS structure
      + * @return pointer to the new CK_GCM_PARAMS structure
        */
      -CK_GCM_PARAMS_NO_IVBITS_PTR
      +CK_GCM_PARAMS_PTR
       jGCMParamsToCKGCMParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength)
       {
      -    CK_GCM_PARAMS_NO_IVBITS_PTR ckParamPtr;
      +    CK_GCM_PARAMS_PTR ckParamPtr;
           jclass jGcmParamsClass;
           jfieldID fieldID;
           jobject jIv, jAad;
      @@ -1053,8 +1054,8 @@ jGCMParamsToCKGCMParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength)
           if (fieldID == NULL) { return NULL; }
           jTagLen = (*env)->GetLongField(env, jParam, fieldID);
       
      -    // allocate memory for CK_GCM_PARAMS_NO_IVBITS pointer
      -    ckParamPtr = calloc(1, sizeof(CK_GCM_PARAMS_NO_IVBITS));
      +    // allocate memory for CK_GCM_PARAMS pointer
      +    ckParamPtr = calloc(1, sizeof(CK_GCM_PARAMS));
           if (ckParamPtr == NULL) {
               throwOutOfMemoryError(env, 0);
               return NULL;
      @@ -1065,6 +1066,8 @@ jGCMParamsToCKGCMParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength)
           if ((*env)->ExceptionCheck(env)) {
               goto cleanup;
           }
      +    // adjust since the value is in bits
      +    ckParamPtr->ulIvBits = ckParamPtr->ulIvLen << 3;
       
           jByteArrayToCKByteArray(env, jAad, &(ckParamPtr->pAAD), &(ckParamPtr->ulAADLen));
           if ((*env)->ExceptionCheck(env)) {
      @@ -1074,9 +1077,9 @@ jGCMParamsToCKGCMParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength)
           ckParamPtr->ulTagBits = jLongToCKULong(jTagLen);
       
           if (pLength != NULL) {
      -        *pLength = sizeof(CK_GCM_PARAMS_NO_IVBITS);
      +        *pLength = sizeof(CK_GCM_PARAMS);
           }
      -    TRACE1("Created inner GCM_PARAMS PTR w/o ulIvBits %p\n", ckParamPtr);
      +    TRACE1("Created inner GCM_PARAMS PTR %p\n", ckParamPtr);
           return ckParamPtr;
       cleanup:
           free(ckParamPtr->pIv);
      diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c
      index 6d8cca4da8374..d969fabffd0c5 100644
      --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c
      +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
        */
       
       /* Copyright  (c) 2002 Graz University of Technology. All rights reserved.
      @@ -72,7 +72,6 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptInit
       {
           CK_SESSION_HANDLE ckSessionHandle;
           CK_MECHANISM_PTR ckpMechanism = NULL;
      -    CK_MECHANISM_PTR ckpTemp;
           CK_OBJECT_HANDLE ckKeyHandle;
           CK_RV rv;
       
      @@ -90,20 +89,60 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptInit
           rv = (*ckpFunctions->C_EncryptInit)(ckSessionHandle, ckpMechanism,
                                               ckKeyHandle);
       
      -    if (ckpMechanism->mechanism == CKM_AES_GCM) {
      -        if (rv == CKR_ARGUMENTS_BAD || rv == CKR_MECHANISM_PARAM_INVALID) {
      -            // retry with CKM_GCM_PARAMS structure in pkcs11t.h
      -            TRACE0("DEBUG C_EncryptInit: retry with CK_GCM_PARAMS\n");
      -            ckpTemp = updateGCMParams(env, ckpMechanism);
      -            if (ckpTemp != NULL) { // only re-call if conversion succeeds
      -                ckpMechanism = ckpTemp;
      -                rv = (*ckpFunctions->C_EncryptInit)(ckSessionHandle, ckpMechanism,
      -                        ckKeyHandle);
      -            }
      +    TRACE1("DEBUG C_EncryptInit: freed pMech = %p\n", ckpMechanism);
      +    freeCKMechanismPtr(ckpMechanism);
      +    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; }
      +
      +    TRACE0("FINISHED\n");
      +}
      +
      +/*
      + * Class:     sun_security_pkcs11_wrapper_PKCS11
      + * Method:    C_GCMEncryptInitWithRetry
      + * Signature: (JLsun/security/pkcs11/wrapper/CK_MECHANISM;JZ)V
      + * Parametermapping:                    *PKCS11*
      + * @param   jlong jSessionHandle        CK_SESSION_HANDLE hSession
      + * @param   jobject jMechanism          CK_MECHANISM_PTR pMechanism
      + * @param   jlong jKeyHandle            CK_OBJECT_HANDLE hKey
      + * @param   jboolean useNormVerFirst    CK_BBOOL retry (only retry if the first
      + *                                      init uses the non-normative version)
      + */
      +JNIEXPORT void JNICALL
      +Java_sun_security_pkcs11_wrapper_PKCS11_C_1GCMEncryptInitWithRetry
      +(JNIEnv *env, jobject obj, jlong jSessionHandle,
      + jobject jMechanism, jlong jKeyHandle, jboolean useNormVerFirst)
      +{
      +    CK_SESSION_HANDLE ckSessionHandle;
      +    CK_MECHANISM_PTR ckpMechanism = NULL;
      +    CK_OBJECT_HANDLE ckKeyHandle;
      +    CK_BBOOL retry = FALSE;
      +    CK_RV rv = 1;
      +
      +    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
      +    if (ckpFunctions == NULL) { return; }
      +
      +    ckSessionHandle = jLongToCKULong(jSessionHandle);
      +    ckKeyHandle = jLongToCKULong(jKeyHandle);
      +    ckpMechanism = jMechanismToCKMechanismPtr(env, jMechanism);
      +
      +    if ((*env)->ExceptionCheck(env)) { return; }
      +
      +    // if !useNormVerFirst, then update 'ckpMechanism' in place w/
      +    // non-normative GCM params.
      +    retry = (!useNormVerFirst && updateGCMParams(env, ckpMechanism) != NULL);
      +
      +    rv = (*ckpFunctions->C_EncryptInit)(ckSessionHandle, ckpMechanism, ckKeyHandle);
      +
      +    if (rv == CKR_ARGUMENTS_BAD || rv == CKR_MECHANISM_PARAM_INVALID) {
      +        // retry and update 'ckpMechanism' in place w/ normative GCM params.
      +        if (retry && updateGCMParams(env, ckpMechanism) != NULL) {
      +            TRACE0("DEBUG retry C_EncryptInit\n");
      +            rv = (*ckpFunctions->C_EncryptInit)(ckSessionHandle,
      +                ckpMechanism, ckKeyHandle);
               }
           }
       
      -    TRACE1("DEBUG C_EncryptInit: freed pMech = %p\n", ckpMechanism);
      +    TRACE1("DEBUG C_GCMEncryptInitWithRetry: freed pMech = %p\n", ckpMechanism);
           freeCKMechanismPtr(ckpMechanism);
           if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; }
       
      @@ -312,7 +351,6 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptInit
       {
           CK_SESSION_HANDLE ckSessionHandle;
           CK_MECHANISM_PTR ckpMechanism = NULL;
      -    CK_MECHANISM_PTR ckpTemp;
           CK_OBJECT_HANDLE ckKeyHandle;
           CK_RV rv;
       
      @@ -330,20 +368,61 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptInit
           rv = (*ckpFunctions->C_DecryptInit)(ckSessionHandle, ckpMechanism,
                                               ckKeyHandle);
       
      -    if (ckpMechanism->mechanism == CKM_AES_GCM) {
      -        if (rv == CKR_ARGUMENTS_BAD || rv == CKR_MECHANISM_PARAM_INVALID) {
      -            // retry with CKM_GCM_PARAMS structure in pkcs11t.h
      -            TRACE0("DEBUG C_DecryptInit: retry with CK_GCM_PARAMS\n");
      -            ckpTemp = updateGCMParams(env, ckpMechanism);
      -            if (ckpTemp != NULL) { // only re-call if conversion succeeds
      -                ckpMechanism = ckpTemp;
      -                rv = (*ckpFunctions->C_DecryptInit)(ckSessionHandle, ckpMechanism,
      -                        ckKeyHandle);
      -            }
      +    TRACE1("DEBUG C_DecryptInit: freed pMech = %p\n", ckpMechanism);
      +    freeCKMechanismPtr(ckpMechanism);
      +    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; }
      +
      +    TRACE0("FINISHED\n");
      +}
      +
      +/*
      + * Class:     sun_security_pkcs11_wrapper_PKCS11
      + * Method:    C_GCMDecryptInitWithRetry
      + * Signature: (JLsun/security/pkcs11/wrapper/CK_MECHANISM;JZ)V
      + * Parametermapping:                    *PKCS11*
      + * @param   jlong jSessionHandle        CK_SESSION_HANDLE hSession
      + * @param   jobject jMechanism          CK_MECHANISM_PTR pMechanism
      + * @param   jlong jKeyHandle            CK_OBJECT_HANDLE hKey
      + * @param   jboolean useNormVerFirst    CK_BBOOL retry (only retry if the first
      + *                                      init uses the non-normative version)
      + */
      +JNIEXPORT void JNICALL
      +Java_sun_security_pkcs11_wrapper_PKCS11_C_1GCMDecryptInitWithRetry
      +(JNIEnv *env, jobject obj, jlong jSessionHandle,
      + jobject jMechanism, jlong jKeyHandle, jboolean useNormVerFirst)
      +{
      +    CK_SESSION_HANDLE ckSessionHandle;
      +    CK_MECHANISM_PTR ckpMechanism = NULL;
      +    CK_OBJECT_HANDLE ckKeyHandle;
      +    CK_BBOOL retry = FALSE;
      +    CK_RV rv = 1;
      +
      +    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
      +    if (ckpFunctions == NULL) { return; }
      +
      +    ckSessionHandle = jLongToCKULong(jSessionHandle);
      +    ckKeyHandle = jLongToCKULong(jKeyHandle);
      +    ckpMechanism = jMechanismToCKMechanismPtr(env, jMechanism);
      +
      +    if ((*env)->ExceptionCheck(env)) { return; }
      +
      +    // if !useNormVerFirst, then update 'ckpMechanism' in place w/
      +    // non-normative GCM params.
      +    retry = (!useNormVerFirst && updateGCMParams(env, ckpMechanism) != NULL);
      +
      +    rv = (*ckpFunctions->C_DecryptInit)(ckSessionHandle, ckpMechanism,
      +        ckKeyHandle);
      +
      +    if (rv == CKR_ARGUMENTS_BAD || rv == CKR_MECHANISM_PARAM_INVALID) {
      +        // retry and update 'ckpMechanism' in place w/ normative GCM params.
      +        if (retry && updateGCMParams(env, ckpMechanism) != NULL) {
      +            TRACE0("DEBUG retry C_DecryptInit with normative CK_GCM_PARAMS\n");
      +            rv = (*ckpFunctions->C_DecryptInit)(ckSessionHandle, ckpMechanism,
      +                ckKeyHandle);
               }
           }
       
      -    TRACE1("DEBUG C_DecryptInit: freed pMech = %p\n", ckpMechanism);
      +    TRACE1("DEBUG C_GCMDecryptInitWithRetry: freed pMech = %p\n", ckpMechanism);
           freeCKMechanismPtr(ckpMechanism);
           if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; }
       
      diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c
      index aa76945283d74..0f49657ada1f6 100644
      --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c
      +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
        */
       
       /* Copyright  (c) 2002 Graz University of Technology. All rights reserved.
      @@ -329,14 +329,14 @@ void freeCKMechanismPtr(CK_MECHANISM_PTR mechPtr) {
                    tmp = mechPtr->pParameter;
                    switch (mechPtr->mechanism) {
                        case CKM_AES_GCM:
      -                     if (mechPtr->ulParameterLen == sizeof(CK_GCM_PARAMS_NO_IVBITS)) {
      -                         TRACE0("[ GCM_PARAMS w/o ulIvBits ]\n");
      -                         free(((CK_GCM_PARAMS_NO_IVBITS*)tmp)->pIv);
      -                         free(((CK_GCM_PARAMS_NO_IVBITS*)tmp)->pAAD);
      -                     } else if (mechPtr->ulParameterLen == sizeof(CK_GCM_PARAMS)) {
      +                     if (mechPtr->ulParameterLen == sizeof(CK_GCM_PARAMS)) {
                                TRACE0("[ GCM_PARAMS ]\n");
                                free(((CK_GCM_PARAMS*)tmp)->pIv);
                                free(((CK_GCM_PARAMS*)tmp)->pAAD);
      +                     } else if (mechPtr->ulParameterLen == sizeof(CK_GCM_PARAMS_NO_IVBITS)) {
      +                         TRACE0("[ GCM_PARAMS w/o ulIvBits ]\n");
      +                         free(((CK_GCM_PARAMS_NO_IVBITS*)tmp)->pIv);
      +                         free(((CK_GCM_PARAMS_NO_IVBITS*)tmp)->pAAD);
                            }
                            break;
                        case CKM_AES_CCM:
      @@ -448,43 +448,54 @@ void freeCKMechanismPtr(CK_MECHANISM_PTR mechPtr) {
            }
       }
       
      -/* This function replaces the CK_GCM_PARAMS_NO_IVBITS structure associated
      - * with the specified CK_MECHANISM structure with CK_GCM_PARAMS
      - * structure.
      +/* This function updates the specified CK_MECHANISM structure
      + * and its GCM parameter structure switching between CK_GCM_PARAMS and
      + * CK_GCM_PARAMS_NO_IVBITS.
        *
        * @param mechPtr pointer to the CK_MECHANISM structure containing
      - * the to-be-converted CK_GCM_PARAMS_NO_IVBITS structure.
      + * the to-be-converted CK_GCM_PARAMS / CK_GCM_PARAMS_NO_IVBITS structure.
        * @return pointer to the CK_MECHANISM structure containing the
      - * converted CK_GCM_PARAMS structure or NULL if no conversion took place.
      + * converted structure or NULL if no conversion is done.
        */
       CK_MECHANISM_PTR updateGCMParams(JNIEnv *env, CK_MECHANISM_PTR mechPtr) {
      -    CK_GCM_PARAMS* pGcmParams2 = NULL;
      -    CK_GCM_PARAMS_NO_IVBITS* pParams = NULL;
      -    if ((mechPtr->mechanism == CKM_AES_GCM) &&
      -            (mechPtr->pParameter != NULL_PTR) &&
      -            (mechPtr->ulParameterLen == sizeof(CK_GCM_PARAMS_NO_IVBITS))) {
      -        pGcmParams2 = calloc(1, sizeof(CK_GCM_PARAMS));
      -        if (pGcmParams2 == NULL) {
      -            throwOutOfMemoryError(env, 0);
      -            return NULL;
      +    CK_GCM_PARAMS_PTR pParams;
      +    CK_GCM_PARAMS_NO_IVBITS_PTR pParamsNoIvBits;
      +    CK_ULONG paramLen;
      +
      +    if (mechPtr != NULL) {
      +        paramLen = mechPtr->ulParameterLen;
      +        if (paramLen == sizeof(CK_GCM_PARAMS)) {
      +            // CK_GCM_PARAMS => CK_GCM_PARAMS_NO_IVBITS
      +            pParams = (CK_GCM_PARAMS*) mechPtr->pParameter;
      +            pParamsNoIvBits = calloc(1, sizeof(CK_GCM_PARAMS_NO_IVBITS));
      +            pParamsNoIvBits->pIv = pParams->pIv;
      +            pParamsNoIvBits->ulIvLen = pParams->ulIvLen;
      +            pParamsNoIvBits->pAAD = pParams->pAAD;
      +            pParamsNoIvBits->ulAADLen = pParams->ulAADLen;
      +            pParamsNoIvBits->ulTagBits = pParams->ulTagBits;
      +            mechPtr->pParameter = pParamsNoIvBits;
      +            mechPtr->ulParameterLen = sizeof(CK_GCM_PARAMS_NO_IVBITS);
      +            free(pParams);
      +            TRACE0("DEBUG update CK_GCM_PARAMS to CK_GCM_PARAMS_NO_IVBITS\n");
      +            return mechPtr;
      +        } else if (paramLen == sizeof(CK_GCM_PARAMS_NO_IVBITS)) {
      +            // CK_GCM_PARAMS_NO_IVBITS => CK_GCM_PARAMS
      +            pParamsNoIvBits = (CK_GCM_PARAMS_NO_IVBITS*) mechPtr->pParameter;
      +            pParams = calloc(1, sizeof(CK_GCM_PARAMS));
      +            pParams->pIv = pParamsNoIvBits->pIv;
      +            pParams->ulIvLen = pParamsNoIvBits->ulIvLen;
      +            pParams->ulIvBits = pParamsNoIvBits->ulIvLen << 3;
      +            pParams->pAAD = pParamsNoIvBits->pAAD;
      +            pParams->ulAADLen = pParamsNoIvBits->ulAADLen;
      +            pParams->ulTagBits = pParamsNoIvBits->ulTagBits;
      +            mechPtr->pParameter = pParams;
      +            mechPtr->ulParameterLen = sizeof(CK_GCM_PARAMS);
      +            free(pParamsNoIvBits);
      +            TRACE0("DEBUG update CK_GCM_PARAMS_NO_IVBITS to CK_GCM_PARAMS\n");
      +            return mechPtr;
               }
      -        pParams = (CK_GCM_PARAMS_NO_IVBITS*) mechPtr->pParameter;
      -        pGcmParams2->pIv = pParams->pIv;
      -        pGcmParams2->ulIvLen = pParams->ulIvLen;
      -        pGcmParams2->ulIvBits = (pGcmParams2->ulIvLen << 3);
      -        pGcmParams2->pAAD = pParams->pAAD;
      -        pGcmParams2->ulAADLen = pParams->ulAADLen;
      -        pGcmParams2->ulTagBits = pParams->ulTagBits;
      -        TRACE1("DEBUG updateGCMParams: pMech %p\n", mechPtr);
      -        TRACE2("\t=> GCM param w/o ulIvBits %p => GCM param %p\n", pParams,
      -                pGcmParams2);
      -        free(pParams);
      -        mechPtr->pParameter = pGcmParams2;
      -        mechPtr->ulParameterLen = sizeof(CK_GCM_PARAMS);
      -        return mechPtr;
      -    } else {
      -        TRACE0("DEBUG updateGCMParams: no conversion done\n");
           }
      +    TRACE0("DEBUG updateGCMParams: no conversion done\n");
           return NULL;
       }
       
      diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11.h b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11.h
      index 5b050def5205f..5933da0e3b75c 100644
      --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11.h
      +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11.h
      @@ -1,8 +1,11 @@
      -/* Copyright (c) OASIS Open 2016-2019. All Rights Reserved.
      - * Distributed under the terms of the OASIS IPR Policy,
      - * [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
      - * IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
      - * PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
      +/*
      + * PKCS #11 Specification Version 3.1
      + * OASIS Standard
      + * 23 July 2023
      + * Copyright (c) OASIS Open 2023. All Rights Reserved.
      + * Source: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/os/include/pkcs11-v3.1/
      + * Latest stage of narrative specification: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/pkcs11-spec-v3.1.html
      + * TC IPR Statement: https://www.oasis-open.org/committees/pkcs11/ipr.php
        */
       
       #ifndef _PKCS11_H_
      @@ -47,7 +50,7 @@ extern "C" {
        *
        * typedef CK_BYTE CK_PTR CK_BYTE_PTR;
        *
      - * If you're using windows, it might be defined by:
      + * If you're using Windows, it might be defined by:
        *
        * #define CK_PTR *
        *
      @@ -65,7 +68,7 @@ extern "C" {
        *   CK_VOID_PTR pReserved
        * );
        *
      - * If you're using Windows to declare a function in a Win32 cryptoki .dll,
      + * If you're using Windows to declare a function in a Win32 Cryptoki .dll,
        * it might be defined by:
        *
        * #define CK_DECLARE_FUNCTION(returnType, name) \
      @@ -241,4 +244,3 @@ struct CK_FUNCTION_LIST {
       
       #endif /* _PKCS11_H_ */
       
      -
      diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11f.h b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11f.h
      index 0553c1dc73ca2..80c43400d05f8 100644
      --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11f.h
      +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11f.h
      @@ -1,12 +1,11 @@
      -/* Copyright (c) OASIS Open 2016, 2019. All Rights Reserved./
      - * /Distributed under the terms of the OASIS IPR Policy,
      - * [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
      - * IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
      - * PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
      - */
      -
      -/* Latest version of the specification:
      - * http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
      +/*
      + * PKCS #11 Specification Version 3.1
      + * OASIS Standard
      + * 23 July 2023
      + * Copyright (c) OASIS Open 2023. All Rights Reserved.
      + * Source: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/os/include/pkcs11-v3.1/
      + * Latest stage of narrative specification: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/pkcs11-spec-v3.1.html
      + * TC IPR Statement: https://www.oasis-open.org/committees/pkcs11/ipr.php
        */
       
       /* This header file contains pretty much everything about all the
      diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h
      index ab6ef326e8bd6..79d7cf7d7dae7 100644
      --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h
      +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h
      @@ -1,12 +1,11 @@
      -/* Copyright (c) OASIS Open 2016, 2019. All Rights Reserved./
      - * /Distributed under the terms of the OASIS IPR Policy,
      - * [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
      - * IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
      - * PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
      - */
      -
      -/* Latest version of the specification:
      - * http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
      +/*
      + * PKCS #11 Specification Version 3.1
      + * OASIS Standard
      + * 23 July 2023
      + * Copyright (c) OASIS Open 2023. All Rights Reserved.
      + * Source: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/os/include/pkcs11-v3.1/
      + * Latest stage of narrative specification: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/pkcs11-spec-v3.1.html
      + * TC IPR Statement: https://www.oasis-open.org/committees/pkcs11/ipr.php
        */
       
       /* See top of pkcs11.h for information about the macros that
      @@ -18,7 +17,7 @@
       #define _PKCS11T_H_ 1
       
       #define CRYPTOKI_VERSION_MAJOR          3
      -#define CRYPTOKI_VERSION_MINOR          0
      +#define CRYPTOKI_VERSION_MINOR          1
       #define CRYPTOKI_VERSION_AMENDMENT      0
       
       #define CK_TRUE         1
      @@ -329,8 +328,11 @@ typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR;
       #define CKP_EXTENDED_PROVIDER         0x00000002UL
       #define CKP_AUTHENTICATION_TOKEN      0x00000003UL
       #define CKP_PUBLIC_CERTIFICATES_TOKEN 0x00000004UL
      +#define CKP_COMPLETE_PROVIDER         0x00000005UL
      +#define CKP_HKDF_TLS_TOKEN            0x00000006UL
       #define CKP_VENDOR_DEFINED            0x80000000UL
       
      +
       /* CK_HW_FEATURE_TYPE is a value that identifies the hardware feature type
        * of an object with CK_OBJECT_CLASS equal to CKO_HW_FEATURE.
        */
      @@ -409,9 +411,11 @@ typedef CK_ULONG          CK_KEY_TYPE;
       #define CKK_EC_EDWARDS          0x00000040UL
       #define CKK_EC_MONTGOMERY       0x00000041UL
       #define CKK_HKDF                0x00000042UL
      +
       #define CKK_SHA512_224_HMAC     0x00000043UL
       #define CKK_SHA512_256_HMAC     0x00000044UL
       #define CKK_SHA512_T_HMAC       0x00000045UL
      +#define CKK_HSS                 0x00000046UL
       
       #define CKK_VENDOR_DEFINED      0x80000000UL
       
      @@ -481,9 +485,9 @@ typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
       #define CKA_CERTIFICATE_CATEGORY        0x00000087UL
       #define CKA_JAVA_MIDP_SECURITY_DOMAIN   0x00000088UL
       #define CKA_URL                         0x00000089UL
      -#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY  0x0000008AUL
      -#define CKA_HASH_OF_ISSUER_PUBLIC_KEY   0x0000008BUL
      -#define CKA_NAME_HASH_ALGORITHM         0x0000008CUL
      +#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY  0x0000008aUL
      +#define CKA_HASH_OF_ISSUER_PUBLIC_KEY   0x0000008bUL
      +#define CKA_NAME_HASH_ALGORITHM         0x0000008cUL
       #define CKA_CHECK_VALUE                 0x00000090UL
       
       #define CKA_KEY_TYPE           0x00000100UL
      @@ -496,9 +500,9 @@ typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
       #define CKA_UNWRAP             0x00000107UL
       #define CKA_SIGN               0x00000108UL
       #define CKA_SIGN_RECOVER       0x00000109UL
      -#define CKA_VERIFY             0x0000010AUL
      -#define CKA_VERIFY_RECOVER     0x0000010BUL
      -#define CKA_DERIVE             0x0000010CUL
      +#define CKA_VERIFY             0x0000010aUL
      +#define CKA_VERIFY_RECOVER     0x0000010bUL
      +#define CKA_DERIVE             0x0000010cUL
       #define CKA_START_DATE         0x00000110UL
       #define CKA_END_DATE           0x00000111UL
       #define CKA_MODULUS            0x00000120UL
      @@ -555,12 +559,12 @@ typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
       #define CKA_OTP_TIME_REQUIREMENT      0x00000225UL
       #define CKA_OTP_COUNTER_REQUIREMENT   0x00000226UL
       #define CKA_OTP_PIN_REQUIREMENT       0x00000227UL
      -#define CKA_OTP_COUNTER               0x0000022EUL
      -#define CKA_OTP_TIME                  0x0000022FUL
      -#define CKA_OTP_USER_IDENTIFIER       0x0000022AUL
      -#define CKA_OTP_SERVICE_IDENTIFIER    0x0000022BUL
      -#define CKA_OTP_SERVICE_LOGO          0x0000022CUL
      -#define CKA_OTP_SERVICE_LOGO_TYPE     0x0000022DUL
      +#define CKA_OTP_COUNTER               0x0000022eUL
      +#define CKA_OTP_TIME                  0x0000022fUL
      +#define CKA_OTP_USER_IDENTIFIER       0x0000022aUL
      +#define CKA_OTP_SERVICE_IDENTIFIER    0x0000022bUL
      +#define CKA_OTP_SERVICE_LOGO          0x0000022cUL
      +#define CKA_OTP_SERVICE_LOGO_TYPE     0x0000022dUL
       
       #define CKA_GOSTR3410_PARAMS            0x00000250UL
       #define CKA_GOSTR3411_PARAMS            0x00000251UL
      @@ -586,6 +590,7 @@ typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
       #define CKA_SUPPORTED_CMS_ATTRIBUTES    0x00000503UL
       #define CKA_ALLOWED_MECHANISMS          (CKF_ARRAY_ATTRIBUTE|0x00000600UL)
       #define CKA_PROFILE_ID                  0x00000601UL
      +
       #define CKA_X2RATCHET_BAG               0x00000602UL
       #define CKA_X2RATCHET_BAGSIZE           0x00000603UL
       #define CKA_X2RATCHET_BOBS1STMSG        0x00000604UL
      @@ -603,6 +608,13 @@ typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
       #define CKA_X2RATCHET_NS                0x00000610UL
       #define CKA_X2RATCHET_PNS               0x00000611UL
       #define CKA_X2RATCHET_RK                0x00000612UL
      +/* HSS */
      +#define CKA_HSS_LEVELS                  0x00000617UL
      +#define CKA_HSS_LMS_TYPE                0x00000618UL
      +#define CKA_HSS_LMOTS_TYPE              0x00000619UL
      +#define CKA_HSS_LMS_TYPES               0x0000061aUL
      +#define CKA_HSS_LMOTS_TYPES             0x0000061bUL
      +#define CKA_HSS_KEYS_REMAINING          0x0000061cUL
       
       #define CKA_VENDOR_DEFINED              0x80000000UL
       
      @@ -644,11 +656,11 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_RIPEMD160_RSA_PKCS         0x00000008UL
       #define CKM_RSA_PKCS_OAEP              0x00000009UL
       
      -#define CKM_RSA_X9_31_KEY_PAIR_GEN     0x0000000AUL
      -#define CKM_RSA_X9_31                  0x0000000BUL
      -#define CKM_SHA1_RSA_X9_31             0x0000000CUL
      -#define CKM_RSA_PKCS_PSS               0x0000000DUL
      -#define CKM_SHA1_RSA_PKCS_PSS          0x0000000EUL
      +#define CKM_RSA_X9_31_KEY_PAIR_GEN     0x0000000aUL
      +#define CKM_RSA_X9_31                  0x0000000bUL
      +#define CKM_SHA1_RSA_X9_31             0x0000000cUL
      +#define CKM_RSA_PKCS_PSS               0x0000000dUL
      +#define CKM_SHA1_RSA_PKCS_PSS          0x0000000eUL
       
       #define CKM_DSA_KEY_PAIR_GEN           0x00000010UL
       #define CKM_DSA                        0x00000011UL
      @@ -659,8 +671,8 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_DSA_SHA512                 0x00000016UL
       #define CKM_DSA_SHA3_224               0x00000018UL
       #define CKM_DSA_SHA3_256               0x00000019UL
      -#define CKM_DSA_SHA3_384               0x0000001AUL
      -#define CKM_DSA_SHA3_512               0x0000001BUL
      +#define CKM_DSA_SHA3_384               0x0000001aUL
      +#define CKM_DSA_SHA3_512               0x0000001bUL
       
       #define CKM_DH_PKCS_KEY_PAIR_GEN       0x00000020UL
       #define CKM_DH_PKCS_DERIVE             0x00000021UL
      @@ -682,12 +694,12 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       
       #define CKM_SHA512_224                 0x00000048UL
       #define CKM_SHA512_224_HMAC            0x00000049UL
      -#define CKM_SHA512_224_HMAC_GENERAL    0x0000004AUL
      -#define CKM_SHA512_224_KEY_DERIVATION  0x0000004BUL
      -#define CKM_SHA512_256                 0x0000004CUL
      -#define CKM_SHA512_256_HMAC            0x0000004DUL
      -#define CKM_SHA512_256_HMAC_GENERAL    0x0000004EUL
      -#define CKM_SHA512_256_KEY_DERIVATION  0x0000004FUL
      +#define CKM_SHA512_224_HMAC_GENERAL    0x0000004aUL
      +#define CKM_SHA512_224_KEY_DERIVATION  0x0000004bUL
      +#define CKM_SHA512_256                 0x0000004cUL
      +#define CKM_SHA512_256_HMAC            0x0000004dUL
      +#define CKM_SHA512_256_HMAC_GENERAL    0x0000004eUL
      +#define CKM_SHA512_256_KEY_DERIVATION  0x0000004fUL
       
       #define CKM_SHA512_T                   0x00000050UL
       #define CKM_SHA512_T_HMAC              0x00000051UL
      @@ -781,25 +793,25 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_SECURID                    0x00000282UL
       #define CKM_HOTP_KEY_GEN               0x00000290UL
       #define CKM_HOTP                       0x00000291UL
      -#define CKM_ACTI                       0x000002A0UL
      -#define CKM_ACTI_KEY_GEN               0x000002A1UL
      -
      -#define CKM_SHA3_256                   0x000002B0UL
      -#define CKM_SHA3_256_HMAC              0x000002B1UL
      -#define CKM_SHA3_256_HMAC_GENERAL      0x000002B2UL
      -#define CKM_SHA3_256_KEY_GEN           0x000002B3UL
      -#define CKM_SHA3_224                   0x000002B5UL
      -#define CKM_SHA3_224_HMAC              0x000002B6UL
      -#define CKM_SHA3_224_HMAC_GENERAL      0x000002B7UL
      -#define CKM_SHA3_224_KEY_GEN           0x000002B8UL
      -#define CKM_SHA3_384                   0x000002C0UL
      -#define CKM_SHA3_384_HMAC              0x000002C1UL
      -#define CKM_SHA3_384_HMAC_GENERAL      0x000002C2UL
      -#define CKM_SHA3_384_KEY_GEN           0x000002C3UL
      -#define CKM_SHA3_512                   0x000002D0UL
      -#define CKM_SHA3_512_HMAC              0x000002D1UL
      -#define CKM_SHA3_512_HMAC_GENERAL      0x000002D2UL
      -#define CKM_SHA3_512_KEY_GEN           0x000002D3UL
      +#define CKM_ACTI                       0x000002a0UL
      +#define CKM_ACTI_KEY_GEN               0x000002a1UL
      +
      +#define CKM_SHA3_256                   0x000002b0UL
      +#define CKM_SHA3_256_HMAC              0x000002b1UL
      +#define CKM_SHA3_256_HMAC_GENERAL      0x000002b2UL
      +#define CKM_SHA3_256_KEY_GEN           0x000002b3UL
      +#define CKM_SHA3_224                   0x000002b5UL
      +#define CKM_SHA3_224_HMAC              0x000002b6UL
      +#define CKM_SHA3_224_HMAC_GENERAL      0x000002b7UL
      +#define CKM_SHA3_224_KEY_GEN           0x000002b8UL
      +#define CKM_SHA3_384                   0x000002c0UL
      +#define CKM_SHA3_384_HMAC              0x000002c1UL
      +#define CKM_SHA3_384_HMAC_GENERAL      0x000002c2UL
      +#define CKM_SHA3_384_KEY_GEN           0x000002c3UL
      +#define CKM_SHA3_512                   0x000002d0UL
      +#define CKM_SHA3_512_HMAC              0x000002d1UL
      +#define CKM_SHA3_512_HMAC_GENERAL      0x000002d2UL
      +#define CKM_SHA3_512_KEY_GEN           0x000002d3UL
       
       
       #define CKM_CAST_KEY_GEN               0x00000300UL
      @@ -870,9 +882,9 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_SHA3_256_KEY_DERIVATION    0x00000397UL
       #define CKM_SHA3_224_KEY_DERIVATION    0x00000398UL
       #define CKM_SHA3_384_KEY_DERIVATION    0x00000399UL
      -#define CKM_SHA3_512_KEY_DERIVATION    0x0000039AUL
      -#define CKM_SHAKE_128_KEY_DERIVATION   0x0000039BUL
      -#define CKM_SHAKE_256_KEY_DERIVATION   0x0000039CUL
      +#define CKM_SHA3_512_KEY_DERIVATION    0x0000039aUL
      +#define CKM_SHAKE_128_KEY_DERIVATION   0x0000039bUL
      +#define CKM_SHAKE_256_KEY_DERIVATION   0x0000039cUL
       #define CKM_SHA3_256_KEY_DERIVE  CKM_SHA3_256_KEY_DERIVATION
       #define CKM_SHA3_224_KEY_DERIVE  CKM_SHA3_224_KEY_DERIVATION
       #define CKM_SHA3_384_KEY_DERIVE  CKM_SHA3_384_KEY_DERIVATION
      @@ -880,40 +892,42 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_SHAKE_128_KEY_DERIVE CKM_SHAKE_128_KEY_DERIVATION
       #define CKM_SHAKE_256_KEY_DERIVE CKM_SHAKE_256_KEY_DERIVATION
       
      -#define CKM_PBE_MD2_DES_CBC            0x000003A0UL
      -#define CKM_PBE_MD5_DES_CBC            0x000003A1UL
      -#define CKM_PBE_MD5_CAST_CBC           0x000003A2UL
      -#define CKM_PBE_MD5_CAST3_CBC          0x000003A3UL
      -#define CKM_PBE_MD5_CAST5_CBC          0x000003A4UL /* Deprecated */
      -#define CKM_PBE_MD5_CAST128_CBC        0x000003A4UL
      -#define CKM_PBE_SHA1_CAST5_CBC         0x000003A5UL /* Deprecated */
      -#define CKM_PBE_SHA1_CAST128_CBC       0x000003A5UL
      -#define CKM_PBE_SHA1_RC4_128           0x000003A6UL
      -#define CKM_PBE_SHA1_RC4_40            0x000003A7UL
      -#define CKM_PBE_SHA1_DES3_EDE_CBC      0x000003A8UL
      -#define CKM_PBE_SHA1_DES2_EDE_CBC      0x000003A9UL
      -#define CKM_PBE_SHA1_RC2_128_CBC       0x000003AAUL
      -#define CKM_PBE_SHA1_RC2_40_CBC        0x000003ABUL
      -
      -#define CKM_PKCS5_PBKD2                0x000003B0UL
      -
      -#define CKM_PBA_SHA1_WITH_SHA1_HMAC    0x000003C0UL
      -
      -#define CKM_WTLS_PRE_MASTER_KEY_GEN         0x000003D0UL
      -#define CKM_WTLS_MASTER_KEY_DERIVE          0x000003D1UL
      -#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC   0x000003D2UL
      -#define CKM_WTLS_PRF                        0x000003D3UL
      -#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE  0x000003D4UL
      -#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE  0x000003D5UL
      -
      -#define CKM_TLS12_MAC                       0x000003D8UL
      -#define CKM_TLS12_KDF                       0x000003D9UL
      -#define CKM_TLS12_MASTER_KEY_DERIVE         0x000003E0UL
      -#define CKM_TLS12_KEY_AND_MAC_DERIVE        0x000003E1UL
      -#define CKM_TLS12_MASTER_KEY_DERIVE_DH      0x000003E2UL
      -#define CKM_TLS12_KEY_SAFE_DERIVE           0x000003E3UL
      -#define CKM_TLS_MAC                         0x000003E4UL
      -#define CKM_TLS_KDF                         0x000003E5UL
      +#define CKM_PBE_MD2_DES_CBC            0x000003a0UL
      +#define CKM_PBE_MD5_DES_CBC            0x000003a1UL
      +#define CKM_PBE_MD5_CAST_CBC           0x000003a2UL
      +#define CKM_PBE_MD5_CAST3_CBC          0x000003a3UL
      +#define CKM_PBE_MD5_CAST5_CBC          0x000003a4UL /* Deprecated */
      +#define CKM_PBE_MD5_CAST128_CBC        0x000003a4UL
      +#define CKM_PBE_SHA1_CAST5_CBC         0x000003a5UL /* Deprecated */
      +#define CKM_PBE_SHA1_CAST128_CBC       0x000003a5UL
      +#define CKM_PBE_SHA1_RC4_128           0x000003a6UL
      +#define CKM_PBE_SHA1_RC4_40            0x000003a7UL
      +#define CKM_PBE_SHA1_DES3_EDE_CBC      0x000003a8UL
      +#define CKM_PBE_SHA1_DES2_EDE_CBC      0x000003a9UL
      +#define CKM_PBE_SHA1_RC2_128_CBC       0x000003aaUL
      +#define CKM_PBE_SHA1_RC2_40_CBC        0x000003abUL
      +
      +#define CKM_PKCS5_PBKD2                0x000003b0UL
      +
      +#define CKM_PBA_SHA1_WITH_SHA1_HMAC    0x000003c0UL
      +
      +#define CKM_WTLS_PRE_MASTER_KEY_GEN         0x000003d0UL
      +#define CKM_WTLS_MASTER_KEY_DERIVE          0x000003d1UL
      +#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC   0x000003d2UL
      +#define CKM_WTLS_PRF                        0x000003d3UL
      +#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE  0x000003d4UL
      +#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE  0x000003d5UL
      +
      +#define CKM_TLS10_MAC_SERVER                0x000003d6UL
      +#define CKM_TLS10_MAC_CLIENT                0x000003d7UL
      +#define CKM_TLS12_MAC                       0x000003d8UL
      +#define CKM_TLS12_KDF                       0x000003d9UL
      +#define CKM_TLS12_MASTER_KEY_DERIVE         0x000003e0UL
      +#define CKM_TLS12_KEY_AND_MAC_DERIVE        0x000003e1UL
      +#define CKM_TLS12_MASTER_KEY_DERIVE_DH      0x000003e2UL
      +#define CKM_TLS12_KEY_SAFE_DERIVE           0x000003e3UL
      +#define CKM_TLS_MAC                         0x000003e4UL
      +#define CKM_TLS_KDF                         0x000003e5UL
       
       #define CKM_KEY_WRAP_LYNKS             0x00000400UL
       #define CKM_KEY_WRAP_SET_OAEP          0x00000401UL
      @@ -983,7 +997,7 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_ECDSA_SHA256               0x00001044UL
       #define CKM_ECDSA_SHA384               0x00001045UL
       #define CKM_ECDSA_SHA512               0x00001046UL
      -#define CKM_EC_KEY_PAIR_GEN_W_EXTRA_BITS 0x0000140BUL
      +#define CKM_EC_KEY_PAIR_GEN_W_EXTRA_BITS 0x0000140bUL
       
       #define CKM_ECDH1_DERIVE               0x00001050UL
       #define CKM_ECDH1_COFACTOR_DERIVE      0x00001051UL
      @@ -1012,12 +1026,12 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_AES_GCM                    0x00001087UL
       #define CKM_AES_CCM                    0x00001088UL
       #define CKM_AES_CTS                    0x00001089UL
      -#define CKM_AES_CMAC                   0x0000108AUL
      -#define CKM_AES_CMAC_GENERAL           0x0000108BUL
      +#define CKM_AES_CMAC                   0x0000108aUL
      +#define CKM_AES_CMAC_GENERAL           0x0000108bUL
       
      -#define CKM_AES_XCBC_MAC               0x0000108CUL
      -#define CKM_AES_XCBC_MAC_96            0x0000108DUL
      -#define CKM_AES_GMAC                   0x0000108EUL
      +#define CKM_AES_XCBC_MAC               0x0000108cUL
      +#define CKM_AES_XCBC_MAC_96            0x0000108dUL
      +#define CKM_AES_GMAC                   0x0000108eUL
       
       #define CKM_BLOWFISH_KEY_GEN           0x00001090UL
       #define CKM_BLOWFISH_CBC               0x00001091UL
      @@ -1066,6 +1080,7 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_AES_KEY_WRAP               0x00002109UL     /* WAS: 0x00001090 */
       #define CKM_AES_KEY_WRAP_PAD           0x0000210AUL     /* WAS: 0x00001091 */
       #define CKM_AES_KEY_WRAP_KWP           0x0000210BUL
      +#define CKM_AES_KEY_WRAP_PKCS7         0x0000210CUL
       
       #define CKM_RSA_PKCS_TPM_1_1           0x00004001UL
       #define CKM_RSA_PKCS_OAEP_TPM_1_1      0x00004002UL
      @@ -1125,6 +1140,14 @@ typedef CK_ULONG          CK_MECHANISM_TYPE;
       #define CKM_SP800_108_FEEDBACK_KDF     0x000003adUL
       #define CKM_SP800_108_DOUBLE_PIPELINE_KDF 0x000003aeUL
       
      +#define CKM_IKE2_PRF_PLUS_DERIVE       0x0000402eUL
      +#define CKM_IKE_PRF_DERIVE             0x0000402fUL
      +#define CKM_IKE1_PRF_DERIVE            0x00004030UL
      +#define CKM_IKE1_EXTENDED_DERIVE       0x00004031UL
      +#define CKM_HSS_KEY_PAIR_GEN           0x00004032UL
      +#define CKM_HSS                        0x00004033UL
      +
      +
       #define CKM_VENDOR_DEFINED             0x80000000UL
       
       typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR;
      @@ -1320,6 +1343,7 @@ typedef CK_ULONG          CK_RV;
       #define CKR_FUNCTION_REJECTED                 0x00000200UL
       #define CKR_TOKEN_RESOURCE_EXCEEDED           0x00000201UL
       #define CKR_OPERATION_CANCEL_FAILED           0x00000202UL
      +#define CKR_KEY_EXHAUSTED                     0x00000203UL
       
       #define CKR_VENDOR_DEFINED                    0x80000000UL
       
      @@ -1436,6 +1460,7 @@ typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR;
       #define CKG_MGF1_SHA3_384     0x00000008UL
       #define CKG_MGF1_SHA3_512     0x00000009UL
       
      +
       /* CK_RSA_PKCS_OAEP_SOURCE_TYPE  is used to indicate the source
        * of the encoding parameter when formatting a message block
        * for the PKCS #1 OAEP encryption scheme.
      @@ -1701,8 +1726,7 @@ typedef struct CK_DES_CBC_ENCRYPT_DATA_PARAMS {
           CK_ULONG    length;
       } CK_DES_CBC_ENCRYPT_DATA_PARAMS;
       
      -typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
      -        CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
      +typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
       
       typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS {
           CK_BYTE     iv[16];
      @@ -1710,8 +1734,7 @@ typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS {
           CK_ULONG    length;
       } CK_AES_CBC_ENCRYPT_DATA_PARAMS;
       
      -typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
      -        CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
      +typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
       
       /* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the
        * CKM_SKIPJACK_PRIVATE_WRAP mechanism
      @@ -2051,6 +2074,7 @@ typedef CK_ULONG CK_GENERATOR_FUNCTION;
       #define CKG_GENERATE         0x00000001UL
       #define CKG_GENERATE_COUNTER 0x00000002UL
       #define CKG_GENERATE_RANDOM  0x00000003UL
      +#define CKG_GENERATE_COUNTER_XOR 0x00000004UL
       
       typedef struct CK_GCM_MESSAGE_PARAMS {
           CK_BYTE_PTR           pIv;
      @@ -2061,7 +2085,7 @@ typedef struct CK_GCM_MESSAGE_PARAMS {
           CK_ULONG              ulTagBits;
       } CK_GCM_MESSAGE_PARAMS;
       
      -typedef CK_GCM_MESSAGE_PARAMS CK_GCM_MESSAGE_PARAMS_PTR;
      +typedef CK_GCM_MESSAGE_PARAMS CK_PTR CK_GCM_MESSAGE_PARAMS_PTR;
       
       typedef struct CK_CCM_PARAMS {
           CK_ULONG    ulDataLen;
      @@ -2084,7 +2108,7 @@ typedef struct CK_CCM_MESSAGE_PARAMS {
           CK_ULONG              ulMACLen;
       } CK_CCM_MESSAGE_PARAMS;
       
      -typedef CK_CCM_MESSAGE_PARAMS CK_CCM_MESSAGE_PARAMS_PTR;
      +typedef CK_CCM_MESSAGE_PARAMS CK_PTR CK_CCM_MESSAGE_PARAMS_PTR;
       
       /* Deprecated. Use CK_GCM_PARAMS */
       typedef struct CK_AES_GCM_PARAMS {
      @@ -2339,7 +2363,6 @@ typedef struct CK_SALSA20_PARAMS {
           CK_BYTE_PTR pNonce;
           CK_ULONG    ulNonceBits;
       } CK_SALSA20_PARAMS;
      -
       typedef CK_SALSA20_PARAMS CK_PTR CK_SALSA20_PARAMS_PTR;
       
       typedef struct CK_SALSA20_CHACHA20_POLY1305_PARAMS {
      @@ -2423,6 +2446,7 @@ typedef struct CK_XEDDSA_PARAMS {
       } CK_XEDDSA_PARAMS;
       typedef CK_XEDDSA_PARAMS CK_PTR CK_XEDDSA_PARAMS_PTR;
       
      +/* HKDF params */
       typedef struct CK_HKDF_PARAMS {
           CK_BBOOL          bExtract;
           CK_BBOOL          bExpand;
      @@ -2440,5 +2464,60 @@ typedef CK_HKDF_PARAMS CK_PTR CK_HKDF_PARAMS_PTR;
       #define CKF_HKDF_SALT_DATA   0x00000002UL
       #define CKF_HKDF_SALT_KEY    0x00000004UL
       
      +/* HSS */
      +typedef CK_ULONG                   CK_HSS_LEVELS;
      +typedef CK_ULONG                   CK_LMS_TYPE;
      +typedef CK_ULONG                   CK_LMOTS_TYPE;
      +
      +typedef struct specifiedParams {
      +    CK_HSS_LEVELS levels;
      +    CK_LMS_TYPE   lm_type[8];
      +    CK_LMOTS_TYPE lm_ots_type[8];
      +} specifiedParams;
      +
      +/* IKE Params */
      +typedef struct CK_IKE2_PRF_PLUS_DERIVE_PARAMS {
      +    CK_MECHANISM_TYPE prfMechanism;
      +    CK_BBOOL          bHasSeedKey;
      +    CK_OBJECT_HANDLE  hSeedKey;
      +    CK_BYTE_PTR       pSeedData;
      +    CK_ULONG          ulSeedDataLen;
      +} CK_IKE2_PRF_PLUS_DERIVE_PARAMS;
      +typedef CK_IKE2_PRF_PLUS_DERIVE_PARAMS CK_PTR CK_IKE2_PRF_PLUS_DERIVE_PARAMS_PTR;
      +
      +typedef struct CK_IKE_PRF_DERIVE_PARAMS {
      +    CK_MECHANISM_TYPE prfMechanism;
      +    CK_BBOOL          bDataAsKey;
      +    CK_BBOOL          bRekey;
      +    CK_BYTE_PTR       pNi;
      +    CK_ULONG          ulNiLen;
      +    CK_BYTE_PTR       pNr;
      +    CK_ULONG          ulNrLen;
      +    CK_OBJECT_HANDLE  hNewKey;
      +} CK_IKE_PRF_DERIVE_PARAMS;
      +typedef CK_IKE_PRF_DERIVE_PARAMS CK_PTR CK_IKE_PRF_DERIVE_PARAMS_PTR;
      +
      +typedef struct CK_IKE1_PRF_DERIVE_PARAMS {
      +    CK_MECHANISM_TYPE prfMechanism;
      +    CK_BBOOL          bHasPrevKey;
      +    CK_OBJECT_HANDLE  hKeygxy;
      +    CK_OBJECT_HANDLE  hPrevKey;
      +    CK_BYTE_PTR       pCKYi;
      +    CK_ULONG          ulCKYiLen;
      +    CK_BYTE_PTR       pCKYr;
      +    CK_ULONG          ulCKYrLen;
      +    CK_BYTE           keyNumber;
      +} CK_IKE1_PRF_DERIVE_PARAMS;
      +typedef CK_IKE1_PRF_DERIVE_PARAMS CK_PTR CK_IKE1_PRF_DERIVE_PARAMS_PTR;
      +
      +typedef struct CK_IKE1_EXTENDED_DERIVE_PARAMS {
      +    CK_MECHANISM_TYPE prfMechanism;
      +    CK_BBOOL          bHasKeygxy;
      +    CK_OBJECT_HANDLE  hKeygxy;
      +    CK_BYTE_PTR       pExtraData;
      +    CK_ULONG          ulExtraDataLen;
      +} CK_IKE1_EXTENDED_DERIVE_PARAMS;
      +typedef CK_IKE1_EXTENDED_DERIVE_PARAMS CK_PTR CK_IKE1_EXTENDED_DERIVE_PARAMS_PTR;
      +
       #endif /* _PKCS11T_H_ */
       
      diff --git a/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp
      index da1e322263236..82e6f2cc5bd7d 100644
      --- a/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp
      +++ b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp
      @@ -1226,17 +1226,17 @@ JNIEXPORT jboolean JNICALL Java_sun_security_mscapi_CSignature_verifyCngSignedHa
       
       #define DUMP_PROP(p) \
           if (::NCryptGetProperty(hKey, p, (PBYTE)buffer, 8192, &len, NCRYPT_SILENT_FLAG) == ERROR_SUCCESS) { \
      -        sprintf(header, "%s %ls", #p, p); \
      +        snprintf(header, sizeof(header), "%s %ls", #p, p); \
               dump(header, buffer, len); \
           }
       
       #define EXPORT_BLOB(p) \
           desc.cBuffers = 0; \
           if (::NCryptExportKey(hKey, NULL, p, &desc, (PBYTE)buffer, 8192, &len, NCRYPT_SILENT_FLAG) == ERROR_SUCCESS) { \
      -        sprintf(header, "%s %ls (%ld)", #p, p, desc.cBuffers); \
      +        snprintf(header, sizeof(header), "%s %ls (%ld)", #p, p, desc.cBuffers); \
               dump(header, buffer, len); \
               for (int i = 0; i < (int)desc.cBuffers; i++) { \
      -            sprintf(header, "desc %ld", desc.pBuffers[i].BufferType); \
      +            snprintf(header, sizeof(header), "desc %ld", desc.pBuffers[i].BufferType); \
                   dump(header, (PBYTE)desc.pBuffers[i].pvBuffer, desc.pBuffers[i].cbBuffer); \
               } \
           }
      @@ -1313,7 +1313,7 @@ void showProperty(NCRYPT_HANDLE hKey) {
           bbd.pBuffers = &bb;
           if(::NCryptExportKey(hKey, NULL, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, NULL,
                   (PBYTE)buffer, 8192, &len, NCRYPT_SILENT_FLAG) == ERROR_SUCCESS) {
      -        sprintf(header, "NCRYPT_PKCS8_PRIVATE_KEY_BLOB %ls", NCRYPT_PKCS8_PRIVATE_KEY_BLOB);
      +        snprintf(header, sizeof(header), "NCRYPT_PKCS8_PRIVATE_KEY_BLOB %ls", NCRYPT_PKCS8_PRIVATE_KEY_BLOB);
               dump(header, buffer, len);
           }
           EXPORT_BLOB(NCRYPT_PROTECTED_KEY_BLOB);
      @@ -1448,7 +1448,7 @@ JNIEXPORT jstring JNICALL Java_sun_security_mscapi_CKey_getKeyType
       
               } else {
                   char buffer[64];
      -            if (sprintf(buffer, "%lu", dwAlgId)) {
      +            if (snprintf(buffer, sizeof(buffer), "%lu", dwAlgId)) {
                       return env->NewStringUTF(buffer);
                   }
               }
      diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp
      index 9accba375a255..04d8e6b468991 100644
      --- a/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp
      +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp
      @@ -421,7 +421,6 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLo
         jboolean isCopy;
         jlongArray array;
         jlong *regs;
      -  int i;
       
         struct ps_prochandle* ph = get_proc_handle(env, this_obj);
         if (get_lwp_regs(ph, lwp_id, &gregs) != true) {
      diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c
      index 1101b9999610e..3068f4756262d 100644
      --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c
      +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
        * This code is free software; you can redistribute it and/or modify it
      @@ -227,7 +227,7 @@ static bool process_doesnt_exist(pid_t pid) {
         FILE *fp = NULL;
         const char state_string[] = "State:";
       
      -  sprintf(fname, "/proc/%d/status", pid);
      +  snprintf(fname, sizeof(fname), "/proc/%d/status", pid);
         fp = fopen(fname, "r");
         if (fp == NULL) {
           print_debug("can't open /proc/%d/status file\n", pid);
      @@ -346,7 +346,7 @@ static bool read_lib_info(struct ps_prochandle* ph) {
         char buf[PATH_MAX];
         FILE *fp = NULL;
       
      -  sprintf(fname, "/proc/%d/maps", ph->pid);
      +  snprintf(fname, sizeof(fname), "/proc/%d/maps", ph->pid);
         fp = fopen(fname, "r");
         if (fp == NULL) {
           print_debug("can't open /proc/%d/maps file\n", ph->pid);
      diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c b/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c
      index e147cdecaf9b4..753f5b5e0870a 100644
      --- a/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c
      +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c
      @@ -359,7 +359,6 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t
       
           if (shdr->sh_type == sym_section) {
             ELF_SYM  *syms;
      -      int rslt;
             size_t size, n, j, htab_sz;
       
             // FIXME: there could be multiple data buffers associated with the
      @@ -393,7 +392,8 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t
               goto bad;
             }
       
      -      rslt = hcreate_r(n, symtab->hash_table);
      +      // int rslt =
      +      hcreate_r(n, symtab->hash_table);
             // guarantee(rslt, "unexpected failure: hcreate_r");
       
             // shdr->sh_link points to the section that contains the actual strings
      diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c b/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c
      index 992df0eacafdb..4853a21ae6405 100644
      --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c
      +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
        * Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
      @@ -297,11 +297,16 @@ static bool read_core_segments(struct ps_prochandle* ph) {
               print_debug("failed to read LC_SEGMENT_64 i = %d!\n", i);
               goto err;
             }
      -      if (add_map_info(ph, fd, segcmd.fileoff, segcmd.vmaddr, segcmd.vmsize, segcmd.flags) == NULL) {
      -        print_debug("Failed to add map_info at i = %d\n", i);
      -        goto err;
      +      // The base of the library is offset by a random amount which ends up as a load command with a
      +      // filesize of 0.  This must be ignored otherwise the base address of the library is wrong.
      +      if (segcmd.filesize != 0) {
      +        if (add_map_info(ph, fd, segcmd.fileoff, segcmd.vmaddr, segcmd.vmsize, segcmd.flags) == NULL) {
      +          print_debug("Failed to add map_info at i = %d\n", i);
      +          goto err;
      +        }
             }
      -      print_debug("LC_SEGMENT_64 added: nsects=%d fileoff=0x%llx vmaddr=0x%llx vmsize=0x%llx filesize=0x%llx %s\n",
      +      print_debug("LC_SEGMENT_64 %s: nsects=%d fileoff=0x%llx vmaddr=0x%llx vmsize=0x%llx filesize=0x%llx %s\n",
      +                  segcmd.filesize == 0 ? "with filesize == 0 ignored" : "added",
                         segcmd.nsects, segcmd.fileoff, segcmd.vmaddr, segcmd.vmsize,
                         segcmd.filesize, &segcmd.segname[0]);
           } else if (lcmd.cmd == LC_THREAD || lcmd.cmd == LC_UNIXTHREAD) {
      diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java
      index d2f8db5efb98c..74cc759dc0e2d 100644
      --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java
      +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java
      @@ -120,7 +120,6 @@ public String getName() {
         }
       
         /** OopMap for frame; can return null if none available */
      -
         public ImmutableOopMapSet getOopMaps() {
           Address value = oopMapsField.getValue(addr);
           if (value == null) {
      diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/riscv/RISCV64ThreadContext.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/riscv64/RISCV64ThreadContext.java
      similarity index 100%
      rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/riscv/RISCV64ThreadContext.java
      rename to src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/riscv64/RISCV64ThreadContext.java
      diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java
      index dec1c41006b70..9e346306ff99f 100644
      --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java
      +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java
      @@ -317,8 +317,9 @@ private Frame senderForEntryFrame(PPC64RegisterMap map) {
         //------------------------------------------------------------------------------
         // frame::adjust_unextended_sp
         private void adjustUnextendedSP() {
      -    raw_unextendedSP = getFP();
      +    // Nothing to do. senderForInterpreterFrame finds the correct unextendedSP.
         }
      +
         private Frame senderForInterpreterFrame(PPC64RegisterMap map) {
           if (DEBUG) {
             System.out.println("senderForInterpreterFrame");
      diff --git a/src/jdk.hotspot.agent/test/libproc/Makefile b/src/jdk.hotspot.agent/test/libproc/Makefile
      index 81fadaeb55272..c7b171bc63381 100644
      --- a/src/jdk.hotspot.agent/test/libproc/Makefile
      +++ b/src/jdk.hotspot.agent/test/libproc/Makefile
      @@ -1,5 +1,5 @@
       #
      -# Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
      +# Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
       # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       #
       # This code is free software; you can redistribute it and/or modify it
      @@ -19,7 +19,7 @@
       # Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       # or visit www.oracle.com if you need additional information or have any
       # questions.
      -#  
      +#
       #
       
       all:
      diff --git a/src/jdk.hotspot.agent/windows/native/libsaproc/sawindbg.cpp b/src/jdk.hotspot.agent/windows/native/libsaproc/sawindbg.cpp
      index 0d8e5d108e11d..4ae0e895fc486 100644
      --- a/src/jdk.hotspot.agent/windows/native/libsaproc/sawindbg.cpp
      +++ b/src/jdk.hotspot.agent/windows/native/libsaproc/sawindbg.cpp
      @@ -184,11 +184,12 @@ static void throwNewDebuggerException(JNIEnv* env, const char* errMsg) {
         do { \
           const HRESULT hr = (v); \
           if (hr != S_OK) { \
      -      AutoArrayPtr errmsg(new char[strlen(str) + 32]); \
      +      size_t errmsg_size = strlen(str) + 32; \
      +      AutoArrayPtr errmsg(new char[errmsg_size]); \
             if (errmsg == nullptr) { \
               THROW_NEW_DEBUGGER_EXCEPTION_(str, retValue); \
             } else { \
      -        sprintf(errmsg, "%s (hr: 0x%08X)", str, hr); \
      +        snprintf(errmsg, errmsg_size, "%s (hr: 0x%08X)", str, hr); \
               THROW_NEW_DEBUGGER_EXCEPTION_(errmsg, retValue); \
             } \
           } \
      diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ChunkedOutputStream.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ChunkedOutputStream.java
      index b79dc75bb0928..9b7e0328dd5f1 100644
      --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ChunkedOutputStream.java
      +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ChunkedOutputStream.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
        * This code is free software; you can redistribute it and/or modify it
      @@ -27,6 +27,8 @@
       
       import java.io.*;
       import java.net.*;
      +import java.util.Objects;
      +
       import com.sun.net.httpserver.*;
       import com.sun.net.httpserver.spi.*;
       
      @@ -77,6 +79,10 @@ public void write (int b) throws IOException {
           }
       
           public void write (byte[]b, int off, int len) throws IOException {
      +        Objects.checkFromIndexSize(off, len, b.length);
      +        if (len == 0) {
      +            return;
      +        }
               if (closed) {
                   throw new StreamClosedException ();
               }
      diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java
      index 454b6cd435c22..9a27eeaf23014 100644
      --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java
      +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
        * This code is free software; you can redistribute it and/or modify it
      @@ -240,6 +240,7 @@ public void sendResponseHeaders (int rCode, long contentLen)
                   }
                   noContentToSend = true;
                   contentLen = 0;
      +            o.setWrappedStream (new FixedLengthOutputStream (this, ros, contentLen));
               } else { /* not a HEAD request or 304 response */
                   if (contentLen == 0) {
                       if (http10) {
      @@ -282,9 +283,7 @@ public void sendResponseHeaders (int rCode, long contentLen)
               sentHeaders = true;
               logger.log(Level.TRACE, "Sent headers: noContentToSend=" + noContentToSend);
               if (noContentToSend) {
      -            WriteFinishedEvent e = new WriteFinishedEvent (this);
      -            server.addEvent (e);
      -            closed = true;
      +            close();
               }
               server.logReply (rCode, req.requestLine(), null);
           }
      diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthOutputStream.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthOutputStream.java
      index 4935214c2e152..277e6bb422878 100644
      --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthOutputStream.java
      +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthOutputStream.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
        * This code is free software; you can redistribute it and/or modify it
      @@ -27,6 +27,8 @@
       
       import java.io.*;
       import java.net.*;
      +import java.util.Objects;
      +
       import com.sun.net.httpserver.*;
       import com.sun.net.httpserver.spi.*;
       
      @@ -41,7 +43,6 @@
       class FixedLengthOutputStream extends FilterOutputStream
       {
           private long remaining;
      -    private boolean eof = false;
           private boolean closed = false;
           ExchangeImpl t;
       
      @@ -58,8 +59,7 @@ public void write (int b) throws IOException {
               if (closed) {
                   throw new IOException ("stream closed");
               }
      -        eof = (remaining == 0);
      -        if (eof) {
      +        if (remaining == 0) {
                   throw new StreamClosedException();
               }
               out.write(b);
      @@ -67,13 +67,13 @@ public void write (int b) throws IOException {
           }
       
           public void write (byte[]b, int off, int len) throws IOException {
      +        Objects.checkFromIndexSize(off, len, b.length);
      +        if (len == 0) {
      +            return;
      +        }
               if (closed) {
                   throw new IOException ("stream closed");
               }
      -        eof = (remaining == 0);
      -        if (eof) {
      -            throw new StreamClosedException();
      -        }
               if (len > remaining) {
                   // stream is still open, caller can retry
                   throw new IOException ("too many bytes to write to stream");
      @@ -92,7 +92,6 @@ public void close () throws IOException {
                   throw new IOException ("insufficient bytes written to stream");
               }
               flush();
      -        eof = true;
               LeftOverInputStream is = t.getOriginalInputStream();
               if (!is.isClosed()) {
                   try {
      diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java
      index 03e2a9ee8f585..0e136d8043705 100644
      --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java
      +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
        * This code is free software; you can redistribute it and/or modify it
      @@ -42,8 +42,10 @@ class Request {
           private SocketChannel chan;
           private InputStream is;
           private OutputStream os;
      +    private final int maxReqHeaderSize;
       
           Request (InputStream rawInputStream, OutputStream rawout) throws IOException {
      +        this.maxReqHeaderSize = ServerConfig.getMaxReqHeaderSize();
               is = rawInputStream;
               os = rawout;
               do {
      @@ -76,6 +78,7 @@ public OutputStream outputStream () {
           public String readLine () throws IOException {
               boolean gotCR = false, gotLF = false;
               pos = 0; lineBuf = new StringBuffer();
      +        long lsize = 32;
               while (!gotLF) {
                   int c = is.read();
                   if (c == -1) {
      @@ -88,20 +91,27 @@ public String readLine () throws IOException {
                           gotCR = false;
                           consume (CR);
                           consume (c);
      +                    lsize = lsize + 2;
                       }
                   } else {
                       if (c == CR) {
                           gotCR = true;
                       } else {
                           consume (c);
      +                    lsize = lsize + 1;
                       }
                   }
      +            if (maxReqHeaderSize > 0 && lsize > maxReqHeaderSize) {
      +                throw new IOException("Maximum header (" +
      +                        "sun.net.httpserver.maxReqHeaderSize) exceeded, " +
      +                        ServerConfig.getMaxReqHeaderSize() + ".");
      +            }
               }
               lineBuf.append (buf, 0, pos);
               return new String (lineBuf);
           }
       
      -    private void consume (int c) {
      +    private void consume (int c) throws IOException {
               if (pos == BUF_LEN) {
                   lineBuf.append (buf);
                   pos = 0;
      @@ -139,13 +149,22 @@ Headers headers () throws IOException {
                   len = 1;
                   firstc = c;
               }
      +        long hsize = startLine.length() + 32L;
       
               while (firstc != LF && firstc != CR && firstc >= 0) {
                   int keyend = -1;
                   int c;
                   boolean inKey = firstc > ' ';
                   s[len++] = (char) firstc;
      +            hsize = hsize + 1;
           parseloop:{
      +                // We start parsing for a new name value pair here.
      +                // The max header size includes an overhead of 32 bytes per
      +                // name value pair.
      +                // See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
      +                long maxRemaining = maxReqHeaderSize > 0
      +                        ? maxReqHeaderSize - hsize - 32
      +                        : Long.MAX_VALUE;
                       while ((c = is.read()) >= 0) {
                           switch (c) {
                             /*fallthrough*/
      @@ -179,6 +198,11 @@ Headers headers () throws IOException {
                               s = ns;
                           }
                           s[len++] = (char) c;
      +                    if (maxReqHeaderSize > 0 && len > maxRemaining) {
      +                        throw new IOException("Maximum header (" +
      +                                "sun.net.httpserver.maxReqHeaderSize) exceeded, " +
      +                                ServerConfig.getMaxReqHeaderSize() + ".");
      +                    }
                       }
                       firstc = -1;
                   }
      @@ -206,6 +230,13 @@ Headers headers () throws IOException {
                               "sun.net.httpserver.maxReqHeaders) exceeded, " +
                               ServerConfig.getMaxReqHeaders() + ".");
                   }
      +            hsize = hsize + len + 32;
      +            if (maxReqHeaderSize > 0 && hsize > maxReqHeaderSize) {
      +                throw new IOException("Maximum header (" +
      +                        "sun.net.httpserver.maxReqHeaderSize) exceeded, " +
      +                        ServerConfig.getMaxReqHeaderSize() + ".");
      +            }
      +
                   if (k == null) {  // Headers disallows null keys, use empty string
                       k = "";       // instead to represent invalid key
                   }
      diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java
      index 303db604a19ea..63d6a57258a00 100644
      --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java
      +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
        * This code is free software; you can redistribute it and/or modify it
      @@ -49,6 +49,7 @@ class ServerConfig {
           // timing out request/response if max request/response time is configured
           private static final long DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS = 1000;
           private static final int  DEFAULT_MAX_REQ_HEADERS = 200;
      +    private static final int  DEFAULT_MAX_REQ_HEADER_SIZE = 380 * 1024;
           private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024;
       
           private static long idleTimerScheduleMillis;
      @@ -62,6 +63,9 @@ class ServerConfig {
           private static int maxIdleConnections;
           // The maximum number of request headers allowable
           private static int maxReqHeaders;
      +    // a maximum value for the header list size. This is the
      +    // names size + values size + 32 bytes per field line
      +    private static int maxReqHeadersSize;
           // max time a request or response is allowed to take
           private static long maxReqTime;
           private static long maxRspTime;
      @@ -104,6 +108,14 @@ public Void run () {
                                   "sun.net.httpserver.maxReqHeaders",
                                   DEFAULT_MAX_REQ_HEADERS);
       
      +                    // a value <= 0 means unlimited
      +                    maxReqHeadersSize = Integer.getInteger(
      +                            "sun.net.httpserver.maxReqHeaderSize",
      +                            DEFAULT_MAX_REQ_HEADER_SIZE);
      +                    if (maxReqHeadersSize <= 0) {
      +                        maxReqHeadersSize = 0;
      +                    }
      +
                           maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime",
                                   DEFAULT_MAX_REQ_TIME);
       
      @@ -212,6 +224,10 @@ static int getMaxReqHeaders() {
               return maxReqHeaders;
           }
       
      +    static int getMaxReqHeaderSize() {
      +        return maxReqHeadersSize;
      +    }
      +
           /**
            * @return Returns the maximum amount of time the server will wait for the request to be read
            * completely. This method can return a value of 0 or negative to imply no maximum limit has
      diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
      index 5555294cc2755..4043fdd880d88 100644
      --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
      +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
      @@ -54,10 +54,10 @@
       import java.nio.channels.SocketChannel;
       import java.security.AccessController;
       import java.security.PrivilegedAction;
      +import java.util.ArrayList;
       import java.util.Collections;
       import java.util.HashSet;
       import java.util.Iterator;
      -import java.util.LinkedList;
       import java.util.List;
       import java.util.Set;
       import java.util.Timer;
      @@ -161,7 +161,7 @@ class ServerImpl {
                   logger.log (Level.DEBUG, "MAX_REQ_TIME:  "+MAX_REQ_TIME);
                   logger.log (Level.DEBUG, "MAX_RSP_TIME:  "+MAX_RSP_TIME);
               }
      -        events = new LinkedList();
      +        events = new ArrayList<>();
               logger.log (Level.DEBUG, "HttpServer created "+protocol+" "+ addr);
           }
       
      @@ -428,8 +428,7 @@ private void handleEvent (Event r) {
                   }
               }
       
      -        final LinkedList connsToRegister =
      -                new LinkedList();
      +        final ArrayList connsToRegister = new ArrayList<>();
       
               void reRegister (HttpConnection c) {
                   /* re-register with selector */
      @@ -454,7 +453,7 @@ public void run() {
                           synchronized (lolock) {
                               if (events.size() > 0) {
                                   list = events;
      -                            events = new LinkedList();
      +                            events = new ArrayList<>();
                               }
                           }
       
      @@ -517,14 +516,15 @@ public void run() {
       
                                           key.cancel();
                                           chan.configureBlocking (true);
      +                                    // check if connection is being closed
                                           if (newlyAcceptedConnections.remove(conn)
                                                   || idleConnections.remove(conn)) {
                                               // was either a newly accepted connection or an idle
                                               // connection. In either case, we mark that the request
                                               // has now started on this connection.
                                               requestStarted(conn);
      +                                        handle (chan, conn);
                                           }
      -                                    handle (chan, conn);
                                       } else {
                                           assert false : "Unexpected non-readable key:" + key;
                                       }
      @@ -696,7 +696,14 @@ public void run () {
                           return;
                       }
                       String uriStr = requestLine.substring (start, space);
      -                URI uri = new URI (uriStr);
      +                URI uri;
      +                try {
      +                    uri = new URI (uriStr);
      +                } catch (URISyntaxException e3) {
      +                    reject(Code.HTTP_BAD_REQUEST,
      +                            requestLine, "URISyntaxException thrown");
      +                    return;
      +                }
                       start = space+1;
                       String version = requestLine.substring (start);
                       Headers headers = req.headers();
      @@ -732,7 +739,13 @@ public void run () {
                       } else {
                           headerValue = headers.getFirst("Content-Length");
                           if (headerValue != null) {
      -                        clen = Long.parseLong(headerValue);
      +                        try {
      +                            clen = Long.parseLong(headerValue);
      +                        } catch (NumberFormatException e2) {
      +                            reject(Code.HTTP_BAD_REQUEST,
      +                                    requestLine, "NumberFormatException thrown");
      +                            return;
      +                        }
                               if (clen < 0) {
                                   reject(Code.HTTP_BAD_REQUEST, requestLine,
                                           "Illegal Content-Length value");
      @@ -818,20 +831,11 @@ public void run () {
                           uc.doFilter (new HttpExchangeImpl (tx));
                       }
       
      -            } catch (IOException e1) {
      -                logger.log (Level.TRACE, "ServerImpl.Exchange (1)", e1);
      -                closeConnection(connection);
      -            } catch (NumberFormatException e2) {
      -                logger.log (Level.TRACE, "ServerImpl.Exchange (2)", e2);
      -                reject (Code.HTTP_BAD_REQUEST,
      -                        requestLine, "NumberFormatException thrown");
      -            } catch (URISyntaxException e3) {
      -                logger.log (Level.TRACE, "ServerImpl.Exchange (3)", e3);
      -                reject (Code.HTTP_BAD_REQUEST,
      -                        requestLine, "URISyntaxException thrown");
      -            } catch (Exception e4) {
      -                logger.log (Level.TRACE, "ServerImpl.Exchange (4)", e4);
      -                closeConnection(connection);
      +            } catch (Exception e) {
      +                logger.log (Level.TRACE, "ServerImpl.Exchange", e);
      +                if (tx == null || !tx.writefinished) {
      +                    closeConnection(connection);
      +                }
                   } catch (Throwable t) {
                       logger.log(Level.TRACE, "ServerImpl.Exchange (5)", t);
                       throw t;
      @@ -856,9 +860,8 @@ void reject (int code, String requestStr, String message) {
                   rejected = true;
                   logReply (code, requestStr, message);
                   sendReply (
      -                code, false, "

      "+code+Code.msg(code)+"

      "+message + code, true, "

      "+code+Code.msg(code)+"

      "+message ); - closeConnection(connection); } void sendReply ( @@ -985,35 +988,30 @@ void responseCompleted (HttpConnection c) { */ class IdleTimeoutTask extends TimerTask { public void run () { - LinkedList toClose = new LinkedList(); - final long currentTime = System.currentTimeMillis(); - synchronized (idleConnections) { - final Iterator it = idleConnections.iterator(); - while (it.hasNext()) { - final HttpConnection c = it.next(); - if (currentTime - c.idleStartTime >= IDLE_INTERVAL) { - toClose.add(c); - it.remove(); - } - } - } + closeConnections(idleConnections, IDLE_INTERVAL); // if any newly accepted connection has been idle (i.e. no byte has been sent on that // connection during the configured idle timeout period) then close it as well - synchronized (newlyAcceptedConnections) { - final Iterator it = newlyAcceptedConnections.iterator(); - while (it.hasNext()) { - final HttpConnection c = it.next(); - if (currentTime - c.idleStartTime >= NEWLY_ACCEPTED_CONN_IDLE_INTERVAL) { - toClose.add(c); - it.remove(); - } + closeConnections(newlyAcceptedConnections, NEWLY_ACCEPTED_CONN_IDLE_INTERVAL); + } + + private void closeConnections(Set connections, long idleInterval) { + long currentTime = System.currentTimeMillis(); + ArrayList toClose = new ArrayList<>(); + + connections.forEach(c -> { + if (currentTime - c.idleStartTime >= idleInterval) { + toClose.add(c); } - } + }); for (HttpConnection c : toClose) { - allConnections.remove(c); - c.close(); - if (logger.isLoggable(Level.TRACE)) { - logger.log(Level.TRACE, "Closed idle connection " + c); + // check if connection still idle + if (currentTime - c.idleStartTime >= idleInterval && + connections.remove(c)) { + allConnections.remove(c); + c.close(); + if (logger.isLoggable(Level.TRACE)) { + logger.log(Level.TRACE, "Closed idle connection " + c); + } } } } @@ -1026,7 +1024,7 @@ class ReqRspTimeoutTask extends TimerTask { // runs every TIMER_MILLIS public void run () { - LinkedList toClose = new LinkedList(); + ArrayList toClose = new ArrayList<>(); final long currentTime = System.currentTimeMillis(); synchronized (reqConnections) { if (MAX_REQ_TIME != -1) { @@ -1043,7 +1041,7 @@ public void run () { } } } - toClose = new LinkedList(); + toClose = new ArrayList<>(); synchronized (rspConnections) { if (MAX_RSP_TIME != -1) { for (HttpConnection c : rspConnections) { diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/UndefLengthOutputStream.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/UndefLengthOutputStream.java index 2918e42a05f8a..76a1be6ec5f3d 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/UndefLengthOutputStream.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/UndefLengthOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,8 @@ import java.io.*; import java.net.*; +import java.util.Objects; + import com.sun.net.httpserver.*; import com.sun.net.httpserver.spi.*; @@ -55,6 +57,10 @@ public void write (int b) throws IOException { } public void write (byte[]b, int off, int len) throws IOException { + Objects.checkFromIndexSize(off, len, b.length); + if (len == 0) { + return; + } if (closed) { throw new IOException ("stream closed"); } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/BindingReader.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/BindingReader.java index 46a45ae396d2b..74d3a46ecf2a7 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/BindingReader.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/BindingReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -66,7 +66,7 @@ public T readBinding(KeyMap keys, KeyMap local, boolean block) { T o = null; int[] remaining = new int[1]; boolean hasRead = false; - for (;;) { + for (; ; ) { if (local != null) { o = local.getBound(opBuffer, remaining); } @@ -78,8 +78,7 @@ public T readBinding(KeyMap keys, KeyMap local, boolean block) { if (remaining[0] >= 0) { runMacro(opBuffer.substring(opBuffer.length() - remaining[0])); opBuffer.setLength(opBuffer.length() - remaining[0]); - } - else { + } else { long ambiguousTimeout = keys.getAmbiguousTimeout(); if (ambiguousTimeout > 0 && peekCharacter(ambiguousTimeout) != NonBlockingReader.READ_EXPIRED) { o = null; @@ -234,5 +233,4 @@ public String getCurrentBuffer() { public String getLastBinding() { return lastBinding; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/KeyMap.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/KeyMap.java index 43c3a1b4fd124..a8a90ea5e0dcf 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/KeyMap.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/KeyMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,8 +8,6 @@ */ package jdk.internal.org.jline.keymap; -import java.io.IOException; -import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -218,7 +216,6 @@ public static Collection range(String range) { return seqs; } - public static String esc() { return "\033"; } @@ -264,7 +261,6 @@ public static String key(Terminal terminal, Capability capability) { // Methods // - public T getUnicode() { return unicode; } @@ -306,9 +302,7 @@ private static void doGetBoundKeys(KeyMap keyMap, String prefix, Map) keyMap.mapping[c], - prefix + (char) (c), - bound); + doGetBoundKeys((KeyMap) keyMap.mapping[c], prefix + (char) (c), bound); } else if (keyMap.mapping[c] != null) { bound.put(prefix + (char) (c), (T) keyMap.mapping[c]); } @@ -456,5 +450,4 @@ private static void bind(KeyMap map, CharSequence keySeq, T function, boo } } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Binding.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Binding.java index 0070b55c22df2..15e7a49a64318 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Binding.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Binding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -14,9 +14,8 @@ * @see Macro * @see Reference * @see Widget - * @see jdk.internal.org.jline.keymap.KeyMap + * @see org.jline.keymap.KeyMap * * @author Guillaume Nodet */ -public interface Binding { -} +public interface Binding {} diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Buffer.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Buffer.java index 9fdd6e0bd7e8f..8d65891d9d900 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Buffer.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Buffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2017, the original author or authors. + * Copyright (c) 2002-2017, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -84,4 +84,8 @@ public interface Buffer { void copyFrom(Buffer buffer); + /** + * Clear any internal buffer. + */ + void zeroOut(); } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java index 0e95538492d40..e28e72ffb2322 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2019, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -47,7 +47,15 @@ public Candidate(String value) { * @param complete the complete flag * @param sort the sort flag */ - public Candidate(String value, String displ, String group, String descr, String suffix, String key, boolean complete, int sort) { + public Candidate( + String value, + String displ, + String group, + String descr, + String suffix, + String key, + boolean complete, + int sort) { this.value = Objects.requireNonNull(value); this.displ = Objects.requireNonNull(displ); this.group = group; @@ -69,7 +77,8 @@ public Candidate(String value, String displ, String group, String descr, String * @param key the key * @param complete the complete flag */ - public Candidate(String value, String displ, String group, String descr, String suffix, String key, boolean complete) { + public Candidate( + String value, String displ, String group, String descr, String suffix, String key, boolean complete) { this(value, displ, group, descr, suffix, key, complete, 0); } @@ -159,11 +168,10 @@ public int sort() { return sort; } - @Override public int compareTo(Candidate o) { // If both candidates have same sort, use default behavior - if( sort == o.sort() ) { + if (sort == o.sort()) { return value.compareTo(o.value); } else { return Integer.compare(sort, o.sort()); @@ -180,7 +188,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(value); + return Objects.hashCode(value); } @Override diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Completer.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Completer.java index b87db7afbf02c..7a5937460d87c 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Completer.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Completer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -18,8 +18,7 @@ * @author Guillaume Nodet * @since 2.3 */ -public interface Completer -{ +public interface Completer { /** * Populates candidates with a list of possible completions for the command line. * diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletingParsedLine.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletingParsedLine.java index 52a36b838969e..93558001af16a 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletingParsedLine.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletingParsedLine.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -10,7 +10,7 @@ /** * An extension of {@link ParsedLine} that, being aware of the quoting and escaping rules - * of the {@link jdk.internal.org.jline.reader.Parser} that produced it, knows if and how a completion candidate + * of the {@link org.jline.reader.Parser} that produced it, knows if and how a completion candidate * should be escaped/quoted. * * @author Eric Bottard @@ -22,5 +22,4 @@ public interface CompletingParsedLine extends ParsedLine { int rawWordCursor(); int rawWordLength(); - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletionMatcher.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletionMatcher.java index 58117c8f21333..4a2334642cad5 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletionMatcher.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletionMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2020, the original author or authors. + * Copyright (c) 2002-2020, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -23,8 +23,13 @@ public interface CompletionMatcher { * @param errors number of errors accepted in matching * @param originalGroupName value of JLineReader variable original-group-name */ - void compile(Map options, boolean prefix, CompletingParsedLine line - , boolean caseInsensitive, int errors, String originalGroupName); + void compile( + Map options, + boolean prefix, + CompletingParsedLine line, + boolean caseInsensitive, + int errors, + String originalGroupName); /** * @@ -44,5 +49,4 @@ void compile(Map options, boolean prefix, Completing * @return a common prefix of matched candidates */ String getCommonPrefix(); - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EOFError.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EOFError.java index 382db8bc49f2b..8a4f516174e17 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EOFError.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EOFError.java @@ -1,20 +1,10 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at + * Copyright (c) 2023, the original author(s). * - * http://www.apache.org/licenses/LICENSE-2.0 + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * https://opensource.org/licenses/BSD-3-Clause */ package jdk.internal.org.jline.reader; @@ -45,7 +35,7 @@ public String getMissing() { return missing; } - public int getOpenBrackets(){ + public int getOpenBrackets() { return openBrackets; } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Editor.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Editor.java index e99706ed5d5ac..b2fadd3bd5213 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Editor.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Editor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2019, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -13,6 +13,8 @@ public interface Editor { public void open(List files) throws IOException; + public void run() throws IOException; + public void setRestricted(boolean restricted); } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java index 9f70420d9dff0..1e50a7ea1ae55 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2020, the original author or authors. + * Copyright (c) 2002-2020, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -17,8 +17,7 @@ public class EndOfFileException extends RuntimeException { private static final long serialVersionUID = 528485360925144689L; private String partialLine; - public EndOfFileException() { - } + public EndOfFileException() {} public EndOfFileException(String message) { super(message); diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Expander.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Expander.java index 0562e92ed9604..614c89b3e6100 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Expander.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Expander.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -13,5 +13,4 @@ public interface Expander { String expandHistory(History history, String line); String expandVar(String word); - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Highlighter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Highlighter.java index 136fb20324b89..dad62551ee062 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Highlighter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Highlighter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2021, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/History.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/History.java index 7a669edddb5a3..0cb46a1ee9dfc 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/History.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/History.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -21,8 +21,7 @@ * @author Jason Dillon * @since 2.3 */ -public interface History extends Iterable -{ +public interface History extends Iterable { /** * Initialize the history for the given reader. @@ -75,7 +74,6 @@ public interface History extends Iterable */ void purge() throws IOException; - int size(); default boolean isEmpty() { @@ -110,8 +108,7 @@ default boolean isPersistable(Entry entry) { // Entries // - interface Entry - { + interface Entry { int index(); Instant time(); @@ -132,14 +129,17 @@ default Iterator reverseIterator() { default Iterator reverseIterator(int index) { return new Iterator() { private final ListIterator it = iterator(index + 1); + @Override public boolean hasNext() { return it.hasPrevious(); } + @Override public Entry next() { return it.previous(); } + @Override public void remove() { it.remove(); diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java index 03729853ca549..bc513f5f636a1 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2023, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -21,7 +21,7 @@ /** Read lines from the console, with input editing. * - *

      Thread safety

      + *

      Thread safety

      * The LineReader implementations are not thread safe, * thus you should not attempt to use a single reader in several threads. * Any attempt to call one of the readLine call while one is @@ -31,7 +31,7 @@ * {@link #printAbove(String)} or {@link #printAbove(AttributedString)} at * any time to allow text to be printed above the current prompt. * - *

      Prompt strings

      + *

      Prompt strings

      * It is traditional for an interactive console-based program * to print a short prompt string to signal that the user is expected * to type a command. JLine supports 3 kinds of prompt string: @@ -81,7 +81,6 @@ * * */ - public interface LineReader { /** @@ -284,6 +283,7 @@ public interface LineReader { String MAIN = "main"; String EMACS = "emacs"; String SAFE = ".safe"; + String DUMB = "dumb"; String MENU = "menu"; // @@ -301,6 +301,7 @@ public interface LineReader { * they are displayed in a list below the field to be completed */ String MENU_LIST_MAX = "menu-list-max"; + String DISABLE_HISTORY = "disable-history"; String DISABLE_COMPLETION = "disable-completion"; String EDITING_MODE = "editing-mode"; @@ -317,18 +318,23 @@ public interface LineReader { String ORIGINAL_GROUP_NAME = "ORIGINAL_GROUP_NAME"; /** Completion style for displaying groups name */ String COMPLETION_STYLE_GROUP = "COMPLETION_STYLE_GROUP"; + String COMPLETION_STYLE_LIST_GROUP = "COMPLETION_STYLE_LIST_GROUP"; /** Completion style for displaying the current selected item */ String COMPLETION_STYLE_SELECTION = "COMPLETION_STYLE_SELECTION"; + String COMPLETION_STYLE_LIST_SELECTION = "COMPLETION_STYLE_LIST_SELECTION"; /** Completion style for displaying the candidate description */ String COMPLETION_STYLE_DESCRIPTION = "COMPLETION_STYLE_DESCRIPTION"; + String COMPLETION_STYLE_LIST_DESCRIPTION = "COMPLETION_STYLE_LIST_DESCRIPTION"; /** Completion style for displaying the matching part of candidates */ String COMPLETION_STYLE_STARTING = "COMPLETION_STYLE_STARTING"; + String COMPLETION_STYLE_LIST_STARTING = "COMPLETION_STYLE_LIST_STARTING"; /** Completion style for displaying the list */ String COMPLETION_STYLE_BACKGROUND = "COMPLETION_STYLE_BACKGROUND"; + String COMPLETION_STYLE_LIST_BACKGROUND = "COMPLETION_STYLE_LIST_BACKGROUND"; /** * Set the template for prompts for secondary (continuation) lines. @@ -390,6 +396,26 @@ public interface LineReader { */ String SUGGESTIONS_MIN_BUFFER_SIZE = "suggestions-min-buffer-size"; + /** + * Max number of times a command can be repeated. + */ + String MAX_REPEAT_COUNT = "max-repeat-count"; + + /** + * Number of spaces to display a tabulation, the default is 4. + */ + String TAB_WIDTH = "tab-width"; + + /** + * Name of inputrc to read at line reader creation time. + */ + String INPUT_RC_FILE_NAME = "input-rc-file-name"; + + /** + * Prefix to automatically delegate variables to system properties + */ + String SYSTEM_PROPERTY_PREFIX = "system-property-prefix"; + Map> defaultKeyMaps(); enum Option { @@ -469,8 +495,7 @@ enum Option { EMPTY_WORD_OPTIONS(true), /** Disable the undo feature */ - DISABLE_UNDO - ; + DISABLE_UNDO; private final boolean def; @@ -611,7 +636,8 @@ enum SuggestionType { * @throws EndOfFileException if an EOF has been found (using Ctrl-D for example) * @throws java.io.IOError in case of other i/o errors */ - String readLine(String prompt, String rightPrompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException; + String readLine(String prompt, String rightPrompt, Character mask, String buffer) + throws UserInterruptException, EndOfFileException; /** * Read a line from the in {@link InputStream}, and return the line @@ -631,7 +657,8 @@ enum SuggestionType { * @throws EndOfFileException if an EOF has been found (using Ctrl-D for example) * @throws java.io.IOError in case of other i/o errors */ - String readLine(String prompt, String rightPrompt, MaskingCallback maskingCallback, String buffer) throws UserInterruptException, EndOfFileException; + String readLine(String prompt, String rightPrompt, MaskingCallback maskingCallback, String buffer) + throws UserInterruptException, EndOfFileException; /** * Prints a line above the prompt and redraw everything. @@ -702,7 +729,7 @@ enum SuggestionType { void runMacro(String macro); /** - * Read a mouse event when the {@link jdk.internal.org.jline.utils.InfoCmp.Capability#key_mouse} sequence + * Read a mouse event when the {@link org.jline.utils.InfoCmp.Capability#key_mouse} sequence * has just been read on the input stream. * Compared to {@link Terminal#readMouseEvent()}, this method takes into account keys * that have been pushed back using {@link #runMacro(String)}. @@ -750,4 +777,9 @@ enum SuggestionType { void setAutosuggestion(SuggestionType type); SuggestionType getAutosuggestion(); + + /** + * Clear any internal buffers. + */ + void zeroOut(); } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java index 14d5a0e8b1c3a..942bba3605d52 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2020, the original author or authors. + * Copyright (c) 2002-2020, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -38,8 +38,7 @@ public static LineReaderBuilder builder() { Expander expander; CompletionMatcher completionMatcher; - private LineReaderBuilder() { - } + private LineReaderBuilder() {} public LineReaderBuilder terminal(Terminal terminal) { this.terminal = terminal; @@ -88,8 +87,9 @@ public LineReaderBuilder parser(Parser parser) { try { if (!Boolean.getBoolean(LineReader.PROP_SUPPORT_PARSEDLINE) && !(parser.parse("", 0) instanceof CompletingParsedLine)) { - Log.warn("The Parser of class " + parser.getClass().getName() + " does not support the CompletingParsedLine interface. " + - "Completion with escaped or quoted words won't work correctly."); + Log.warn("The Parser of class " + parser.getClass().getName() + + " does not support the CompletingParsedLine interface. " + + "Completion with escaped or quoted words won't work correctly."); } } catch (Throwable t) { // Ignore @@ -153,5 +153,4 @@ public LineReader build() { } return reader; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Macro.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Macro.java index e66efcc5f9281..b00769aff52f5 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Macro.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Macro.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -35,7 +35,6 @@ public int hashCode() { @Override public String toString() { - return "Macro[" + - sequence + ']'; + return "Macro[" + sequence + ']'; } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/MaskingCallback.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/MaskingCallback.java index 03501aaf07c6a..3b624e306f28a 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/MaskingCallback.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/MaskingCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -31,5 +31,4 @@ public interface MaskingCallback { * @return the modified line */ String history(String line); - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ParsedLine.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ParsedLine.java index 683c4b48d776e..3d87e6b80b2eb 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ParsedLine.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ParsedLine.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -64,5 +64,4 @@ public interface ParsedLine { * @return the cursor position within the line */ int cursor(); - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java index 1f7df67d5739b..5db53818f67db 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2021, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -35,7 +35,7 @@ default boolean validVariableName(String name) { default String getCommand(final String line) { String out; - Pattern patternCommand = Pattern.compile("^\\s*" + REGEX_VARIABLE + "=(" + REGEX_COMMAND + ")(\\s+|$)"); + Pattern patternCommand = Pattern.compile("^\\s*" + REGEX_VARIABLE + "=(" + REGEX_COMMAND + ")(\\s+|$)"); Matcher matcher = patternCommand.matcher(line); if (matcher.find()) { out = matcher.group(1); @@ -50,7 +50,7 @@ default String getCommand(final String line) { default String getVariable(final String line) { String out = null; - Pattern patternCommand = Pattern.compile("^\\s*(" + REGEX_VARIABLE + ")\\s*=[^=~].*"); + Pattern patternCommand = Pattern.compile("^\\s*(" + REGEX_VARIABLE + ")\\s*=[^=~].*"); Matcher matcher = patternCommand.matcher(line); if (matcher.find()) { out = matcher.group(1); diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/PrintAboveWriter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/PrintAboveWriter.java index 63c9decd19444..e23988310a593 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/PrintAboveWriter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/PrintAboveWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2021, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Reference.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Reference.java index f799a3da22070..2fb1aaa671b0a 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Reference.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Reference.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -38,7 +38,6 @@ public int hashCode() { @Override public String toString() { - return "Reference[" + - name + ']'; + return "Reference[" + name + ']'; } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/SyntaxError.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/SyntaxError.java index e46143e4c3998..eaf3e1d758064 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/SyntaxError.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/SyntaxError.java @@ -1,20 +1,10 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at + * Copyright (c) 2023, the original author(s). * - * http://www.apache.org/licenses/LICENSE-2.0 + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * https://opensource.org/licenses/BSD-3-Clause */ package jdk.internal.org.jline.reader; diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/UserInterruptException.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/UserInterruptException.java index b5510cab5c918..68bec8041faa5 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/UserInterruptException.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/UserInterruptException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -14,23 +14,19 @@ * interrupt character (ctrl-C). The partially entered line is * available via the {@link #getPartialLine()} method. */ -public class UserInterruptException - extends RuntimeException -{ +public class UserInterruptException extends RuntimeException { private static final long serialVersionUID = 6172232572140736750L; private final String partialLine; - public UserInterruptException(String partialLine) - { + public UserInterruptException(String partialLine) { this.partialLine = partialLine; } /** * @return the partially entered line when ctrl-C was pressed */ - public String getPartialLine() - { + public String getPartialLine() { return partialLine; } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Widget.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Widget.java index d5add7fb1a7df..ae831c5eaadc6 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Widget.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Widget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -15,5 +15,4 @@ public interface Widget extends Binding { boolean apply(); - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/BufferImpl.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/BufferImpl.java index a3b2a21ac665a..a68ef229f8682 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/BufferImpl.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/BufferImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2017, the original author or authors. + * Copyright (c) 2002-2017, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,6 +8,7 @@ */ package jdk.internal.org.jline.reader.impl; +import java.util.Arrays; import java.util.Objects; import jdk.internal.org.jline.reader.Buffer; @@ -19,8 +20,7 @@ * @author Jason Dillon * @since 2.0 */ -public class BufferImpl implements Buffer -{ +public class BufferImpl implements Buffer { private int cursor = 0; private int cursorCol = -1; private int[] buffer; @@ -45,7 +45,7 @@ private BufferImpl(BufferImpl buffer) { this.g1 = buffer.g1; } - public BufferImpl copy () { + public BufferImpl copy() { return new BufferImpl(this); } @@ -106,7 +106,7 @@ private int adjust(int i) { * @param c the character to insert */ public void write(int c) { - write(new int[] { c }); + write(new int[] {c}); } /** @@ -120,7 +120,7 @@ public void write(int c, boolean overTyping) { if (overTyping) { delete(1); } - write(new int[] { c }); + write(new int[] {c}); } /** @@ -223,8 +223,7 @@ public int move(final int num) { if ((cursor + where) < 0) { where = -cursor; - } - else if ((cursor + where) > length()) { + } else if ((cursor + where) > length()) { where = length() - cursor; } @@ -369,4 +368,9 @@ private void moveGapToCursor() { g1 += l; } } + + @Override + public void zeroOut() { + Arrays.fill(buffer, 0); + } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/CompletionMatcherImpl.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/CompletionMatcherImpl.java index f829f13d51b2c..761ee1815c3d0 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/CompletionMatcherImpl.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/CompletionMatcherImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2021, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,26 +8,25 @@ */ package jdk.internal.org.jline.reader.impl; -import jdk.internal.org.jline.reader.Candidate; -import jdk.internal.org.jline.reader.CompletingParsedLine; -import jdk.internal.org.jline.reader.CompletionMatcher; -import jdk.internal.org.jline.reader.LineReader; -import jdk.internal.org.jline.utils.AttributedString; - import java.util.*; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; +import jdk.internal.org.jline.reader.Candidate; +import jdk.internal.org.jline.reader.CompletingParsedLine; +import jdk.internal.org.jline.reader.CompletionMatcher; +import jdk.internal.org.jline.reader.LineReader; +import jdk.internal.org.jline.utils.AttributedString; + public class CompletionMatcherImpl implements CompletionMatcher { protected Predicate exact; protected List>, Map>>> matchers; private Map> matching; private boolean caseInsensitive; - public CompletionMatcherImpl() { - } + public CompletionMatcherImpl() {} protected void reset(boolean caseInsensitive) { this.caseInsensitive = caseInsensitive; @@ -37,8 +36,13 @@ protected void reset(boolean caseInsensitive) { } @Override - public void compile(Map options, boolean prefix, CompletingParsedLine line - , boolean caseInsensitive, int errors, String originalGroupName) { + public void compile( + Map options, + boolean prefix, + CompletingParsedLine line, + boolean caseInsensitive, + int errors, + String originalGroupName) { reset(caseInsensitive); defaultMatchers(options, prefix, line, caseInsensitive, errors, originalGroupName); } @@ -47,15 +51,18 @@ public void compile(Map options, boolean prefix, Com public List matches(List candidates) { matching = Collections.emptyMap(); Map> sortedCandidates = sort(candidates); - for (Function>, - Map>> matcher : matchers) { + for (Function>, Map>> matcher : matchers) { matching = matcher.apply(sortedCandidates); if (!matching.isEmpty()) { break; } } - return !matching.isEmpty() ? matching.entrySet().stream().flatMap(e -> e.getValue().stream()).distinct().collect(Collectors.toList()) - : new ArrayList<>(); + return !matching.isEmpty() + ? matching.entrySet().stream() + .flatMap(e -> e.getValue().stream()) + .distinct() + .collect(Collectors.toList()) + : new ArrayList<>(); } @Override @@ -63,10 +70,12 @@ public Candidate exactMatch() { if (matching == null) { throw new IllegalStateException(); } - return matching.values().stream().flatMap(Collection::stream) + return matching.values().stream() + .flatMap(Collection::stream) .filter(Candidate::complete) .filter(c -> exact.test(c.value())) - .findFirst().orElse(null); + .findFirst() + .orElse(null); } @Override @@ -84,8 +93,13 @@ public String getCommonPrefix() { /** * Default JLine matchers */ - protected void defaultMatchers(Map options, boolean prefix, CompletingParsedLine line - , boolean caseInsensitive, int errors, String originalGroupName) { + protected void defaultMatchers( + Map options, + boolean prefix, + CompletingParsedLine line, + boolean caseInsensitive, + int errors, + String originalGroupName) { // Find matchers // TODO: glob completion String wd = line.word(); @@ -94,8 +108,7 @@ protected void defaultMatchers(Map options, boolean if (prefix) { matchers = new ArrayList<>(Arrays.asList( simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).startsWith(wp)), - simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wp)) - )); + simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wp)))); if (LineReader.Option.COMPLETE_MATCHER_TYPO.isSet(options)) { matchers.add(typoMatcher(wp, errors, caseInsensitive, originalGroupName)); } @@ -109,14 +122,14 @@ protected void defaultMatchers(Map options, boolean Pattern p1 = Pattern.compile(Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*"); Pattern p2 = Pattern.compile(".*" + Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*"); matchers = new ArrayList<>(Arrays.asList( - simpleMatcher(s -> p1.matcher(caseInsensitive ? s.toLowerCase() : s).matches()), - simpleMatcher(s -> p2.matcher(caseInsensitive ? s.toLowerCase() : s).matches()) - )); + simpleMatcher(s -> p1.matcher(caseInsensitive ? s.toLowerCase() : s) + .matches()), + simpleMatcher(s -> p2.matcher(caseInsensitive ? s.toLowerCase() : s) + .matches()))); } else { matchers = new ArrayList<>(Arrays.asList( simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).startsWith(wdi)), - simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wdi)) - )); + simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wdi)))); } if (LineReader.Option.COMPLETE_MATCHER_CAMELCASE.isSet(options)) { matchers.add(simpleMatcher(s -> camelMatch(wd, 0, s, 0))); @@ -128,18 +141,20 @@ protected void defaultMatchers(Map options, boolean } } - protected Function>, - Map>> simpleMatcher(Predicate predicate) { + protected Function>, Map>> simpleMatcher( + Predicate predicate) { return m -> m.entrySet().stream() .filter(e -> predicate.test(e.getKey())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } - protected Function>, - Map>> typoMatcher(String word, int errors, boolean caseInsensitive, String originalGroupName) { + protected Function>, Map>> typoMatcher( + String word, int errors, boolean caseInsensitive, String originalGroupName) { return m -> { Map> map = m.entrySet().stream() - .filter(e -> ReaderUtils.distance(word, caseInsensitive ? e.getKey().toLowerCase() : e.getKey()) < errors) + .filter(e -> ReaderUtils.distance( + word, caseInsensitive ? e.getKey().toLowerCase() : e.getKey()) + < errors) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); if (map.size() > 1) { map.computeIfAbsent(word, w -> new ArrayList<>()) @@ -178,7 +193,8 @@ private Map> sort(List candidates) { Map> sortedCandidates = new HashMap<>(); for (Candidate candidate : candidates) { sortedCandidates - .computeIfAbsent(AttributedString.fromAnsi(candidate.value()).toString(), s -> new ArrayList<>()) + .computeIfAbsent( + AttributedString.fromAnsi(candidate.value()).toString(), s -> new ArrayList<>()) .add(candidate); } return sortedCandidates; @@ -206,5 +222,4 @@ private String getCommonStart(String str1, String str2, boolean caseInsensitive) } return new String(s1, 0, len); } - -} \ No newline at end of file +} diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultExpander.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultExpander.java index b534b96ff2f04..e5e7735a38479 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultExpander.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultExpander.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -32,27 +32,25 @@ public String expandHistory(History history, String line) { if (unicode > 0) { escaped = (--unicode >= 0); sb.append(c); - } - else if (escaped) { + } else if (escaped) { if (c == 'u') { unicode = 4; } else { escaped = false; } sb.append(c); - } - else if (c == '\'') { + } else if (c == '\'') { inQuote = !inQuote; sb.append(c); - } - else if (inQuote) { + } else if (inQuote) { sb.append(c); - } - else { + } else { switch (c) { case '\\': - // any '\!' should be considered an expansion escape, so skip expansion and strip the escape character - // a leading '\^' should be considered an expansion escape, so skip expansion and strip the escape character + // any '\!' should be considered an expansion escape, so skip expansion and strip the escape + // character + // a leading '\^' should be considered an expansion escape, so skip expansion and strip the + // escape character // otherwise, add the escape escaped = true; sb.append(c); @@ -91,7 +89,8 @@ else if (inQuote) { if (history.size() == 0) { throw new IllegalArgumentException("!$: event not found"); } - String previous = history.get(history.index() - 1).trim(); + String previous = + history.get(history.index() - 1).trim(); int lastSpace = previous.lastIndexOf(' '); if (lastSpace != -1) { rep = previous.substring(lastSpace + 1); @@ -128,14 +127,18 @@ else if (inQuote) { try { idx = Integer.parseInt(line.substring(i1, i)); } catch (NumberFormatException e) { - throw new IllegalArgumentException((neg ? "!-" : "!") + line.substring(i1, i) + ": event not found"); + throw new IllegalArgumentException( + (neg ? "!-" : "!") + line.substring(i1, i) + ": event not found"); } if (neg && idx > 0 && idx <= history.size()) { rep = history.get(history.index() - idx); - } else if (!neg && idx > history.index() - history.size() && idx <= history.index()) { + } else if (!neg + && idx > history.index() - history.size() + && idx <= history.index()) { rep = history.get(idx - 1); } else { - throw new IllegalArgumentException((neg ? "!-" : "!") + line.substring(i1, i) + ": event not found"); + throw new IllegalArgumentException( + (neg ? "!-" : "!") + line.substring(i1, i) + ": event not found"); } break; default: diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java index ac286ad734f26..55d39efde27da 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2021, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -10,9 +10,9 @@ import java.util.regex.Pattern; +import jdk.internal.org.jline.reader.Highlighter; import jdk.internal.org.jline.reader.LineReader; import jdk.internal.org.jline.reader.LineReader.RegionType; -import jdk.internal.org.jline.reader.Highlighter; import jdk.internal.org.jline.utils.AttributedString; import jdk.internal.org.jline.utils.AttributedStringBuilder; import jdk.internal.org.jline.utils.AttributedStyle; @@ -57,7 +57,8 @@ public AttributedString highlight(LineReader reader, String buffer) { while (negativeStart > 0 && reader.getBuffer().atChar(negativeStart - 1) != '\n') { negativeStart--; } - while (negativeEnd < reader.getBuffer().length() - 1 && reader.getBuffer().atChar(negativeEnd + 1) != '\n') { + while (negativeEnd < reader.getBuffer().length() - 1 + && reader.getBuffer().atChar(negativeEnd + 1) != '\n') { negativeEnd++; } } @@ -104,5 +105,4 @@ public AttributedString highlight(LineReader reader, String buffer) { } return sb.toAttributedString(); } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java index d9b3a9948d505..26cdf6abc51af 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2020, the original author or authors. + * Copyright (c) 2002-2020, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -21,18 +21,18 @@ public class DefaultParser implements Parser { public enum Bracket { - ROUND, // () - CURLY, // {} - SQUARE, // [] - ANGLE // <> + ROUND, // () + CURLY, // {} + SQUARE, // [] + ANGLE // <> } public static class BlockCommentDelims { private final String start; private final String end; + public BlockCommentDelims(String start, String end) { - if (start == null || end == null - || start.isEmpty() || end.isEmpty() || start.equals(end)) { + if (start == null || end == null || start.isEmpty() || end.isEmpty() || start.equals(end)) { throw new IllegalArgumentException("Bad block comment delimiter!"); } this.start = start; @@ -185,22 +185,22 @@ public void setEofOnUnclosedBracket(Bracket... brackets) { int i = 0; for (Bracket b : bs) { switch (b) { - case ROUND: - openingBrackets[i] = '('; - closingBrackets[i] = ')'; - break; - case CURLY: - openingBrackets[i] = '{'; - closingBrackets[i] = '}'; - break; - case SQUARE: - openingBrackets[i] = '['; - closingBrackets[i] = ']'; - break; - case ANGLE: - openingBrackets[i] = '<'; - closingBrackets[i] = '>'; - break; + case ROUND: + openingBrackets[i] = '('; + closingBrackets[i] = ')'; + break; + case CURLY: + openingBrackets[i] = '{'; + closingBrackets[i] = '}'; + break; + case SQUARE: + openingBrackets[i] = '['; + closingBrackets[i] = ']'; + break; + case ANGLE: + openingBrackets[i] = '<'; + closingBrackets[i] = '>'; + break; } i++; } @@ -229,7 +229,6 @@ public boolean validVariableName(String name) { return name != null && regexVariable != null && name.matches(regexVariable); } - @Override public String getCommand(final String line) { String out = ""; @@ -296,7 +295,7 @@ public ParsedLine parse(final String line, final int cursor, ParseContext contex if (quoteStart < 0 && isQuoteChar(line, i) && !lineCommented && !blockCommented) { // Start a quote block quoteStart = i; - if (current.length()==0) { + if (current.length() == 0) { quotedWord = true; if (context == ParseContext.SPLIT_LINE) { current.append(line.charAt(i)); @@ -324,13 +323,15 @@ public ParsedLine parse(final String line, final int cursor, ParseContext contex } } else { // Delimiter - rawWordLength = handleDelimiterAndGetRawWordLength(current, words, rawWordStart, rawWordCursor, rawWordLength, i); + rawWordLength = handleDelimiterAndGetRawWordLength( + current, words, rawWordStart, rawWordCursor, rawWordLength, i); rawWordStart = i + 1; } } else { if (quoteStart < 0 && !blockCommented && (lineCommented || isLineCommentStarted(line, i))) { lineCommented = true; - } else if (quoteStart < 0 && !lineCommented + } else if (quoteStart < 0 + && !lineCommented && (blockCommented || isCommentDelim(line, i, blockCommentStart))) { if (blockCommented) { if (blockCommentEnd != null && isCommentDelim(line, i, blockCommentEnd)) { @@ -339,12 +340,12 @@ public ParsedLine parse(final String line, final int cursor, ParseContext contex } } else { blockCommented = true; - rawWordLength = handleDelimiterAndGetRawWordLength(current, words, rawWordStart, rawWordCursor, rawWordLength, i); + rawWordLength = handleDelimiterAndGetRawWordLength( + current, words, rawWordStart, rawWordCursor, rawWordLength, i); i += blockCommentStart == null ? 0 : blockCommentStart.length() - 1; rawWordStart = i + 1; } - } else if (quoteStart < 0 && !lineCommented - && isCommentDelim(line, i, blockCommentEnd)) { + } else if (quoteStart < 0 && !lineCommented && isCommentDelim(line, i, blockCommentEnd)) { current.append(line.charAt(i)); blockCommentInRightOrder = false; } else if (!isEscapeChar(line, i)) { @@ -377,16 +378,14 @@ && isCommentDelim(line, i, blockCommentEnd)) { throw new EOFError(-1, -1, "Escaped new line", "newline"); } if (eofOnUnclosedQuote && quoteStart >= 0) { - throw new EOFError(-1, -1, "Missing closing quote", line.charAt(quoteStart) == '\'' - ? "quote" : "dquote"); + throw new EOFError( + -1, -1, "Missing closing quote", line.charAt(quoteStart) == '\'' ? "quote" : "dquote"); } if (blockCommented) { - throw new EOFError(-1, -1, "Missing closing block comment delimiter", - "add: " + blockCommentEnd); + throw new EOFError(-1, -1, "Missing closing block comment delimiter", "add: " + blockCommentEnd); } if (!blockCommentInRightOrder) { - throw new EOFError(-1, -1, "Missing opening block comment delimiter", - "missing: " + blockCommentStart); + throw new EOFError(-1, -1, "Missing opening block comment delimiter", "missing: " + blockCommentStart); } if (bracketChecker.isClosingBracketMissing() || bracketChecker.isOpeningBracketMissing()) { String message = null; @@ -398,8 +397,13 @@ && isCommentDelim(line, i, blockCommentEnd)) { message = "Missing opening bracket"; missing = "missing: " + bracketChecker.getMissingOpeningBracket(); } - throw new EOFError(-1, -1, message, missing, - bracketChecker.getOpenBrackets(), bracketChecker.getNextClosingBracket()); + throw new EOFError( + -1, + -1, + message, + missing, + bracketChecker.getOpenBrackets(), + bracketChecker.getNextClosingBracket()); } } @@ -420,7 +424,13 @@ public boolean isDelimiter(final CharSequence buffer, final int pos) { return !isQuoted(buffer, pos) && !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos); } - private int handleDelimiterAndGetRawWordLength(StringBuilder current, List words, int rawWordStart, int rawWordCursor, int rawWordLength, int pos) { + private int handleDelimiterAndGetRawWordLength( + StringBuilder current, + List words, + int rawWordStart, + int rawWordCursor, + int rawWordLength, + int pos) { if (current.length() > 0) { words.add(current.toString()); current.setLength(0); // reset the arg @@ -470,7 +480,7 @@ private boolean isCommentDelim(final CharSequence buffer, final int pos, final S public boolean isLineCommentStarted(final CharSequence buffer, final int pos) { if (lineCommentDelims != null) { - for (String comment: lineCommentDelims) { + for (String comment : lineCommentDelims) { if (isCommentDelim(buffer, pos, comment)) { return true; } @@ -584,8 +594,8 @@ public void check(final CharSequence buffer, final int pos) { } else { bid = bracketId(closingBrackets, buffer, pos); if (bid >= 0) { - if (!nested.isEmpty() && bid == nested.get(nested.size()-1)) { - nested.remove(nested.size()-1); + if (!nested.isEmpty() && bid == nested.get(nested.size() - 1)) { + nested.remove(nested.size() - 1); } else { missingOpeningBracket = bid; } @@ -634,7 +644,7 @@ public String getNextClosingBracket() { } private int bracketId(final char[] brackets, final CharSequence buffer, final int pos) { - for (int i=0; i < brackets.length; i++) { + for (int i = 0; i < brackets.length; i++) { if (buffer.charAt(pos) == brackets[i]) { return i; } @@ -648,8 +658,7 @@ private int bracketId(final char[] brackets, final CharSequence buffer, final in * * @author Marc Prud'hommeaux */ - public class ArgumentList implements ParsedLine, CompletingParsedLine - { + public class ArgumentList implements ParsedLine, CompletingParsedLine { private final String line; private final List words; @@ -667,11 +676,21 @@ public class ArgumentList implements ParsedLine, CompletingParsedLine private final int rawWordLength; @Deprecated - public ArgumentList(final String line, final List words, - final int wordIndex, final int wordCursor, - final int cursor) { - this(line, words, wordIndex, wordCursor, cursor, - null, wordCursor, words.get(wordIndex).length()); + public ArgumentList( + final String line, + final List words, + final int wordIndex, + final int wordCursor, + final int cursor) { + this( + line, + words, + wordIndex, + wordCursor, + cursor, + null, + wordCursor, + words.get(wordIndex).length()); } /** @@ -685,10 +704,15 @@ public ArgumentList(final String line, final List words, * @param rawWordCursor the cursor position inside the raw word (i.e. including quotes and escape characters) * @param rawWordLength the raw word length, including quotes and escape characters */ - public ArgumentList(final String line, final List words, - final int wordIndex, final int wordCursor, - final int cursor, final String openingQuote, - final int rawWordCursor, final int rawWordLength) { + public ArgumentList( + final String line, + final List words, + final int wordIndex, + final int wordCursor, + final int cursor, + final String openingQuote, + final int rawWordCursor, + final int rawWordLength) { this.line = line; this.words = Collections.unmodifiableList(Objects.requireNonNull(words)); this.wordIndex = wordIndex; @@ -732,8 +756,8 @@ public CharSequence escape(CharSequence candidate, boolean complete) { Predicate needToBeEscaped; String quote = openingQuote; boolean middleQuotes = false; - if (openingQuote==null) { - for (int i=0; i < sb.length(); i++) { + if (openingQuote == null) { + for (int i = 0; i < sb.length(); i++) { if (isQuoteChar(sb, i)) { middleQuotes = true; break; @@ -746,7 +770,8 @@ public CharSequence escape(CharSequence candidate, boolean complete) { // Delimiters (spaces) don't need to be escaped, nor do other quotes, but everything else does. // Also, close the quote at the end if (openingQuote != null) { - needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i)) || String.valueOf(sb.charAt(i)).equals(openingQuote); + needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i)) + || String.valueOf(sb.charAt(i)).equals(openingQuote); } // Completion is protected by middle quotes: // Delimiters (spaces) don't need to be escaped, nor do quotes, but everything else does. @@ -756,8 +781,8 @@ else if (middleQuotes) { // No quote protection, need to escape everything: delimiter chars (spaces), quote chars // and escapes themselves else { - needToBeEscaped = i -> isDelimiterChar(sb, i) || isRawEscapeChar(sb.charAt(i)) - || isRawQuoteChar(sb.charAt(i)); + needToBeEscaped = i -> + isDelimiterChar(sb, i) || isRawEscapeChar(sb.charAt(i)) || isRawQuoteChar(sb.charAt(i)); } for (int i = 0; i < sb.length(); i++) { if (needToBeEscaped.test(i)) { @@ -792,5 +817,4 @@ public int rawWordLength() { return rawWordLength; } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/InputRC.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/InputRC.java new file mode 100644 index 0000000000000..0c3aee057a46d --- /dev/null +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/InputRC.java @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2002-2023, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package jdk.internal.org.jline.reader.impl; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import jdk.internal.org.jline.reader.LineReader; +import jdk.internal.org.jline.reader.Macro; +import jdk.internal.org.jline.reader.Reference; +import jdk.internal.org.jline.terminal.Terminal; +import jdk.internal.org.jline.utils.Log; + +public final class InputRC { + + public static void configure(LineReader reader, URL url) throws IOException { + try (InputStream is = url.openStream()) { + configure(reader, is); + } + } + + public static void configure(LineReader reader, InputStream is) throws IOException { + try (InputStreamReader r = new InputStreamReader(is)) { + configure(reader, r); + } + } + + public static void configure(LineReader reader, Reader r) throws IOException { + BufferedReader br; + if (r instanceof BufferedReader) { + br = (BufferedReader) r; + } else { + br = new BufferedReader(r); + } + + Terminal terminal = reader.getTerminal(); + + if (Terminal.TYPE_DUMB.equals(terminal.getType()) || Terminal.TYPE_DUMB_COLOR.equals(terminal.getType())) { + reader.getVariables().putIfAbsent(LineReader.EDITING_MODE, "dumb"); + } else { + reader.getVariables().putIfAbsent(LineReader.EDITING_MODE, "emacs"); + } + + reader.setKeyMap(LineReader.MAIN); + new InputRC(reader).parse(br); + if ("vi".equals(reader.getVariable(LineReader.EDITING_MODE))) { + reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.VIINS)); + } else if ("emacs".equals(reader.getVariable(LineReader.EDITING_MODE))) { + reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.EMACS)); + } else if ("dumb".equals(reader.getVariable(LineReader.EDITING_MODE))) { + reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.DUMB)); + } + } + + private final LineReader reader; + + private InputRC(LineReader reader) { + this.reader = reader; + } + + private void parse(BufferedReader br) throws IOException, IllegalArgumentException { + String line; + boolean parsing = true; + List ifsStack = new ArrayList<>(); + while ((line = br.readLine()) != null) { + try { + line = line.trim(); + if (line.length() == 0) { + continue; + } + if (line.charAt(0) == '#') { + continue; + } + int i = 0; + if (line.charAt(i) == '$') { + String cmd; + String args; + ++i; + while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { + i++; + } + int s = i; + while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { + i++; + } + cmd = line.substring(s, i); + while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { + i++; + } + s = i; + while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { + i++; + } + args = line.substring(s, i); + if ("if".equalsIgnoreCase(cmd)) { + ifsStack.add(parsing); + if (!parsing) { + continue; + } + if (args.startsWith("term=")) { + // TODO + } else if (args.startsWith("mode=")) { + String mode = (String) reader.getVariable(LineReader.EDITING_MODE); + parsing = args.substring("mode=".length()).equalsIgnoreCase(mode); + } else { + parsing = args.equalsIgnoreCase(reader.getAppName()); + } + } else if ("else".equalsIgnoreCase(cmd)) { + if (ifsStack.isEmpty()) { + throw new IllegalArgumentException("$else found without matching $if"); + } + boolean invert = true; + for (boolean b : ifsStack) { + if (!b) { + invert = false; + break; + } + } + if (invert) { + parsing = !parsing; + } + } else if ("endif".equalsIgnoreCase(cmd)) { + if (ifsStack.isEmpty()) { + throw new IllegalArgumentException("endif found without matching $if"); + } + parsing = ifsStack.remove(ifsStack.size() - 1); + } else if ("include".equalsIgnoreCase(cmd)) { + // TODO + } + continue; + } + if (!parsing) { + continue; + } + if (line.charAt(i++) == '"') { + boolean esc = false; + for (; ; i++) { + if (i >= line.length()) { + throw new IllegalArgumentException("Missing closing quote on line '" + line + "'"); + } + if (esc) { + esc = false; + } else if (line.charAt(i) == '\\') { + esc = true; + } else if (line.charAt(i) == '"') { + break; + } + } + } + while (i < line.length() && line.charAt(i) != ':' && line.charAt(i) != ' ' && line.charAt(i) != '\t') { + i++; + } + String keySeq = line.substring(0, i); + boolean equivalency = i + 1 < line.length() && line.charAt(i) == ':' && line.charAt(i + 1) == '='; + i++; + if (equivalency) { + i++; + } + if (keySeq.equalsIgnoreCase("set")) { + String key; + String val; + while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { + i++; + } + int s = i; + while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { + i++; + } + key = line.substring(s, i); + while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { + i++; + } + s = i; + while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { + i++; + } + val = line.substring(s, i); + setVar(reader, key, val); + } else { + while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { + i++; + } + int start = i; + if (i < line.length() && (line.charAt(i) == '\'' || line.charAt(i) == '\"')) { + char delim = line.charAt(i++); + boolean esc = false; + for (; ; i++) { + if (i >= line.length()) { + break; + } + if (esc) { + esc = false; + } else if (line.charAt(i) == '\\') { + esc = true; + } else if (line.charAt(i) == delim) { + break; + } + } + } + for (; i < line.length() && line.charAt(i) != ' ' && line.charAt(i) != '\t'; i++) + ; + String val = line.substring(Math.min(start, line.length()), Math.min(i, line.length())); + if (keySeq.charAt(0) == '"') { + keySeq = translateQuoted(keySeq); + } else { + // Bind key name + String keyName = + keySeq.lastIndexOf('-') > 0 ? keySeq.substring(keySeq.lastIndexOf('-') + 1) : keySeq; + char key = getKeyFromName(keyName); + keyName = keySeq.toLowerCase(); + keySeq = ""; + if (keyName.contains("meta-") || keyName.contains("m-")) { + keySeq += "\u001b"; + } + if (keyName.contains("control-") || keyName.contains("c-") || keyName.contains("ctrl-")) { + key = (char) (Character.toUpperCase(key) & 0x1f); + } + keySeq += key; + } + if (val.length() > 0 && (val.charAt(0) == '\'' || val.charAt(0) == '\"')) { + reader.getKeys().bind(new Macro(translateQuoted(val)), keySeq); + } else { + reader.getKeys().bind(new Reference(val), keySeq); + } + } + } catch (IllegalArgumentException e) { + Log.warn("Unable to parse user configuration: ", e); + } + } + } + + private static String translateQuoted(String keySeq) { + int i; + String str = keySeq.substring(1, keySeq.length() - 1); + StringBuilder sb = new StringBuilder(); + for (i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == '\\') { + boolean ctrl = str.regionMatches(i, "\\C-", 0, 3) || str.regionMatches(i, "\\M-\\C-", 0, 6); + boolean meta = str.regionMatches(i, "\\M-", 0, 3) || str.regionMatches(i, "\\C-\\M-", 0, 6); + i += (meta ? 3 : 0) + (ctrl ? 3 : 0) + (!meta && !ctrl ? 1 : 0); + if (i >= str.length()) { + break; + } + c = str.charAt(i); + if (meta) { + sb.append("\u001b"); + } + if (ctrl) { + c = c == '?' ? 0x7f : (char) (Character.toUpperCase(c) & 0x1f); + } + if (!meta && !ctrl) { + switch (c) { + case 'a': + c = 0x07; + break; + case 'b': + c = '\b'; + break; + case 'd': + c = 0x7f; + break; + case 'e': + c = 0x1b; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = 0x0b; + break; + case '\\': + c = '\\'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + c = 0; + for (int j = 0; j < 3; j++, i++) { + if (i >= str.length()) { + break; + } + int k = Character.digit(str.charAt(i), 8); + if (k < 0) { + break; + } + c = (char) (c * 8 + k); + } + c &= 0xFF; + break; + case 'x': + i++; + c = 0; + for (int j = 0; j < 2; j++, i++) { + if (i >= str.length()) { + break; + } + int k = Character.digit(str.charAt(i), 16); + if (k < 0) { + break; + } + c = (char) (c * 16 + k); + } + c &= 0xFF; + break; + case 'u': + i++; + c = 0; + for (int j = 0; j < 4; j++, i++) { + if (i >= str.length()) { + break; + } + int k = Character.digit(str.charAt(i), 16); + if (k < 0) { + break; + } + c = (char) (c * 16 + k); + } + break; + } + } + sb.append(c); + } else { + sb.append(c); + } + } + return sb.toString(); + } + + private static char getKeyFromName(String name) { + if ("DEL".equalsIgnoreCase(name) || "Rubout".equalsIgnoreCase(name)) { + return 0x7f; + } else if ("ESC".equalsIgnoreCase(name) || "Escape".equalsIgnoreCase(name)) { + return '\033'; + } else if ("LFD".equalsIgnoreCase(name) || "NewLine".equalsIgnoreCase(name)) { + return '\n'; + } else if ("RET".equalsIgnoreCase(name) || "Return".equalsIgnoreCase(name)) { + return '\r'; + } else if ("SPC".equalsIgnoreCase(name) || "Space".equalsIgnoreCase(name)) { + return ' '; + } else if ("Tab".equalsIgnoreCase(name)) { + return '\t'; + } else { + return name.charAt(0); + } + } + + static void setVar(LineReader reader, String key, String val) { + if (LineReader.KEYMAP.equalsIgnoreCase(key)) { + reader.setKeyMap(val); + return; + } + + for (LineReader.Option option : LineReader.Option.values()) { + if (option.name().toLowerCase(Locale.ENGLISH).replace('_', '-').equals(val)) { + if ("on".equalsIgnoreCase(val)) { + reader.setOpt(option); + } else if ("off".equalsIgnoreCase(val)) { + reader.unsetOpt(option); + } + return; + } + } + + reader.setVariable(key, val); + } +} diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/KillRing.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/KillRing.java index f89ed35d04e0d..2510fd9e32035 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/KillRing.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/KillRing.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java index ee8a434e1a211..94ed9ae450dc4 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2022, the original author or authors. + * Copyright (c) 2002-2023, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -18,6 +18,9 @@ import java.io.InputStream; import java.io.InterruptedIOException; import java.lang.reflect.Constructor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Instant; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -56,6 +59,7 @@ import static jdk.internal.org.jline.keymap.KeyMap.esc; import static jdk.internal.org.jline.keymap.KeyMap.range; import static jdk.internal.org.jline.keymap.KeyMap.translate; +import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_DISABLE_ALTERNATE_CHARSET; /** * A reader for terminal applications. It supports custom tab-completion, @@ -66,23 +70,27 @@ * @author Guillaume Nodet */ @SuppressWarnings("StatementWithEmptyBody") -public class LineReaderImpl implements LineReader, Flushable -{ +public class LineReaderImpl implements LineReader, Flushable { public static final char NULL_MASK = 0; + /** + * @deprecated use {@link #DEFAULT_TAB_WIDTH} and {@link #getTabWidth()} + */ + @Deprecated public static final int TAB_WIDTH = 4; + public static final int DEFAULT_TAB_WIDTH = 4; public static final String DEFAULT_WORDCHARS = "*?_-.[]~=/&;!#$%^(){}<>"; public static final String DEFAULT_REMOVE_SUFFIX_CHARS = " \t\n;&|"; public static final String DEFAULT_COMMENT_BEGIN = "#"; public static final String DEFAULT_SEARCH_TERMINATORS = "\033\012"; public static final String DEFAULT_BELL_STYLE = ""; - public static final int DEFAULT_LIST_MAX = 100; - public static final int DEFAULT_MENU_LIST_MAX = Integer.MAX_VALUE; - public static final int DEFAULT_ERRORS = 2; - public static final long DEFAULT_BLINK_MATCHING_PAREN = 500L; - public static final long DEFAULT_AMBIGUOUS_BINDING = 1000L; + public static final int DEFAULT_LIST_MAX = 100; + public static final int DEFAULT_MENU_LIST_MAX = Integer.MAX_VALUE; + public static final int DEFAULT_ERRORS = 2; + public static final long DEFAULT_BLINK_MATCHING_PAREN = 500L; + public static final long DEFAULT_AMBIGUOUS_BINDING = 1000L; public static final String DEFAULT_SECONDARY_PROMPT_PATTERN = "%M> "; public static final String DEFAULT_OTHERS_GROUP_NAME = "others"; public static final String DEFAULT_ORIGINAL_GROUP_NAME = "original"; @@ -96,9 +104,10 @@ public class LineReaderImpl implements LineReader, Flushable public static final String DEFAULT_COMPLETION_STYLE_LIST_GROUP = "fg:black,bold"; public static final String DEFAULT_COMPLETION_STYLE_LIST_SELECTION = DEFAULT_COMPLETION_STYLE_SELECTION; public static final String DEFAULT_COMPLETION_STYLE_LIST_BACKGROUND = "bg:bright-magenta"; - public static final int DEFAULT_INDENTATION = 0; - public static final int DEFAULT_FEATURES_MAX_BUFFER_SIZE = 1000; - public static final int DEFAULT_SUGGESTIONS_MIN_BUFFER_SIZE = 1; + public static final int DEFAULT_INDENTATION = 0; + public static final int DEFAULT_FEATURES_MAX_BUFFER_SIZE = 1000; + public static final int DEFAULT_SUGGESTIONS_MIN_BUFFER_SIZE = 1; + public static final String DEFAULT_SYSTEM_PROPERTY_PREFIX = "org.jline.reader.props."; private static final int MIN_ROWS = 3; @@ -109,6 +118,7 @@ public class LineReaderImpl implements LineReader, Flushable public static final String FOCUS_IN_SEQ = "\033[I"; public static final String FOCUS_OUT_SEQ = "\033[O"; + public static final int DEFAULT_MAX_REPEAT_COUNT = 9999; /** * Possible states in which the current readline operation may be in. @@ -197,27 +207,28 @@ protected enum BellType { protected int searchIndex = -1; protected boolean doAutosuggestion; - // Reading buffers protected final BindingReader bindingReader; - /** * VI character find */ protected int findChar; + protected int findDir; protected int findTailAdd; /** * VI history string search */ private int searchDir; + private String searchString; /** * Region state */ protected int regionMark; + protected RegionType regionActive; private boolean forceChar; @@ -232,7 +243,7 @@ protected enum BellType { protected KillRing killRing = new KillRing(); - protected UndoTree undo = new UndoTree<>(this::setBuffer); + protected UndoTree undo; protected boolean isUndo; /** @@ -242,7 +253,7 @@ protected enum BellType { /* * Current internal state of the line reader */ - protected State state = State.DONE; + protected State state = State.DONE; protected final AtomicBoolean startedReading = new AtomicBoolean(); protected boolean reading; @@ -278,7 +289,10 @@ protected enum BellType { */ protected List commandsBuffer = new ArrayList<>(); - int candidateStartPosition = 0; + protected int candidateStartPosition = 0; + + protected String alternateIn; + protected String alternateOut; public LineReaderImpl(Terminal terminal) throws IOException { this(terminal, terminal.getName(), null); @@ -288,6 +302,7 @@ public LineReaderImpl(Terminal terminal, String appName) throws IOException { this(terminal, appName, null); } + @SuppressWarnings("this-escape") public LineReaderImpl(Terminal terminal, String appName, Map variables) { Objects.requireNonNull(terminal, "terminal can not be null"); this.terminal = terminal; @@ -300,11 +315,40 @@ public LineReaderImpl(Terminal terminal, String appName, Map var } else { this.variables = new HashMap<>(); } + String prefix = getString(SYSTEM_PROPERTY_PREFIX, DEFAULT_SYSTEM_PROPERTY_PREFIX); + if (prefix != null) { + Properties sysProps = System.getProperties(); + for (String s : sysProps.stringPropertyNames()) { + if (s.startsWith(prefix)) { + String key = s.substring(prefix.length()); + InputRC.setVar(this, key, sysProps.getProperty(s)); + } + } + } + this.keyMaps = defaultKeyMaps(); + if (!Boolean.getBoolean(PROP_DISABLE_ALTERNATE_CHARSET)) { + this.alternateIn = Curses.tputs(terminal.getStringCapability(Capability.enter_alt_charset_mode)); + this.alternateOut = Curses.tputs(terminal.getStringCapability(Capability.exit_alt_charset_mode)); + } + undo = new UndoTree<>(this::setBuffer); builtinWidgets = builtinWidgets(); widgets = new HashMap<>(builtinWidgets); bindingReader = new BindingReader(terminal.reader()); + + String inputRc = getString(INPUT_RC_FILE_NAME, null); + if (inputRc != null) { + Path inputRcPath = Paths.get(inputRc); + if (Files.exists(inputRcPath)) { + try (InputStream is = Files.newInputStream(inputRcPath)) { + InputRC.configure(this, is); + } catch (Exception e) { + Log.debug("Error reading inputRc config file: ", inputRc, e); + } + } + } + doDisplay(); } @@ -489,7 +533,8 @@ public String readLine(String prompt, Character mask) throws UserInterruptExcept * @param buffer A string that will be set for editing. * @return A line that is read from the terminal, can never be null. */ - public String readLine(String prompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException { + public String readLine(String prompt, Character mask, String buffer) + throws UserInterruptException, EndOfFileException { return readLine(prompt, null, mask, buffer); } @@ -503,7 +548,8 @@ public String readLine(String prompt, Character mask, String buffer) throws User * @param buffer A string that will be set for editing. * @return A line that is read from the terminal, can never be null. */ - public String readLine(String prompt, String rightPrompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException { + public String readLine(String prompt, String rightPrompt, Character mask, String buffer) + throws UserInterruptException, EndOfFileException { return readLine(prompt, rightPrompt, mask != null ? new SimpleMaskingCallback(mask) : null, buffer); } @@ -517,7 +563,8 @@ public String readLine(String prompt, String rightPrompt, Character mask, String * @param buffer A string that will be set for editing. * @return A line that is read from the terminal, can never be null. */ - public String readLine(String prompt, String rightPrompt, MaskingCallback maskingCallback, String buffer) throws UserInterruptException, EndOfFileException { + public String readLine(String prompt, String rightPrompt, MaskingCallback maskingCallback, String buffer) + throws UserInterruptException, EndOfFileException { // prompt may be null // maskingCallback may be null // buffer may be null @@ -618,23 +665,14 @@ public String readLine(String prompt, String rightPrompt, MaskingCallback maskin // Move into application mode if (!dumb) { terminal.puts(Capability.keypad_xmit); - if (isSet(Option.AUTO_FRESH_LINE)) - callWidget(FRESH_LINE); - if (isSet(Option.MOUSE)) - terminal.trackMouse(Terminal.MouseTracking.Normal); - if (isSet(Option.BRACKETED_PASTE)) - terminal.writer().write(BRACKETED_PASTE_ON); - } else { - // For dumb terminals, we need to make sure that CR are ignored - Attributes attr = new Attributes(originalAttributes); - attr.setInputFlag(Attributes.InputFlag.IGNCR, true); - terminal.setAttributes(attr); + if (isSet(Option.AUTO_FRESH_LINE)) callWidget(FRESH_LINE); + if (isSet(Option.MOUSE)) terminal.trackMouse(Terminal.MouseTracking.Normal); + if (isSet(Option.BRACKETED_PASTE)) terminal.writer().write(BRACKETED_PASTE_ON); } callWidget(CALLBACK_INIT); - if (!isSet(Option.DISABLE_UNDO)) - undo.newState(buf.copy()); + if (!isSet(Option.DISABLE_UNDO)) undo.newState(buf.copy()); // Draw initial prompt redrawLine(); @@ -654,7 +692,8 @@ public String readLine(String prompt, String rightPrompt, MaskingCallback maskin throw new EndOfFileException().partialLine(buf.length() > 0 ? buf.toString() : null); } Log.trace("Binding: ", o); - if (buf.length() == 0 && getLastBinding().charAt(0) == originalAttributes.getControlChar(ControlChar.VEOF)) { + if (buf.length() == 0 + && getLastBinding().charAt(0) == originalAttributes.getControlChar(ControlChar.VEOF)) { throw new EndOfFileException(); } @@ -675,12 +714,17 @@ public String readLine(String prompt, String rightPrompt, MaskingCallback maskin try { lock.lock(); // Get executable widget - Buffer copy = buf.length() <= getInt(FEATURES_MAX_BUFFER_SIZE, DEFAULT_FEATURES_MAX_BUFFER_SIZE) ? buf.copy() : null; + Buffer copy = buf.length() <= getInt(FEATURES_MAX_BUFFER_SIZE, DEFAULT_FEATURES_MAX_BUFFER_SIZE) + ? buf.copy() + : null; Widget w = getWidget(o); if (!w.apply()) { beep(); } - if (!isSet(Option.DISABLE_UNDO) && !isUndo && copy != null && buf.length() <= getInt(FEATURES_MAX_BUFFER_SIZE, DEFAULT_FEATURES_MAX_BUFFER_SIZE) + if (!isSet(Option.DISABLE_UNDO) + && !isUndo + && copy != null + && buf.length() <= getInt(FEATURES_MAX_BUFFER_SIZE, DEFAULT_FEATURES_MAX_BUFFER_SIZE) && !copy.toString().equals(buf.toString())) { undo.newState(buf.copy()); } @@ -718,13 +762,18 @@ public String readLine(String prompt, String rightPrompt, MaskingCallback maskin } else { throw e; } - } - finally { + } finally { + AtomicBoolean interrupted = new AtomicBoolean(Thread.interrupted()); try { lock.lock(); this.reading = false; + Terminal.SignalHandler tmpHandler = terminal.handle(Signal.INT, s -> interrupted.set(true)); + if (previousIntrHandler == null) { + previousIntrHandler = tmpHandler; + } + cleanup(); if (originalAttributes != null) { terminal.setAttributes(originalAttributes); @@ -741,13 +790,15 @@ public String readLine(String prompt, String rightPrompt, MaskingCallback maskin } finally { lock.unlock(); startedReading.set(false); + if (interrupted.get()) { + Thread.currentThread().interrupt(); + } } } } private boolean isTerminalDumb() { - return Terminal.TYPE_DUMB.equals(terminal.getType()) - || Terminal.TYPE_DUMB_COLOR.equals(terminal.getType()); + return Terminal.TYPE_DUMB.equals(terminal.getType()) || Terminal.TYPE_DUMB_COLOR.equals(terminal.getType()); } private void doDisplay() { @@ -757,8 +808,7 @@ private void doDisplay() { display = new Display(terminal, false); display.resize(size.getRows(), size.getColumns()); - if (isSet(Option.DELAY_LINE_WRAP)) - display.setDelayLineWrap(true); + if (isSet(Option.DELAY_LINE_WRAP)) display.setDelayLineWrap(true); } @Override @@ -965,8 +1015,10 @@ public Binding readBinding(KeyMap keys, KeyMap local) { if (!YANK_POP.equals(ref) && !YANK.equals(ref)) { killRing.resetLastYank(); } - if (!KILL_LINE.equals(ref) && !KILL_WHOLE_LINE.equals(ref) - && !BACKWARD_KILL_WORD.equals(ref) && !KILL_WORD.equals(ref)) { + if (!KILL_LINE.equals(ref) + && !KILL_WHOLE_LINE.equals(ref) + && !BACKWARD_KILL_WORD.equals(ref) + && !KILL_WORD.equals(ref)) { killRing.resetLastKill(); } } @@ -1083,7 +1135,7 @@ public void editAndAddInBuffer(File file) throws Exception { if (isSet(Option.BRACKETED_PASTE)) { terminal.writer().write(BRACKETED_PASTE_OFF); } - Constructor ctor = Class.forName("jdk.internal.org.jline.builtins.Nano").getConstructor(Terminal.class, File.class); + Constructor ctor = Class.forName("org.jline.builtins.Nano").getConstructor(Terminal.class, File.class); Editor editor = (Editor) ctor.newInstance(terminal, new File(file.getParent())); editor.setRestricted(true); editor.open(Collections.singletonList(file.getName())); @@ -1097,6 +1149,10 @@ public void editAndAddInBuffer(File file) throws Exception { } } + protected int getTabWidth() { + return getInt(LineReader.TAB_WIDTH, DEFAULT_TAB_WIDTH); + } + // // Widget implementation // @@ -1137,26 +1193,27 @@ protected String finish(String str) { } // we only add it to the history if the buffer is not empty - if (historyLine != null && historyLine.length() > 0 ) { + if (historyLine != null && historyLine.length() > 0) { history.add(Instant.now(), historyLine); } return str; } - protected void handleSignal(Signal signal) { + protected synchronized void handleSignal(Signal signal) { doAutosuggestion = false; if (signal == Signal.WINCH) { + size.copy(terminal.getBufferSize()); + display.resize(size.getRows(), size.getColumns()); Status status = Status.getStatus(terminal, false); if (status != null) { - status.hardReset(); + status.resize(size); + status.reset(); } - size.copy(terminal.getBufferSize()); - display.resize(size.getRows(), size.getColumns()); - // restores prompt but also prevents scrolling in consoleZ, see #492 - // redrawLine(); + terminal.puts(Capability.carriage_return); + terminal.puts(Capability.clr_eos); + redrawLine(); redisplay(); - } - else if (signal == Signal.CONT) { + } else if (signal == Signal.CONT) { terminal.enterRawMode(); size.copy(terminal.getBufferSize()); display.resize(size.getRows(), size.getColumns()); @@ -1200,13 +1257,11 @@ protected Widget getWidget(Object binding) { // public void setPrompt(final String prompt) { - this.prompt = (prompt == null ? AttributedString.EMPTY - : expandPromptPattern(prompt, 0, "", 0)); + this.prompt = (prompt == null ? AttributedString.EMPTY : expandPromptPattern(prompt, 0, "", 0)); } public void setRightPrompt(final String rightPrompt) { - this.rightPrompt = (rightPrompt == null ? AttributedString.EMPTY - : expandPromptPattern(rightPrompt, 0, "", 0)); + this.rightPrompt = (rightPrompt == null ? AttributedString.EMPTY : expandPromptPattern(rightPrompt, 0, "", 0)); } protected void setBuffer(Buffer buffer) { @@ -1233,7 +1288,7 @@ protected void setBuffer(final String buffer) { * @param op The incoming operation to remap * @return The remaped operation */ - protected String viDeleteChangeYankToRemap (String op) { + protected String viDeleteChangeYankToRemap(String op) { switch (op) { case SEND_BREAK: case BACKWARD_CHAR: @@ -1292,7 +1347,6 @@ protected boolean isInViCmdMode() { return VICMD.equals(keyMap); } - // // Movement // @@ -1332,7 +1386,6 @@ protected boolean viBackwardChar() { return true; } - // // Word movement // @@ -1365,9 +1418,7 @@ protected boolean viForwardWord() { buf.move(1); } } else { - while (buf.cursor() < buf.length() - && !isViAlphaNum(buf.currChar()) - && !isWhitespace(buf.currChar())) { + while (buf.cursor() < buf.length() && !isViAlphaNum(buf.currChar()) && !isWhitespace(buf.currChar())) { buf.move(1); } } @@ -1375,9 +1426,7 @@ protected boolean viForwardWord() { return true; } int nl = buf.currChar() == '\n' ? 1 : 0; - while (buf.cursor() < buf.length() - && nl < 2 - && isWhitespace(buf.currChar())) { + while (buf.cursor() < buf.length() && nl < 2 && isWhitespace(buf.currChar())) { buf.move(1); nl += buf.currChar() == '\n' ? 1 : 0; } @@ -1397,9 +1446,7 @@ protected boolean viForwardBlankWord() { return true; } int nl = buf.currChar() == '\n' ? 1 : 0; - while (buf.cursor() < buf.length() - && nl < 2 - && isWhitespace(buf.currChar())) { + while (buf.cursor() < buf.length() && nl < 2 && isWhitespace(buf.currChar())) { buf.move(1); nl += buf.currChar() == '\n' ? 1 : 0; } @@ -1451,7 +1498,9 @@ protected boolean viForwardWordEnd() { } } else { buf.move(1); - while (buf.cursor() < buf.length() && !isViAlphaNum(buf.nextChar()) && !isWhitespace(buf.nextChar())) { + while (buf.cursor() < buf.length() + && !isViAlphaNum(buf.nextChar()) + && !isWhitespace(buf.nextChar())) { buf.move(1); } } @@ -1720,9 +1769,7 @@ protected boolean capitalizeWord() { buf.move(1); } while (buf.cursor() < buf.length() && isWord(buf.currChar())) { - buf.currChar(first - ? Character.toUpperCase(buf.currChar()) - : Character.toLowerCase(buf.currChar())); + buf.currChar(first ? Character.toUpperCase(buf.currChar()) : Character.toLowerCase(buf.currChar())); buf.move(1); first = false; } @@ -1810,7 +1857,8 @@ protected boolean transposeWords() { sta1--; } end1 = sta1; - while (end1 < lend && !isDelimiter(buf.atChar(++end1))); + while (end1 < lend && !isDelimiter(buf.atChar(++end1))) + ; if (neg) { end2 = sta1 - 1; while (end2 > lstart && isDelimiter(buf.atChar(end2 - 1))) { @@ -1819,9 +1867,11 @@ protected boolean transposeWords() { if (end2 < lstart) { // No word before, use the word after sta2 = end1; - while (isDelimiter(buf.atChar(++sta2))); + while (isDelimiter(buf.atChar(++sta2))) + ; end2 = sta2; - while (end2 < lend && !isDelimiter(buf.atChar(++end2))); + while (end2 < lend && !isDelimiter(buf.atChar(++end2))) + ; } else { sta2 = end2; while (sta2 > lstart && !isDelimiter(buf.atChar(sta2 - 1))) { @@ -1830,7 +1880,8 @@ protected boolean transposeWords() { } } else { sta2 = end1; - while (sta2 < lend && isDelimiter(buf.atChar(++sta2))); + while (sta2 < lend && isDelimiter(buf.atChar(++sta2))) + ; if (sta2 == lend) { // No word after, use the word before end2 = sta1; @@ -1843,19 +1894,24 @@ protected boolean transposeWords() { } } else { end2 = sta2; - while (end2 < lend && !isDelimiter(buf.atChar(++end2))) ; + while (end2 < lend && !isDelimiter(buf.atChar(++end2))) + ; } } if (sta1 < sta2) { - String res = buf.substring(0, sta1) + buf.substring(sta2, end2) - + buf.substring(end1, sta2) + buf.substring(sta1, end1) + String res = buf.substring(0, sta1) + + buf.substring(sta2, end2) + + buf.substring(end1, sta2) + + buf.substring(sta1, end1) + buf.substring(end2); buf.clear(); buf.write(res); buf.cursor(neg ? end1 : end2); } else { - String res = buf.substring(0, sta2) + buf.substring(sta1, end1) - + buf.substring(end2, sta1) + buf.substring(sta2, end2) + String res = buf.substring(0, sta2) + + buf.substring(sta1, end1) + + buf.substring(end2, sta1) + + buf.substring(sta2, end2) + buf.substring(end1); buf.clear(); buf.write(res); @@ -1988,11 +2044,11 @@ private boolean vifindchar(boolean repeat) { while (count-- > 0) { do { buf.move(findDir); - } while (buf.cursor() > 0 && buf.cursor() < buf.length() + } while (buf.cursor() > 0 + && buf.cursor() < buf.length() && buf.currChar() != findChar && buf.currChar() != '\n'); - if (buf.cursor() <= 0 || buf.cursor() >= buf.length() - || buf.currChar() == '\n') { + if (buf.cursor() <= 0 || buf.cursor() >= buf.length() || buf.currChar() == '\n') { buf.cursor(cursor); return false; } @@ -2211,18 +2267,17 @@ protected boolean undefinedKey() { * character or if there was no matching bracket. */ protected boolean doViMatchBracket() { - int pos = buf.cursor(); + int pos = buf.cursor(); if (pos == buf.length()) { return false; } - int type = getBracketType(buf.atChar(pos)); - int move = (type < 0) ? -1 : 1; - int count = 1; + int type = getBracketType(buf.atChar(pos)); + int move = (type < 0) ? -1 : 1; + int count = 1; - if (type == 0) - return false; + if (type == 0) return false; while (count > 0) { pos += move; @@ -2235,8 +2290,7 @@ protected boolean doViMatchBracket() { int curType = getBracketType(buf.atChar(pos)); if (curType == type) { ++count; - } - else if (curType == -type) { + } else if (curType == -type) { --count; } } @@ -2245,8 +2299,7 @@ else if (curType == -type) { * Slight adjustment for delete-to, yank-to, change-to to ensure * that the matching paren is consumed */ - if (move > 0 && isInViMoveOperation()) - ++pos; + if (move > 0 && isInViMoveOperation()) ++pos; buf.cursor(pos); return true; @@ -2259,14 +2312,20 @@ else if (curType == -type) { * @return 1 is square, 2 curly, 3 parent, or zero for none. The value * will be negated if it is the closing form of the bracket. */ - protected int getBracketType (int ch) { + protected int getBracketType(int ch) { switch (ch) { - case '[': return 1; - case ']': return -1; - case '{': return 2; - case '}': return -2; - case '(': return 3; - case ')': return -3; + case '[': + return 1; + case ']': + return -1; + case '{': + return 2; + case '}': + return -2; + case '(': + return 3; + case ')': + return -3; default: return 0; } @@ -2331,7 +2390,7 @@ protected boolean sendBreak() { buf.clear(); println(); redrawLine(); -// state = State.INTERRUPT; + // state = State.INTERRUPT; return false; } return true; @@ -2378,6 +2437,10 @@ protected boolean negArgument() { protected boolean digitArgument() { String s = getLastBinding(); repeatCount = (repeatCount * 10) + s.charAt(s.length() - 1) - '0'; + int maxRepeatCount = getInt(MAX_REPEAT_COUNT, DEFAULT_MAX_REPEAT_COUNT); + if (repeatCount > maxRepeatCount) { + throw new IllegalArgumentException("digit argument should be less than " + maxRepeatCount); + } isArgDigit = true; return true; } @@ -2438,10 +2501,12 @@ protected boolean viYankTo() { protected boolean viYankWholeLine() { int s, e; int p = buf.cursor(); - while (buf.move(-1) == -1 && buf.prevChar() != '\n') ; + while (buf.move(-1) == -1 && buf.prevChar() != '\n') + ; s = buf.cursor(); for (int i = 0; i < repeatCount; i++) { - while (buf.move(1) == 1 && buf.prevChar() != '\n') ; + while (buf.move(1) == 1 && buf.prevChar() != '\n') + ; } e = buf.cursor(); yankBuffer = buf.substring(s, e); @@ -2554,15 +2619,19 @@ protected boolean historyIncrementalSearchBackward() { return doSearchHistory(true); } - static class Pair { - final U u; final V v; + static class Pair { + final U u; + final V v; + public Pair(U u, V v) { this.u = u; this.v = v; } + public U getU() { return u; } + public V getV() { return v; } @@ -2575,7 +2644,8 @@ protected boolean doSearchHistory(boolean backward) { KeyMap terminators = new KeyMap<>(); getString(SEARCH_TERMINATORS, DEFAULT_SEARCH_TERMINATORS) - .codePoints().forEach(c -> bind(terminators, ACCEPT_LINE, new String(Character.toChars(c)))); + .codePoints() + .forEach(c -> bind(terminators, ACCEPT_LINE, new String(Character.toChars(c)))); Buffer originalBuffer = buf.copy(); searchIndex = -1; @@ -2583,8 +2653,8 @@ protected boolean doSearchHistory(boolean backward) { searchBackward = backward; searchFailing = false; post = () -> new AttributedString((searchFailing ? "failing" + " " : "") - + (searchBackward ? "bck-i-search" : "fwd-i-search") - + ": " + searchTerm + "_"); + + (searchBackward ? "bck-i-search" : "fwd-i-search") + + ": " + searchTerm + "_"); redisplay(); try { @@ -2630,8 +2700,9 @@ protected boolean doSearchHistory(boolean backward) { searchFailing = false; } else { boolean caseInsensitive = isSet(Option.CASE_INSENSITIVE_SEARCH); - Pattern pat = Pattern.compile(pattern, caseInsensitive ? Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE - : Pattern.UNICODE_CASE); + Pattern pat = Pattern.compile( + pattern, + caseInsensitive ? Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE : Pattern.UNICODE_CASE); Pair pair = null; if (searchBackward) { boolean nextOnly = next; @@ -2641,7 +2712,11 @@ protected boolean doSearchHistory(boolean backward) { .orElse(null); if (pair == null) { pair = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(history.reverseIterator(searchIndex < 0 ? history.last() : searchIndex - 1), Spliterator.ORDERED), false) + Spliterators.spliteratorUnknownSize( + history.reverseIterator( + searchIndex < 0 ? history.last() : searchIndex - 1), + Spliterator.ORDERED), + false) .flatMap(e -> matches(pat, e.line(), e.index()).stream()) .findFirst() .orElse(null); @@ -2654,7 +2729,11 @@ protected boolean doSearchHistory(boolean backward) { .orElse(null); if (pair == null) { pair = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(history.iterator((searchIndex < 0 ? history.last() : searchIndex) + 1), Spliterator.ORDERED), false) + Spliterators.spliteratorUnknownSize( + history.iterator( + (searchIndex < 0 ? history.last() : searchIndex) + 1), + Spliterator.ORDERED), + false) .flatMap(e -> matches(pat, e.line(), e.index()).stream()) .findFirst() .orElse(null); @@ -2714,7 +2793,10 @@ private String doGetSearchPattern() { sb.append("\\E"); inQuote = false; } - sb.append("[").append(Character.toLowerCase(c)).append(Character.toUpperCase(c)).append("]"); + sb.append("[") + .append(Character.toLowerCase(c)) + .append(Character.toUpperCase(c)) + .append("]"); } else { if (!inQuote) { sb.append("\\Q"); @@ -2742,8 +2824,7 @@ private void pushBackBinding(boolean skip) { } protected boolean historySearchForward() { - if (historyBuffer == null || buf.length() == 0 - || !buf.toString().equals(history.current())) { + if (historyBuffer == null || buf.length() == 0 || !buf.toString().equals(history.current())) { historyBuffer = buf.copy(); searchBuffer = getFirstWord(); } @@ -2791,8 +2872,7 @@ private CharSequence getFirstWord() { } protected boolean historySearchBackward() { - if (historyBuffer == null || buf.length() == 0 - || !buf.toString().equals(history.current())) { + if (historyBuffer == null || buf.length() == 0 || !buf.toString().equals(history.current())) { historyBuffer = buf.copy(); searchBuffer = getFirstWord(); } @@ -2984,7 +3064,7 @@ protected boolean acceptLine() { } void indention(int nb, StringBuilder sb) { - int indent = getInt(INDENTATION, DEFAULT_INDENTATION)*nb; + int indent = getInt(INDENTATION, DEFAULT_INDENTATION) * nb; for (int i = 0; i < indent; i++) { sb.append(' '); } @@ -3017,7 +3097,6 @@ protected boolean overwriteMode() { return true; } - // // History Control // @@ -3108,13 +3187,11 @@ protected boolean downHistory() { } protected boolean viUpLineOrHistory() { - return upLine() - || upHistory() && viFirstNonBlank(); + return upLine() || upHistory() && viFirstNonBlank(); } protected boolean viDownLineOrHistory() { - return downLine() - || downHistory() && viFirstNonBlank(); + return downLine() || downHistory() && viFirstNonBlank(); } protected boolean upLine() { @@ -3175,8 +3252,7 @@ protected boolean viChangeWholeLine() { } protected boolean viChangeEol() { - return viChange(buf.cursor(), buf.length()) - && setKeyMap(VIINS); + return viChange(buf.cursor(), buf.length()) && setKeyMap(VIINS); } protected boolean viKillEol() { @@ -3199,7 +3275,8 @@ protected boolean quotedInsert() { protected boolean viJoin() { if (buf.down()) { - while (buf.move(-1) == -1 && buf.prevChar() != '\n') ; + while (buf.move(-1) == -1 && buf.prevChar() != '\n') + ; buf.backspace(); buf.write(' '); buf.move(-1); @@ -3253,14 +3330,16 @@ protected boolean viEndOfLine() { protected boolean beginningOfLine() { while (count-- > 0) { - while (buf.move(-1) == -1 && buf.prevChar() != '\n') ; + while (buf.move(-1) == -1 && buf.prevChar() != '\n') + ; } return true; } protected boolean endOfLine() { while (count-- > 0) { - while (buf.move(1) == 1 && buf.currChar() != '\n') ; + while (buf.move(1) == 1 && buf.currChar() != '\n') + ; } return true; } @@ -3384,7 +3463,7 @@ protected boolean doViDeleteOrChange(int startPos, int endPos, boolean isChange) // what is really happening is that if we are in "move-mode" then the // cursor can't be moved off the end of the line, but in "edit-mode" it // is ok, but I have no easy way of knowing which mode we are in. - if (! isChange && startPos > 0 && startPos == buf.length()) { + if (!isChange && startPos > 0 && startPos == buf.length()) { buf.move(-1); } return true; @@ -3424,14 +3503,16 @@ protected boolean viYankTo(int startPos, int endPos) { } protected boolean viOpenLineAbove() { - while (buf.move(-1) == -1 && buf.prevChar() != '\n') ; + while (buf.move(-1) == -1 && buf.prevChar() != '\n') + ; buf.write('\n'); buf.move(-1); return setKeyMap(VIINS); } protected boolean viOpenLineBelow() { - while (buf.move(1) == 1 && buf.currChar() != '\n') ; + while (buf.move(1) == 1 && buf.currChar() != '\n') + ; buf.write('\n'); return setKeyMap(VIINS); } @@ -3443,11 +3524,12 @@ protected boolean viOpenLineBelow() { */ protected boolean viPutAfter() { if (yankBuffer.indexOf('\n') >= 0) { - while (buf.move(1) == 1 && buf.currChar() != '\n'); + while (buf.move(1) == 1 && buf.currChar() != '\n') + ; buf.move(1); putString(yankBuffer); - buf.move(- yankBuffer.length()); - } else if (yankBuffer.length () != 0) { + buf.move(-yankBuffer.length()); + } else if (yankBuffer.length() != 0) { if (buf.cursor() < buf.length()) { buf.move(1); } @@ -3461,10 +3543,11 @@ protected boolean viPutAfter() { protected boolean viPutBefore() { if (yankBuffer.indexOf('\n') >= 0) { - while (buf.move(-1) == -1 && buf.prevChar() != '\n'); + while (buf.move(-1) == -1 && buf.prevChar() != '\n') + ; putString(yankBuffer); - buf.move(- yankBuffer.length()); - } else if (yankBuffer.length () != 0) { + buf.move(-yankBuffer.length()); + } else if (yankBuffer.length() != 0) { if (buf.cursor() > 0) { buf.move(-1); } @@ -3681,7 +3764,7 @@ protected Map builtinWidgets() { addBuiltinWidget(widgets, MENU_EXPAND_OR_COMPLETE, this::menuExpandOrComplete); addBuiltinWidget(widgets, NEG_ARGUMENT, this::negArgument); addBuiltinWidget(widgets, OVERWRITE_MODE, this::overwriteMode); -// addBuiltinWidget(widgets, QUIT, this::quit); + // addBuiltinWidget(widgets, QUIT, this::quit); addBuiltinWidget(widgets, QUOTED_INSERT, this::quotedInsert); addBuiltinWidget(widgets, REDISPLAY, this::redisplay); addBuiltinWidget(widgets, REDRAW_LINE, this::redrawLine); @@ -3774,6 +3857,7 @@ private Widget namedWidget(String name, Widget widget) { public String toString() { return name; } + @Override public boolean apply() { return widget.apply(); @@ -3797,14 +3881,11 @@ protected void redisplay(boolean flush) { Status status = Status.getStatus(terminal, false); if (status != null) { - if (terminal.getType().startsWith(AbstractWindowsTerminal.TYPE_WINDOWS)) { - status.resize(); - } status.redraw(); } if (size.getRows() > 0 && size.getRows() < MIN_ROWS) { - AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH); + AttributedStringBuilder sb = new AttributedStringBuilder().tabs(getTabWidth()); sb.append(prompt); concat(getHighlightedBuffer(buf.toString()).columnSplitLength(Integer.MAX_VALUE), sb); @@ -3877,14 +3958,15 @@ protected void redisplay(boolean flush) { int cursorNewLinesId = -1; int cursorColPos = -1; if (size.getColumns() > 0) { - AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH); + AttributedStringBuilder sb = new AttributedStringBuilder().tabs(getTabWidth()); sb.append(prompt); String buffer = buf.upToCursor(); if (maskingCallback != null) { buffer = maskingCallback.display(buffer); } sb.append(insertSecondaryPrompts(new AttributedString(buffer), secondaryPrompts, false)); - List promptLines = sb.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); + List promptLines = + sb.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); if (!promptLines.isEmpty()) { cursorNewLinesId = promptLines.size() - 1; cursorColPos = promptLines.get(promptLines.size() - 1).columnLength(); @@ -3904,7 +3986,7 @@ protected void redisplay(boolean flush) { int lineId = newLines.size() - displaySize + 1; int endId = displaySize; int startId = 1; - if (lineId > cursorNewLinesId) { + if (lineId > cursorNewLinesId) { lineId = cursorNewLinesId; endId = displaySize - 1; startId = 0; @@ -3949,10 +4031,9 @@ private String matchPreviousCommand(String buffer) { } History history = getHistory(); StringBuilder sb = new StringBuilder(); - for (char c: buffer.replace("\\", "\\\\").toCharArray()) { - if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '*' - || c == '$' || c == '.' || c == '?' || c == '+' || c == '|' || c == '<' || c == '>' || c == '!' - || c == '-') { + for (char c : buffer.replace("\\", "\\\\").toCharArray()) { + if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '*' || c == '$' + || c == '.' || c == '?' || c == '+' || c == '|' || c == '<' || c == '>' || c == '!' || c == '-') { sb.append('\\'); } sb.append(c); @@ -3984,7 +4065,7 @@ public AttributedString getDisplayedBufferWithPrompts(List sec AttributedString attBuf = getHighlightedBuffer(buf.toString()); AttributedString tNewBuf = insertSecondaryPrompts(attBuf, secondaryPrompts); - AttributedStringBuilder full = new AttributedStringBuilder().tabs(TAB_WIDTH); + AttributedStringBuilder full = new AttributedStringBuilder().tabs(getTabWidth()); full.append(prompt); full.append(tNewBuf); if (doAutosuggestion && !isTerminalDumb()) { @@ -4040,15 +4121,15 @@ private AttributedString getHighlightedBuffer(String buffer) { if (maskingCallback != null) { buffer = maskingCallback.display(buffer); } - if (highlighter != null && !isSet(Option.DISABLE_HIGHLIGHTER) + if (highlighter != null + && !isSet(Option.DISABLE_HIGHLIGHTER) && buffer.length() < getInt(FEATURES_MAX_BUFFER_SIZE, DEFAULT_FEATURES_MAX_BUFFER_SIZE)) { return highlighter.highlight(this, buffer); } return new AttributedString(buffer); } - private AttributedString expandPromptPattern(String pattern, int padToWidth, - String message, int line) { + private AttributedString expandPromptPattern(String pattern, int padToWidth, String message, int line) { ArrayList parts = new ArrayList<>(); boolean isHidden = false; int padPartIndex = -1; @@ -4065,7 +4146,8 @@ private AttributedString expandPromptPattern(String pattern, int padToWidth, if (ch == '%' && i < plen) { int count = 0; boolean countSeen = false; - decode: while (true) { + decode: + while (true) { ch = pattern.charAt(i++); switch (ch) { case '{': @@ -4073,7 +4155,7 @@ private AttributedString expandPromptPattern(String pattern, int padToWidth, String str = sb.toString(); AttributedString astr; if (!isHidden) { - astr = AttributedString.fromAnsi(str); + astr = fromAnsi(str); cols += astr.columnLength(); } else { astr = new AttributedString(str, AttributedStyle.HIDDEN); @@ -4096,12 +4178,10 @@ private AttributedString expandPromptPattern(String pattern, int padToWidth, sb.append(getInt(LINE_OFFSET, 0) + line); break decode; case 'M': - if (message != null) - sb.append(message); + if (message != null) sb.append(message); break decode; case 'P': - if (countSeen && count >= 0) - padToWidth = count; + if (countSeen && count >= 0) padToWidth = count; if (i < plen) { padChar = pattern.charAt(i++); // FIXME check surrogate @@ -4140,25 +4220,28 @@ private AttributedString expandPromptPattern(String pattern, int padToWidth, break decode; } } - } else - sb.append(ch); + } else sb.append(ch); } if (padToWidth > cols && padToWidth > 0) { int padCharCols = WCWidth.wcwidth(padChar); int padCount = (padToWidth - cols) / padCharCols; sb = padPartString; - while (--padCount >= 0) - sb.insert(padPos, (char) padChar); // FIXME if wide - parts.set(padPartIndex, AttributedString.fromAnsi(sb.toString())); + while (--padCount >= 0) sb.insert(padPos, (char) padChar); // FIXME if wide + parts.set(padPartIndex, fromAnsi(sb.toString())); } return AttributedString.join(null, parts); } + private AttributedString fromAnsi(String str) { + return AttributedString.fromAnsi(str, Collections.singletonList(0), alternateIn, alternateOut); + } + private AttributedString insertSecondaryPrompts(AttributedString str, List prompts) { return insertSecondaryPrompts(str, prompts, true); } - private AttributedString insertSecondaryPrompts(AttributedString strAtt, List prompts, boolean computePrompts) { + private AttributedString insertSecondaryPrompts( + AttributedString strAtt, List prompts, boolean computePrompts) { Objects.requireNonNull(prompts); List lines = strAtt.columnSplitLength(Integer.MAX_VALUE); AttributedStringBuilder sb = new AttributedStringBuilder(); @@ -4171,7 +4254,9 @@ private AttributedString insertSecondaryPrompts(AttributedString strAtt, List size.getColumns() || prompt.contains('\n')) { - width = new TerminalLine(prompt.toString(), 0, size.getColumns()).getEndLine().length(); + width = new TerminalLine(prompt.toString(), 0, size.getColumns()) + .getEndLine() + .length(); } for (int line = 0; line < lines.size() - 1; line++) { AttributedString prompt; @@ -4227,11 +4312,9 @@ private AttributedString insertSecondaryPrompts(AttributedString strAtt, List 0 - && line.charAt(line.length() - 1) == '\n'; + boolean endsWithNl = line.length() > 0 && line.charAt(line.length() - 1) == '\n'; // columnLength counts -1 for the final newline; adjust for that - int nb = size.getColumns() - width - - (line.columnLength() + (endsWithNl ? 1 : 0)); + int nb = size.getColumns() - width - (line.columnLength() + (endsWithNl ? 1 : 0)); if (nb >= 3) { AttributedStringBuilder sb = new AttributedStringBuilder(size.getColumns()); sb.append(line, 0, endsWithNl ? line.length() - 1 : line.length()); @@ -4253,8 +4336,8 @@ private AttributedString addRightPrompt(AttributedString prompt, AttributedStrin protected boolean insertTab() { return isSet(Option.INSERT_TAB) - && getLastBinding().equals("\t") - && buf.toString().matches("(^|[\\s\\S]*\n)[\r\n\t ]*"); + && getLastBinding().equals("\t") + && buf.toString().matches("(^|[\\s\\S]*\n)[\r\n\t ]*"); } protected boolean expandHistory() { @@ -4465,7 +4548,8 @@ else if (isSet(Option.RECOGNIZE_EXACT)) { if (op != null) { String chars = getString(REMOVE_SUFFIX_CHARS, DEFAULT_REMOVE_SUFFIX_CHARS); String ref = op instanceof Reference ? ((Reference) op).name() : null; - if (SELF_INSERT.equals(ref) && chars.indexOf(getLastBinding().charAt(0)) >= 0 + if (SELF_INSERT.equals(ref) + && chars.indexOf(getLastBinding().charAt(0)) >= 0 || ACCEPT_LINE.equals(ref)) { buf.backspace(completion.suffix().length()); if (getLastBinding().charAt(0) != ' ') { @@ -4533,27 +4617,35 @@ protected static CompletingParsedLine wrap(ParsedLine line) { public String word() { return line.word(); } + public int wordCursor() { return line.wordCursor(); } + public int wordIndex() { return line.wordIndex(); } + public List words() { return line.words(); } + public String line() { return line.line(); } + public int cursor() { return line.cursor(); } + public CharSequence escape(CharSequence candidate, boolean complete) { return candidate; } + public int rawWordCursor() { return wordCursor(); } + public int rawWordLength() { return word().length(); } @@ -4564,8 +4656,7 @@ public int rawWordLength() { protected Comparator getCandidateComparator(boolean caseInsensitive, String word) { String wdi = caseInsensitive ? word.toLowerCase() : word; ToIntFunction wordDistance = w -> ReaderUtils.distance(wdi, caseInsensitive ? w.toLowerCase() : w); - return Comparator - .comparing(Candidate::value, Comparator.comparingInt(wordDistance)) + return Comparator.comparing(Candidate::value, Comparator.comparingInt(wordDistance)) .thenComparing(Comparator.naturalOrder()); } @@ -4577,9 +4668,10 @@ protected String getOriginalGroupName() { return getString(ORIGINAL_GROUP_NAME, DEFAULT_ORIGINAL_GROUP_NAME); } - protected Comparator getGroupComparator() { - return Comparator.comparingInt(s -> getOthersGroupName().equals(s) ? 1 : getOriginalGroupName().equals(s) ? -1 : 0) + return Comparator.comparingInt(s -> getOthersGroupName().equals(s) + ? 1 + : getOriginalGroupName().equals(s) ? -1 : 0) .thenComparing(String::toLowerCase, Comparator.naturalOrder()); } @@ -4600,11 +4692,9 @@ private void mergeCandidates(List possible) { // the same description candidates.sort(Comparator.comparing(Candidate::value)); Candidate first = candidates.get(0); - String disp = candidates.stream() - .map(Candidate::displ) - .collect(Collectors.joining(" ")); - possible.add(new Candidate(first.value(), disp, first.group(), - first.descr(), first.suffix(), null, first.complete())); + String disp = candidates.stream().map(Candidate::displ).collect(Collectors.joining(" ")); + possible.add(new Candidate( + first.value(), disp, first.group(), first.descr(), first.suffix(), null, first.complete())); } } } @@ -4636,8 +4726,10 @@ private int visibleDisplayRows() { } private int promptLines() { - AttributedString text = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); - return text.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size(); + AttributedString text = + insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); + return text.columnSplitLength(size.getColumns(), false, display.delayLineWrap()) + .size(); } private class MenuSupport implements Supplier { @@ -4651,7 +4743,8 @@ private class MenuSupport implements Supplier { int columns; String completed; - public MenuSupport(List original, String completed, BiFunction escaper) { + public MenuSupport( + List original, String completed, BiFunction escaper) { this.possible = new ArrayList<>(); this.escaper = escaper; this.selection = -1; @@ -4714,7 +4807,7 @@ private void minor(int step) { if (selection - row + axis > options) { // selection is the last row/column // so there are fewer options than other rows - axis = options%axis; + axis = options % axis; } selection = selection - row + ((axis + row + step) % axis); update(); @@ -4772,7 +4865,9 @@ private void update() { AttributedString post = pr.post; if (post.length() > 0 && post.charAt(post.length() - 1) != '\n') { post = new AttributedStringBuilder(post.length() + 1) - .append(post).append("\n").toAttributedString(); + .append(post) + .append("\n") + .toAttributedString(); } List lines = post.columnSplitLength(size.getColumns(), true, display.delayLineWrap()); List sub = new ArrayList<>(lines.subList(topLine, topLine + displayed)); @@ -4785,7 +4880,8 @@ private void update() { .append(" of ") .append(Integer.toString(lines.size())) .append("\n") - .style(AttributedStyle.DEFAULT).toAttributedString()); + .style(AttributedStyle.DEFAULT) + .toAttributedString()); computed = AttributedString.join(AttributedString.EMPTY, sub); } else { computed = pr.post; @@ -4798,10 +4894,10 @@ private void update() { public AttributedString get() { return computed; } - } - protected boolean doMenu(List original, String completed, BiFunction escaper) { + protected boolean doMenu( + List original, String completed, BiFunction escaper) { // Reorder candidates according to display order final List possible = new ArrayList<>(); boolean caseInsensitive = isSet(Option.CASE_INSENSITIVE); @@ -4854,7 +4950,7 @@ protected boolean doMenu(List original, String completed, BiFunction< if (completion.suffix() != null) { String chars = getString(REMOVE_SUFFIX_CHARS, DEFAULT_REMOVE_SUFFIX_CHARS); if (SELF_INSERT.equals(ref) - && chars.indexOf(getLastBinding().charAt(0)) >= 0 + && chars.indexOf(getLastBinding().charAt(0)) >= 0 || BACKWARD_DELETE_CHAR.equals(ref)) { buf.backspace(completion.suffix().length()); } @@ -4866,8 +4962,8 @@ && getLastBinding().charAt(0) != ' ' } if (!ACCEPT_LINE.equals(ref) && !(SELF_INSERT.equals(ref) - && completion.suffix() != null - && completion.suffix().startsWith(getLastBinding()))) { + && completion.suffix() != null + && completion.suffix().startsWith(getLastBinding()))) { pushBackBinding(true); } post = null; @@ -4888,29 +4984,40 @@ protected boolean clearChoices() { return doList(new ArrayList<>(), "", false, null, false); } - protected boolean doList(List possible - , String completed, boolean runLoop, BiFunction escaper) { + protected boolean doList( + List possible, + String completed, + boolean runLoop, + BiFunction escaper) { return doList(possible, completed, runLoop, escaper, false); } - protected boolean doList(List possible - , String completed - , boolean runLoop, BiFunction escaper, boolean forSuggestion) { + protected boolean doList( + List possible, + String completed, + boolean runLoop, + BiFunction escaper, + boolean forSuggestion) { // If we list only and if there's a big // number of items, we should ask the user // for confirmation, display the list // and redraw the line at the bottom mergeCandidates(possible); - AttributedString text = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); - int promptLines = text.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size(); + AttributedString text = + insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); + int promptLines = text.columnSplitLength(size.getColumns(), false, display.delayLineWrap()) + .size(); PostResult postResult = computePost(possible, null, null, completed); int lines = postResult.lines; int listMax = getInt(LIST_MAX, DEFAULT_LIST_MAX); - if (listMax > 0 && possible.size() >= listMax - || lines >= size.getRows() - promptLines) { + int possibleSize = possible.size(); + if (possibleSize == 0 || size.getRows() == 0) { + return false; + } + if (listMax > 0 && possibleSize >= listMax || lines >= size.getRows() - promptLines) { if (!forSuggestion) { // prompt - post = () -> new AttributedString(getAppName() + ": do you wish to see all " + possible.size() + post = () -> new AttributedString(getAppName() + ": do you wish to see all " + possibleSize + " possibilities (" + lines + " lines)?"); redisplay(true); int c = readCharacter(); @@ -4930,8 +5037,7 @@ protected boolean doList(List possible String current = completed + sb.toString(); List cands; if (sb.length() > 0) { - completionMatcher.compile(options, false, new CompletingWord(current), caseInsensitive, 0 - , null); + completionMatcher.compile(options, false, new CompletingWord(current), caseInsensitive, 0, null); cands = completionMatcher.matches(possible).stream() .sorted(getCandidateComparator(caseInsensitive, current)) .collect(Collectors.toList()); @@ -4944,8 +5050,10 @@ protected boolean doList(List possible candidateStartPosition = candidateStartPosition(cands); } post = () -> { - AttributedString t = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); - int pl = t.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size(); + AttributedString t = insertSecondaryPrompts( + AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); + int pl = t.columnSplitLength(size.getColumns(), false, display.delayLineWrap()) + .size(); PostResult pr = computePost(cands, null, null, current); if (pr.lines >= size.getRows() - pl) { post = null; @@ -4954,7 +5062,8 @@ protected boolean doList(List possible redisplay(false); buf.cursor(oldCursor); println(); - List ls = pr.post.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); + List ls = + pr.post.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); Display d = new Display(terminal, false); d.resize(size.getRows(), size.getColumns()); d.update(ls, -1); @@ -5074,25 +5183,41 @@ public PostResult(AttributedString post, int lines, int selectedLine) { } } - protected PostResult computePost(List possible, Candidate selection, List ordered, String completed) { - return computePost(possible, selection, ordered, completed, display::wcwidth, size.getColumns(), isSet(Option.AUTO_GROUP), isSet(Option.GROUP), isSet(Option.LIST_ROWS_FIRST)); - } - - protected PostResult computePost(List possible, Candidate selection, List ordered, String completed, Function wcwidth, int width, boolean autoGroup, boolean groupName, boolean rowsFirst) { + protected PostResult computePost( + List possible, Candidate selection, List ordered, String completed) { + return computePost( + possible, + selection, + ordered, + completed, + display::wcwidth, + size.getColumns(), + isSet(Option.AUTO_GROUP), + isSet(Option.GROUP), + isSet(Option.LIST_ROWS_FIRST)); + } + + protected PostResult computePost( + List possible, + Candidate selection, + List ordered, + String completed, + Function wcwidth, + int width, + boolean autoGroup, + boolean groupName, + boolean rowsFirst) { List strings = new ArrayList<>(); - boolean customOrder = possible.stream().anyMatch(c -> c.sort() != 0); if (groupName) { Comparator groupComparator = getGroupComparator(); - Map> sorted; - sorted = groupComparator != null - ? new TreeMap<>(groupComparator) - : new LinkedHashMap<>(); + Map> sorted; + sorted = groupComparator != null ? new TreeMap<>(groupComparator) : new LinkedHashMap<>(); for (Candidate cand : possible) { String group = cand.group(); - sorted.computeIfAbsent(group != null ? group : "", s -> new LinkedHashMap<>()) - .put((customOrder ? cand.sort() : cand.value()), cand); + sorted.computeIfAbsent(group != null ? group : "", s -> new ArrayList<>()) + .add(cand); } - for (Map.Entry> entry : sorted.entrySet()) { + for (Map.Entry> entry : sorted.entrySet()) { String group = entry.getKey(); if (group.isEmpty() && sorted.size() > 1) { group = getOthersGroupName(); @@ -5100,27 +5225,30 @@ protected PostResult computePost(List possible, Candidate selection, if (!group.isEmpty() && autoGroup) { strings.add(group); } - strings.add(new ArrayList<>(entry.getValue().values())); + List candidates = entry.getValue(); + Collections.sort(candidates); + strings.add(candidates); if (ordered != null) { - ordered.addAll(entry.getValue().values()); + ordered.addAll(candidates); } } } else { Set groups = new LinkedHashSet<>(); - TreeMap sorted = new TreeMap<>(); + List sorted = new ArrayList<>(); for (Candidate cand : possible) { String group = cand.group(); if (group != null) { groups.add(group); } - sorted.put((customOrder ? cand.sort() : cand.value()), cand); + sorted.add(cand); } if (autoGroup) { strings.addAll(groups); } - strings.add(new ArrayList<>(sorted.values())); + Collections.sort(sorted); + strings.add(sorted); if (ordered != null) { - ordered.addAll(sorted.values()); + ordered.addAll(sorted); } } return toColumns(strings, selection, completed, wcwidth, width, rowsFirst); @@ -5163,12 +5291,15 @@ public String getEndLine() { } private int candidateStartPosition(List cands) { - List values = cands.stream().map(c -> AttributedString.stripAnsi(c.displ())) - .filter(c -> !c.matches("\\w+") && c.length() > 1).collect(Collectors.toList()); + List values = cands.stream() + .map(c -> AttributedString.stripAnsi(c.displ())) + .filter(c -> !c.matches("\\w+") && c.length() > 1) + .collect(Collectors.toList()); Set notDelimiters = new HashSet<>(); - values.forEach(v -> v.substring(0, v.length() - 1).chars() + values.forEach(v -> v.substring(0, v.length() - 1) + .chars() .filter(c -> !Character.isDigit(c) && !Character.isAlphabetic(c)) - .forEach(c -> notDelimiters.add(Character.toString((char)c)))); + .forEach(c -> notDelimiters.add(Character.toString((char) c)))); int width = size.getColumns(); int promptLength = prompt != null ? prompt.length() : 0; if (promptLength > 0) { @@ -5179,8 +5310,7 @@ private int candidateStartPosition(List cands) { int out = tl.getStartPos(); String buffer = tl.getEndLine(); for (int i = buffer.length(); i > 0; i--) { - if (buffer.substring(0, i).matches(".*\\W") - && !notDelimiters.contains(buffer.substring(i - 1, i))) { + if (buffer.substring(0, i).matches(".*\\W") && !notDelimiters.contains(buffer.substring(i - 1, i))) { out += i; break; } @@ -5189,7 +5319,13 @@ private int candidateStartPosition(List cands) { } @SuppressWarnings("unchecked") - protected PostResult toColumns(List items, Candidate selection, String completed, Function wcwidth, int width, boolean rowsFirst) { + protected PostResult toColumns( + List items, + Candidate selection, + String completed, + Function wcwidth, + int width, + boolean rowsFirst) { int[] out = new int[2]; // TODO: support Option.LIST_PACKED // Compute column width @@ -5199,8 +5335,7 @@ protected PostResult toColumns(List items, Candidate selection, String c if (item instanceof String) { int len = wcwidth.apply((String) item); maxWidth = Math.max(maxWidth, len); - } - else if (item instanceof List) { + } else if (item instanceof List) { for (Candidate cand : (List) item) { listSize++; int len = wcwidth.apply(cand.displ()); @@ -5218,7 +5353,10 @@ else if (item instanceof List) { AttributedStringBuilder sb = new AttributedStringBuilder(); if (listSize > 0) { if (isSet(Option.AUTO_MENU_LIST) - && listSize < Math.min(getInt(MENU_LIST_MAX, DEFAULT_MENU_LIST_MAX), visibleDisplayRows() - promptLines())) { + && listSize + < Math.min( + getInt(MENU_LIST_MAX, DEFAULT_MENU_LIST_MAX), + visibleDisplayRows() - promptLines())) { maxWidth = Math.max(maxWidth, MENU_LIST_WIDTH); sb.tabs(Math.max(Math.min(candidateStartPosition, width - maxWidth - 1), 1)); width = maxWidth + 2; @@ -5251,8 +5389,16 @@ else if (item instanceof List) { } @SuppressWarnings("unchecked") - protected void toColumns(Object items, int width, int maxWidth, AttributedStringBuilder sb, Candidate selection, String completed - , boolean rowsFirst, boolean doMenuList, int[] out) { + protected void toColumns( + Object items, + int width, + int maxWidth, + AttributedStringBuilder sb, + Candidate selection, + String completed, + boolean rowsFirst, + boolean doMenuList, + int[] out) { if (maxWidth <= 0 || width <= 0) { return; } @@ -5305,18 +5451,20 @@ else if (items instanceof List) { if (idx < candidates.size()) { Candidate cand = candidates.get(idx); boolean hasRightItem = j < columns - 1 && index.applyAsInt(i, j + 1) < candidates.size(); - AttributedString left = AttributedString.fromAnsi(cand.displ()); - AttributedString right = AttributedString.fromAnsi(cand.descr()); + AttributedString left = fromAnsi(cand.displ()); + AttributedString right = fromAnsi(cand.descr()); int lw = left.columnLength(); int rw = 0; if (right != null) { - int rem = maxWidth - (lw + MARGIN_BETWEEN_DISPLAY_AND_DESC - + DESC_PREFIX.length() + DESC_SUFFIX.length()); + int rem = maxWidth + - (lw + + MARGIN_BETWEEN_DISPLAY_AND_DESC + + DESC_PREFIX.length() + + DESC_SUFFIX.length()); rw = right.columnLength(); if (rw > rem) { right = AttributedStringBuilder.append( - right.columnSubSequence(0, rem - WCWidth.wcwidth('\u2026')), - "\u2026"); + right.columnSubSequence(0, rem - WCWidth.wcwidth('\u2026')), "\u2026"); rw = right.columnLength(); } right = AttributedStringBuilder.append(DESC_PREFIX, right, DESC_SUFFIX); @@ -5325,8 +5473,9 @@ else if (items instanceof List) { if (cand == selection) { out[1] = i; asb.style(getCompletionStyleSelection(doMenuList)); - if (left.toString().regionMatches( - isSet(Option.CASE_INSENSITIVE), 0, completed, 0, completed.length())) { + if (left.toString() + .regionMatches( + isSet(Option.CASE_INSENSITIVE), 0, completed, 0, completed.length())) { asb.append(left.toString(), 0, completed.length()); asb.append(left.toString(), completed.length(), left.length()); } else { @@ -5340,8 +5489,9 @@ else if (items instanceof List) { } asb.style(AttributedStyle.DEFAULT); } else { - if (left.toString().regionMatches( - isSet(Option.CASE_INSENSITIVE), 0, completed, 0, completed.length())) { + if (left.toString() + .regionMatches( + isSet(Option.CASE_INSENSITIVE), 0, completed, 0, completed.length())) { asb.style(getCompletionStyleStarting(doMenuList)); asb.append(left, 0, completed.length()); asb.style(AttributedStyle.DEFAULT); @@ -5447,7 +5597,7 @@ protected AttributedStyle getCompletionStyle(String name, String value) { } protected AttributedStyle buildStyle(String str) { - return AttributedString.fromAnsi("\u001b[" + str + "m ").styleAt(0); + return fromAnsi("\u001b[" + str + "m ").styleAt(0); } /** @@ -5477,14 +5627,14 @@ protected boolean moveHistory(final boolean next) { } if (next && !history.next()) { return false; - } - else if (!next && !history.previous()) { + } else if (!next && !history.previous()) { return false; } - setBuffer(modifiedHistory.containsKey(history.index()) - ? modifiedHistory.get(history.index()) - : history.current()); + setBuffer( + modifiedHistory.containsKey(history.index()) + ? modifiedHistory.get(history.index()) + : history.current()); return true; } @@ -5515,7 +5665,6 @@ void println() { redrawLine(); } - // // Actions // @@ -5725,8 +5874,7 @@ public boolean yankPop() { public boolean mouse() { MouseEvent event = readMouseEvent(); - if (event.getType() == MouseEvent.Type.Released - && event.getButton() == MouseEvent.Button.Button1) { + if (event.getType() == MouseEvent.Type.Released && event.getButton() == MouseEvent.Button.Button1) { StringBuilder tsb = new StringBuilder(); Cursor cursor = terminal.getCursorPosition(c -> tsb.append((char) c)); bindingReader.runMacro(tsb.toString()); @@ -5734,15 +5882,20 @@ public boolean mouse() { List secondaryPrompts = new ArrayList<>(); getDisplayedBufferWithPrompts(secondaryPrompts); - AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH); + AttributedStringBuilder sb = new AttributedStringBuilder().tabs(getTabWidth()); sb.append(prompt); sb.append(insertSecondaryPrompts(new AttributedString(buf.upToCursor()), secondaryPrompts, false)); - List promptLines = sb.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); + List promptLines = + sb.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); int currentLine = promptLines.size() - 1; int wantedLine = Math.max(0, Math.min(currentLine + event.getY() - cursor.getY(), secondaryPrompts.size())); - int pl0 = currentLine == 0 ? prompt.columnLength() : secondaryPrompts.get(currentLine - 1).columnLength(); - int pl1 = wantedLine == 0 ? prompt.columnLength() : secondaryPrompts.get(wantedLine - 1).columnLength(); + int pl0 = currentLine == 0 + ? prompt.columnLength() + : secondaryPrompts.get(currentLine - 1).columnLength(); + int pl1 = wantedLine == 0 + ? prompt.columnLength() + : secondaryPrompts.get(wantedLine - 1).columnLength(); int adjust = pl1 - pl0; buf.moveXY(event.getX() - cursor.getX() - adjust, event.getY() - cursor.getY()); } @@ -5814,13 +5967,11 @@ public boolean beep() { bell_preference = BellType.VISIBLE; break; case "on": - bell_preference = getBoolean(PREFER_VISIBLE_BELL, false) - ? BellType.VISIBLE : BellType.AUDIBLE; + bell_preference = getBoolean(PREFER_VISIBLE_BELL, false) ? BellType.VISIBLE : BellType.AUDIBLE; break; } if (bell_preference == BellType.VISIBLE) { - if (terminal.puts(Capability.flash_screen) - || terminal.puts(Capability.bell)) { + if (terminal.puts(Capability.flash_screen) || terminal.puts(Capability.bell)) { flush(); } } else if (bell_preference == BellType.AUDIBLE) { @@ -5869,8 +6020,7 @@ protected boolean isAlpha(int c) { protected boolean isWord(int c) { String wordchars = getString(WORDCHARS, DEFAULT_WORDCHARS); - return Character.isLetterOrDigit(c) - || (c < 128 && wordchars.indexOf((char) c) >= 0); + return Character.isLetterOrDigit(c) || (c < 128 && wordchars.indexOf((char) c) >= 0); } String getString(String name, String def) { @@ -5899,6 +6049,8 @@ public Map> defaultKeyMaps() { keyMaps.put(VIOPP, viOpp()); keyMaps.put(VISUAL, visual()); keyMaps.put(SAFE, safe()); + keyMaps.put(DUMB, dumb()); + if (getBoolean(BIND_TTY_SPECIAL_CHARS, true)) { Attributes attr = terminal.getAttributes(); bindConsoleChars(keyMaps.get(EMACS), attr); @@ -5909,240 +6061,241 @@ public Map> defaultKeyMaps() { keyMap.setUnicode(new Reference(SELF_INSERT)); keyMap.setAmbiguousTimeout(getLong(AMBIGUOUS_BINDING, DEFAULT_AMBIGUOUS_BINDING)); } - // By default, link main to emacs - keyMaps.put(MAIN, keyMaps.get(EMACS)); + // By default, link main to emacs unless the temrinal is dumb + keyMaps.put(MAIN, keyMaps.get(isTerminalDumb() ? DUMB : EMACS)); + return keyMaps; } public KeyMap emacs() { KeyMap emacs = new KeyMap<>(); bindKeys(emacs); - bind(emacs, SET_MARK_COMMAND, ctrl('@')); - bind(emacs, BEGINNING_OF_LINE, ctrl('A')); - bind(emacs, BACKWARD_CHAR, ctrl('B')); - bind(emacs, DELETE_CHAR_OR_LIST, ctrl('D')); - bind(emacs, END_OF_LINE, ctrl('E')); - bind(emacs, FORWARD_CHAR, ctrl('F')); - bind(emacs, SEND_BREAK, ctrl('G')); - bind(emacs, BACKWARD_DELETE_CHAR, ctrl('H')); - bind(emacs, EXPAND_OR_COMPLETE, ctrl('I')); - bind(emacs, ACCEPT_LINE, ctrl('J')); - bind(emacs, KILL_LINE, ctrl('K')); - bind(emacs, CLEAR_SCREEN, ctrl('L')); - bind(emacs, ACCEPT_LINE, ctrl('M')); - bind(emacs, DOWN_LINE_OR_HISTORY, ctrl('N')); - bind(emacs, ACCEPT_LINE_AND_DOWN_HISTORY, ctrl('O')); - bind(emacs, UP_LINE_OR_HISTORY, ctrl('P')); - bind(emacs, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('R')); - bind(emacs, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('S')); - bind(emacs, TRANSPOSE_CHARS, ctrl('T')); - bind(emacs, KILL_WHOLE_LINE, ctrl('U')); - bind(emacs, QUOTED_INSERT, ctrl('V')); - bind(emacs, BACKWARD_KILL_WORD, ctrl('W')); - bind(emacs, YANK, ctrl('Y')); - bind(emacs, CHARACTER_SEARCH, ctrl(']')); - bind(emacs, UNDO, ctrl('_')); - bind(emacs, SELF_INSERT, range(" -~")); - bind(emacs, INSERT_CLOSE_PAREN, ")"); - bind(emacs, INSERT_CLOSE_SQUARE, "]"); - bind(emacs, INSERT_CLOSE_CURLY, "}"); - bind(emacs, BACKWARD_DELETE_CHAR, del()); - bind(emacs, VI_MATCH_BRACKET, translate("^X^B")); - bind(emacs, SEND_BREAK, translate("^X^G")); - bind(emacs, EDIT_AND_EXECUTE_COMMAND, translate("^X^E")); - bind(emacs, VI_FIND_NEXT_CHAR, translate("^X^F")); - bind(emacs, VI_JOIN, translate("^X^J")); - bind(emacs, KILL_BUFFER, translate("^X^K")); - bind(emacs, INFER_NEXT_HISTORY, translate("^X^N")); - bind(emacs, OVERWRITE_MODE, translate("^X^O")); - bind(emacs, REDO, translate("^X^R")); - bind(emacs, UNDO, translate("^X^U")); - bind(emacs, VI_CMD_MODE, translate("^X^V")); - bind(emacs, EXCHANGE_POINT_AND_MARK, translate("^X^X")); - bind(emacs, DO_LOWERCASE_VERSION, translate("^XA-^XZ")); - bind(emacs, WHAT_CURSOR_POSITION, translate("^X=")); - bind(emacs, KILL_LINE, translate("^X^?")); - bind(emacs, SEND_BREAK, alt(ctrl('G'))); - bind(emacs, BACKWARD_KILL_WORD, alt(ctrl('H'))); - bind(emacs, SELF_INSERT_UNMETA, alt(ctrl('M'))); - bind(emacs, COMPLETE_WORD, alt(esc())); - bind(emacs, CHARACTER_SEARCH_BACKWARD, alt(ctrl(']'))); - bind(emacs, COPY_PREV_WORD, alt(ctrl('_'))); - bind(emacs, SET_MARK_COMMAND, alt(' ')); - bind(emacs, NEG_ARGUMENT, alt('-')); - bind(emacs, DIGIT_ARGUMENT, range("\\E0-\\E9")); - bind(emacs, BEGINNING_OF_HISTORY, alt('<')); - bind(emacs, LIST_CHOICES, alt('=')); - bind(emacs, END_OF_HISTORY, alt('>')); - bind(emacs, LIST_CHOICES, alt('?')); - bind(emacs, DO_LOWERCASE_VERSION, range("^[A-^[Z")); - bind(emacs, ACCEPT_AND_HOLD, alt('a')); - bind(emacs, BACKWARD_WORD, alt('b')); - bind(emacs, CAPITALIZE_WORD, alt('c')); - bind(emacs, KILL_WORD, alt('d')); - bind(emacs, KILL_WORD, translate("^[[3;5~")); // ctrl-delete - bind(emacs, FORWARD_WORD, alt('f')); - bind(emacs, DOWN_CASE_WORD, alt('l')); - bind(emacs, HISTORY_SEARCH_FORWARD, alt('n')); - bind(emacs, HISTORY_SEARCH_BACKWARD, alt('p')); - bind(emacs, TRANSPOSE_WORDS, alt('t')); - bind(emacs, UP_CASE_WORD, alt('u')); - bind(emacs, YANK_POP, alt('y')); - bind(emacs, BACKWARD_KILL_WORD, alt(del())); + bind(emacs, SET_MARK_COMMAND, ctrl('@')); + bind(emacs, BEGINNING_OF_LINE, ctrl('A')); + bind(emacs, BACKWARD_CHAR, ctrl('B')); + bind(emacs, DELETE_CHAR_OR_LIST, ctrl('D')); + bind(emacs, END_OF_LINE, ctrl('E')); + bind(emacs, FORWARD_CHAR, ctrl('F')); + bind(emacs, SEND_BREAK, ctrl('G')); + bind(emacs, BACKWARD_DELETE_CHAR, ctrl('H')); + bind(emacs, EXPAND_OR_COMPLETE, ctrl('I')); + bind(emacs, ACCEPT_LINE, ctrl('J')); + bind(emacs, KILL_LINE, ctrl('K')); + bind(emacs, CLEAR_SCREEN, ctrl('L')); + bind(emacs, ACCEPT_LINE, ctrl('M')); + bind(emacs, DOWN_LINE_OR_HISTORY, ctrl('N')); + bind(emacs, ACCEPT_LINE_AND_DOWN_HISTORY, ctrl('O')); + bind(emacs, UP_LINE_OR_HISTORY, ctrl('P')); + bind(emacs, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('R')); + bind(emacs, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('S')); + bind(emacs, TRANSPOSE_CHARS, ctrl('T')); + bind(emacs, KILL_WHOLE_LINE, ctrl('U')); + bind(emacs, QUOTED_INSERT, ctrl('V')); + bind(emacs, BACKWARD_KILL_WORD, ctrl('W')); + bind(emacs, YANK, ctrl('Y')); + bind(emacs, CHARACTER_SEARCH, ctrl(']')); + bind(emacs, UNDO, ctrl('_')); + bind(emacs, SELF_INSERT, range(" -~")); + bind(emacs, INSERT_CLOSE_PAREN, ")"); + bind(emacs, INSERT_CLOSE_SQUARE, "]"); + bind(emacs, INSERT_CLOSE_CURLY, "}"); + bind(emacs, BACKWARD_DELETE_CHAR, del()); + bind(emacs, VI_MATCH_BRACKET, translate("^X^B")); + bind(emacs, SEND_BREAK, translate("^X^G")); + bind(emacs, EDIT_AND_EXECUTE_COMMAND, translate("^X^E")); + bind(emacs, VI_FIND_NEXT_CHAR, translate("^X^F")); + bind(emacs, VI_JOIN, translate("^X^J")); + bind(emacs, KILL_BUFFER, translate("^X^K")); + bind(emacs, INFER_NEXT_HISTORY, translate("^X^N")); + bind(emacs, OVERWRITE_MODE, translate("^X^O")); + bind(emacs, REDO, translate("^X^R")); + bind(emacs, UNDO, translate("^X^U")); + bind(emacs, VI_CMD_MODE, translate("^X^V")); + bind(emacs, EXCHANGE_POINT_AND_MARK, translate("^X^X")); + bind(emacs, DO_LOWERCASE_VERSION, translate("^XA-^XZ")); + bind(emacs, WHAT_CURSOR_POSITION, translate("^X=")); + bind(emacs, KILL_LINE, translate("^X^?")); + bind(emacs, SEND_BREAK, alt(ctrl('G'))); + bind(emacs, BACKWARD_KILL_WORD, alt(ctrl('H'))); + bind(emacs, SELF_INSERT_UNMETA, alt(ctrl('M'))); + bind(emacs, COMPLETE_WORD, alt(esc())); + bind(emacs, CHARACTER_SEARCH_BACKWARD, alt(ctrl(']'))); + bind(emacs, COPY_PREV_WORD, alt(ctrl('_'))); + bind(emacs, SET_MARK_COMMAND, alt(' ')); + bind(emacs, NEG_ARGUMENT, alt('-')); + bind(emacs, DIGIT_ARGUMENT, range("\\E0-\\E9")); + bind(emacs, BEGINNING_OF_HISTORY, alt('<')); + bind(emacs, LIST_CHOICES, alt('=')); + bind(emacs, END_OF_HISTORY, alt('>')); + bind(emacs, LIST_CHOICES, alt('?')); + bind(emacs, DO_LOWERCASE_VERSION, range("^[A-^[Z")); + bind(emacs, ACCEPT_AND_HOLD, alt('a')); + bind(emacs, BACKWARD_WORD, alt('b')); + bind(emacs, CAPITALIZE_WORD, alt('c')); + bind(emacs, KILL_WORD, alt('d')); + bind(emacs, KILL_WORD, translate("^[[3;5~")); // ctrl-delete + bind(emacs, FORWARD_WORD, alt('f')); + bind(emacs, DOWN_CASE_WORD, alt('l')); + bind(emacs, HISTORY_SEARCH_FORWARD, alt('n')); + bind(emacs, HISTORY_SEARCH_BACKWARD, alt('p')); + bind(emacs, TRANSPOSE_WORDS, alt('t')); + bind(emacs, UP_CASE_WORD, alt('u')); + bind(emacs, YANK_POP, alt('y')); + bind(emacs, BACKWARD_KILL_WORD, alt(del())); bindArrowKeys(emacs); - bind(emacs, FORWARD_WORD, translate("^[[1;5C")); // ctrl-left - bind(emacs, BACKWARD_WORD, translate("^[[1;5D")); // ctrl-right - bind(emacs, FORWARD_WORD, alt(key(Capability.key_right))); - bind(emacs, BACKWARD_WORD, alt(key(Capability.key_left))); - bind(emacs, FORWARD_WORD, alt(translate("^[[C"))); - bind(emacs, BACKWARD_WORD, alt(translate("^[[D"))); + bind(emacs, FORWARD_WORD, translate("^[[1;5C")); // ctrl-left + bind(emacs, BACKWARD_WORD, translate("^[[1;5D")); // ctrl-right + bind(emacs, FORWARD_WORD, alt(key(Capability.key_right))); + bind(emacs, BACKWARD_WORD, alt(key(Capability.key_left))); + bind(emacs, FORWARD_WORD, alt(translate("^[[C"))); + bind(emacs, BACKWARD_WORD, alt(translate("^[[D"))); return emacs; } public KeyMap viInsertion() { KeyMap viins = new KeyMap<>(); bindKeys(viins); - bind(viins, SELF_INSERT, range("^@-^_")); - bind(viins, LIST_CHOICES, ctrl('D')); - bind(viins, SEND_BREAK, ctrl('G')); - bind(viins, BACKWARD_DELETE_CHAR, ctrl('H')); - bind(viins, EXPAND_OR_COMPLETE, ctrl('I')); - bind(viins, ACCEPT_LINE, ctrl('J')); - bind(viins, CLEAR_SCREEN, ctrl('L')); - bind(viins, ACCEPT_LINE, ctrl('M')); - bind(viins, MENU_COMPLETE, ctrl('N')); - bind(viins, REVERSE_MENU_COMPLETE, ctrl('P')); - bind(viins, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('R')); - bind(viins, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('S')); - bind(viins, TRANSPOSE_CHARS, ctrl('T')); - bind(viins, KILL_WHOLE_LINE, ctrl('U')); - bind(viins, QUOTED_INSERT, ctrl('V')); - bind(viins, BACKWARD_KILL_WORD, ctrl('W')); - bind(viins, YANK, ctrl('Y')); - bind(viins, VI_CMD_MODE, ctrl('[')); - bind(viins, UNDO, ctrl('_')); - bind(viins, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('X') + "r"); - bind(viins, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('X') + "s"); - bind(viins, SELF_INSERT, range(" -~")); - bind(viins, INSERT_CLOSE_PAREN, ")"); - bind(viins, INSERT_CLOSE_SQUARE, "]"); - bind(viins, INSERT_CLOSE_CURLY, "}"); - bind(viins, BACKWARD_DELETE_CHAR, del()); + bind(viins, SELF_INSERT, range("^@-^_")); + bind(viins, LIST_CHOICES, ctrl('D')); + bind(viins, SEND_BREAK, ctrl('G')); + bind(viins, BACKWARD_DELETE_CHAR, ctrl('H')); + bind(viins, EXPAND_OR_COMPLETE, ctrl('I')); + bind(viins, ACCEPT_LINE, ctrl('J')); + bind(viins, CLEAR_SCREEN, ctrl('L')); + bind(viins, ACCEPT_LINE, ctrl('M')); + bind(viins, MENU_COMPLETE, ctrl('N')); + bind(viins, REVERSE_MENU_COMPLETE, ctrl('P')); + bind(viins, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('R')); + bind(viins, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('S')); + bind(viins, TRANSPOSE_CHARS, ctrl('T')); + bind(viins, KILL_WHOLE_LINE, ctrl('U')); + bind(viins, QUOTED_INSERT, ctrl('V')); + bind(viins, BACKWARD_KILL_WORD, ctrl('W')); + bind(viins, YANK, ctrl('Y')); + bind(viins, VI_CMD_MODE, ctrl('[')); + bind(viins, UNDO, ctrl('_')); + bind(viins, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('X') + "r"); + bind(viins, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('X') + "s"); + bind(viins, SELF_INSERT, range(" -~")); + bind(viins, INSERT_CLOSE_PAREN, ")"); + bind(viins, INSERT_CLOSE_SQUARE, "]"); + bind(viins, INSERT_CLOSE_CURLY, "}"); + bind(viins, BACKWARD_DELETE_CHAR, del()); bindArrowKeys(viins); return viins; } public KeyMap viCmd() { KeyMap vicmd = new KeyMap<>(); - bind(vicmd, LIST_CHOICES, ctrl('D')); - bind(vicmd, EMACS_EDITING_MODE, ctrl('E')); - bind(vicmd, SEND_BREAK, ctrl('G')); - bind(vicmd, VI_BACKWARD_CHAR, ctrl('H')); - bind(vicmd, ACCEPT_LINE, ctrl('J')); - bind(vicmd, KILL_LINE, ctrl('K')); - bind(vicmd, CLEAR_SCREEN, ctrl('L')); - bind(vicmd, ACCEPT_LINE, ctrl('M')); - bind(vicmd, VI_DOWN_LINE_OR_HISTORY, ctrl('N')); - bind(vicmd, VI_UP_LINE_OR_HISTORY, ctrl('P')); - bind(vicmd, QUOTED_INSERT, ctrl('Q')); - bind(vicmd, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('R')); - bind(vicmd, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('S')); - bind(vicmd, TRANSPOSE_CHARS, ctrl('T')); - bind(vicmd, KILL_WHOLE_LINE, ctrl('U')); - bind(vicmd, QUOTED_INSERT, ctrl('V')); - bind(vicmd, BACKWARD_KILL_WORD, ctrl('W')); - bind(vicmd, YANK, ctrl('Y')); - bind(vicmd, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('X') + "r"); - bind(vicmd, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('X') + "s"); - bind(vicmd, SEND_BREAK, alt(ctrl('G'))); - bind(vicmd, BACKWARD_KILL_WORD, alt(ctrl('H'))); - bind(vicmd, SELF_INSERT_UNMETA, alt(ctrl('M'))); - bind(vicmd, COMPLETE_WORD, alt(esc())); - bind(vicmd, CHARACTER_SEARCH_BACKWARD, alt(ctrl(']'))); - bind(vicmd, SET_MARK_COMMAND, alt(' ')); -// bind(vicmd, INSERT_COMMENT, alt('#')); -// bind(vicmd, INSERT_COMPLETIONS, alt('*')); - bind(vicmd, DIGIT_ARGUMENT, alt('-')); - bind(vicmd, BEGINNING_OF_HISTORY, alt('<')); - bind(vicmd, LIST_CHOICES, alt('=')); - bind(vicmd, END_OF_HISTORY, alt('>')); - bind(vicmd, LIST_CHOICES, alt('?')); - bind(vicmd, DO_LOWERCASE_VERSION, range("^[A-^[Z")); - bind(vicmd, BACKWARD_WORD, alt('b')); - bind(vicmd, CAPITALIZE_WORD, alt('c')); - bind(vicmd, KILL_WORD, alt('d')); - bind(vicmd, FORWARD_WORD, alt('f')); - bind(vicmd, DOWN_CASE_WORD, alt('l')); - bind(vicmd, HISTORY_SEARCH_FORWARD, alt('n')); - bind(vicmd, HISTORY_SEARCH_BACKWARD, alt('p')); - bind(vicmd, TRANSPOSE_WORDS, alt('t')); - bind(vicmd, UP_CASE_WORD, alt('u')); - bind(vicmd, YANK_POP, alt('y')); - bind(vicmd, BACKWARD_KILL_WORD, alt(del())); - - bind(vicmd, FORWARD_CHAR, " "); - bind(vicmd, VI_INSERT_COMMENT, "#"); - bind(vicmd, END_OF_LINE, "$"); - bind(vicmd, VI_MATCH_BRACKET, "%"); - bind(vicmd, VI_DOWN_LINE_OR_HISTORY, "+"); - bind(vicmd, VI_REV_REPEAT_FIND, ","); - bind(vicmd, VI_UP_LINE_OR_HISTORY, "-"); - bind(vicmd, VI_REPEAT_CHANGE, "."); - bind(vicmd, VI_HISTORY_SEARCH_BACKWARD, "/"); - bind(vicmd, VI_DIGIT_OR_BEGINNING_OF_LINE, "0"); - bind(vicmd, DIGIT_ARGUMENT, range("1-9")); - bind(vicmd, VI_REPEAT_FIND, ";"); - bind(vicmd, LIST_CHOICES, "="); - bind(vicmd, VI_HISTORY_SEARCH_FORWARD, "?"); - bind(vicmd, VI_ADD_EOL, "A"); - bind(vicmd, VI_BACKWARD_BLANK_WORD, "B"); - bind(vicmd, VI_CHANGE_EOL, "C"); - bind(vicmd, VI_KILL_EOL, "D"); - bind(vicmd, VI_FORWARD_BLANK_WORD_END, "E"); - bind(vicmd, VI_FIND_PREV_CHAR, "F"); - bind(vicmd, VI_FETCH_HISTORY, "G"); - bind(vicmd, VI_INSERT_BOL, "I"); - bind(vicmd, VI_JOIN, "J"); - bind(vicmd, VI_REV_REPEAT_SEARCH, "N"); - bind(vicmd, VI_OPEN_LINE_ABOVE, "O"); - bind(vicmd, VI_PUT_BEFORE, "P"); - bind(vicmd, VI_REPLACE, "R"); - bind(vicmd, VI_KILL_LINE, "S"); - bind(vicmd, VI_FIND_PREV_CHAR_SKIP, "T"); - bind(vicmd, REDO, "U"); - bind(vicmd, VISUAL_LINE_MODE, "V"); - bind(vicmd, VI_FORWARD_BLANK_WORD, "W"); - bind(vicmd, VI_BACKWARD_DELETE_CHAR, "X"); - bind(vicmd, VI_YANK_WHOLE_LINE, "Y"); - bind(vicmd, VI_FIRST_NON_BLANK, "^"); - bind(vicmd, VI_ADD_NEXT, "a"); - bind(vicmd, VI_BACKWARD_WORD, "b"); - bind(vicmd, VI_CHANGE, "c"); - bind(vicmd, VI_DELETE, "d"); - bind(vicmd, VI_FORWARD_WORD_END, "e"); - bind(vicmd, VI_FIND_NEXT_CHAR, "f"); - bind(vicmd, WHAT_CURSOR_POSITION, "ga"); - bind(vicmd, VI_BACKWARD_BLANK_WORD_END, "gE"); - bind(vicmd, VI_BACKWARD_WORD_END, "ge"); - bind(vicmd, VI_BACKWARD_CHAR, "h"); - bind(vicmd, VI_INSERT, "i"); - bind(vicmd, DOWN_LINE_OR_HISTORY, "j"); - bind(vicmd, UP_LINE_OR_HISTORY, "k"); - bind(vicmd, VI_FORWARD_CHAR, "l"); - bind(vicmd, VI_REPEAT_SEARCH, "n"); - bind(vicmd, VI_OPEN_LINE_BELOW, "o"); - bind(vicmd, VI_PUT_AFTER, "p"); - bind(vicmd, VI_REPLACE_CHARS, "r"); - bind(vicmd, VI_SUBSTITUTE, "s"); - bind(vicmd, VI_FIND_NEXT_CHAR_SKIP, "t"); - bind(vicmd, UNDO, "u"); - bind(vicmd, VISUAL_MODE, "v"); - bind(vicmd, VI_FORWARD_WORD, "w"); - bind(vicmd, VI_DELETE_CHAR, "x"); - bind(vicmd, VI_YANK, "y"); - bind(vicmd, VI_GOTO_COLUMN, "|"); - bind(vicmd, VI_SWAP_CASE, "~"); - bind(vicmd, VI_BACKWARD_CHAR, del()); + bind(vicmd, LIST_CHOICES, ctrl('D')); + bind(vicmd, EMACS_EDITING_MODE, ctrl('E')); + bind(vicmd, SEND_BREAK, ctrl('G')); + bind(vicmd, VI_BACKWARD_CHAR, ctrl('H')); + bind(vicmd, ACCEPT_LINE, ctrl('J')); + bind(vicmd, KILL_LINE, ctrl('K')); + bind(vicmd, CLEAR_SCREEN, ctrl('L')); + bind(vicmd, ACCEPT_LINE, ctrl('M')); + bind(vicmd, VI_DOWN_LINE_OR_HISTORY, ctrl('N')); + bind(vicmd, VI_UP_LINE_OR_HISTORY, ctrl('P')); + bind(vicmd, QUOTED_INSERT, ctrl('Q')); + bind(vicmd, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('R')); + bind(vicmd, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('S')); + bind(vicmd, TRANSPOSE_CHARS, ctrl('T')); + bind(vicmd, KILL_WHOLE_LINE, ctrl('U')); + bind(vicmd, QUOTED_INSERT, ctrl('V')); + bind(vicmd, BACKWARD_KILL_WORD, ctrl('W')); + bind(vicmd, YANK, ctrl('Y')); + bind(vicmd, HISTORY_INCREMENTAL_SEARCH_BACKWARD, ctrl('X') + "r"); + bind(vicmd, HISTORY_INCREMENTAL_SEARCH_FORWARD, ctrl('X') + "s"); + bind(vicmd, SEND_BREAK, alt(ctrl('G'))); + bind(vicmd, BACKWARD_KILL_WORD, alt(ctrl('H'))); + bind(vicmd, SELF_INSERT_UNMETA, alt(ctrl('M'))); + bind(vicmd, COMPLETE_WORD, alt(esc())); + bind(vicmd, CHARACTER_SEARCH_BACKWARD, alt(ctrl(']'))); + bind(vicmd, SET_MARK_COMMAND, alt(' ')); + // bind(vicmd, INSERT_COMMENT, alt('#')); + // bind(vicmd, INSERT_COMPLETIONS, alt('*')); + bind(vicmd, DIGIT_ARGUMENT, alt('-')); + bind(vicmd, BEGINNING_OF_HISTORY, alt('<')); + bind(vicmd, LIST_CHOICES, alt('=')); + bind(vicmd, END_OF_HISTORY, alt('>')); + bind(vicmd, LIST_CHOICES, alt('?')); + bind(vicmd, DO_LOWERCASE_VERSION, range("^[A-^[Z")); + bind(vicmd, BACKWARD_WORD, alt('b')); + bind(vicmd, CAPITALIZE_WORD, alt('c')); + bind(vicmd, KILL_WORD, alt('d')); + bind(vicmd, FORWARD_WORD, alt('f')); + bind(vicmd, DOWN_CASE_WORD, alt('l')); + bind(vicmd, HISTORY_SEARCH_FORWARD, alt('n')); + bind(vicmd, HISTORY_SEARCH_BACKWARD, alt('p')); + bind(vicmd, TRANSPOSE_WORDS, alt('t')); + bind(vicmd, UP_CASE_WORD, alt('u')); + bind(vicmd, YANK_POP, alt('y')); + bind(vicmd, BACKWARD_KILL_WORD, alt(del())); + + bind(vicmd, FORWARD_CHAR, " "); + bind(vicmd, VI_INSERT_COMMENT, "#"); + bind(vicmd, END_OF_LINE, "$"); + bind(vicmd, VI_MATCH_BRACKET, "%"); + bind(vicmd, VI_DOWN_LINE_OR_HISTORY, "+"); + bind(vicmd, VI_REV_REPEAT_FIND, ","); + bind(vicmd, VI_UP_LINE_OR_HISTORY, "-"); + bind(vicmd, VI_REPEAT_CHANGE, "."); + bind(vicmd, VI_HISTORY_SEARCH_BACKWARD, "/"); + bind(vicmd, VI_DIGIT_OR_BEGINNING_OF_LINE, "0"); + bind(vicmd, DIGIT_ARGUMENT, range("1-9")); + bind(vicmd, VI_REPEAT_FIND, ";"); + bind(vicmd, LIST_CHOICES, "="); + bind(vicmd, VI_HISTORY_SEARCH_FORWARD, "?"); + bind(vicmd, VI_ADD_EOL, "A"); + bind(vicmd, VI_BACKWARD_BLANK_WORD, "B"); + bind(vicmd, VI_CHANGE_EOL, "C"); + bind(vicmd, VI_KILL_EOL, "D"); + bind(vicmd, VI_FORWARD_BLANK_WORD_END, "E"); + bind(vicmd, VI_FIND_PREV_CHAR, "F"); + bind(vicmd, VI_FETCH_HISTORY, "G"); + bind(vicmd, VI_INSERT_BOL, "I"); + bind(vicmd, VI_JOIN, "J"); + bind(vicmd, VI_REV_REPEAT_SEARCH, "N"); + bind(vicmd, VI_OPEN_LINE_ABOVE, "O"); + bind(vicmd, VI_PUT_BEFORE, "P"); + bind(vicmd, VI_REPLACE, "R"); + bind(vicmd, VI_KILL_LINE, "S"); + bind(vicmd, VI_FIND_PREV_CHAR_SKIP, "T"); + bind(vicmd, REDO, "U"); + bind(vicmd, VISUAL_LINE_MODE, "V"); + bind(vicmd, VI_FORWARD_BLANK_WORD, "W"); + bind(vicmd, VI_BACKWARD_DELETE_CHAR, "X"); + bind(vicmd, VI_YANK_WHOLE_LINE, "Y"); + bind(vicmd, VI_FIRST_NON_BLANK, "^"); + bind(vicmd, VI_ADD_NEXT, "a"); + bind(vicmd, VI_BACKWARD_WORD, "b"); + bind(vicmd, VI_CHANGE, "c"); + bind(vicmd, VI_DELETE, "d"); + bind(vicmd, VI_FORWARD_WORD_END, "e"); + bind(vicmd, VI_FIND_NEXT_CHAR, "f"); + bind(vicmd, WHAT_CURSOR_POSITION, "ga"); + bind(vicmd, VI_BACKWARD_BLANK_WORD_END, "gE"); + bind(vicmd, VI_BACKWARD_WORD_END, "ge"); + bind(vicmd, VI_BACKWARD_CHAR, "h"); + bind(vicmd, VI_INSERT, "i"); + bind(vicmd, DOWN_LINE_OR_HISTORY, "j"); + bind(vicmd, UP_LINE_OR_HISTORY, "k"); + bind(vicmd, VI_FORWARD_CHAR, "l"); + bind(vicmd, VI_REPEAT_SEARCH, "n"); + bind(vicmd, VI_OPEN_LINE_BELOW, "o"); + bind(vicmd, VI_PUT_AFTER, "p"); + bind(vicmd, VI_REPLACE_CHARS, "r"); + bind(vicmd, VI_SUBSTITUTE, "s"); + bind(vicmd, VI_FIND_NEXT_CHAR_SKIP, "t"); + bind(vicmd, UNDO, "u"); + bind(vicmd, VISUAL_MODE, "v"); + bind(vicmd, VI_FORWARD_WORD, "w"); + bind(vicmd, VI_DELETE_CHAR, "x"); + bind(vicmd, VI_YANK, "y"); + bind(vicmd, VI_GOTO_COLUMN, "|"); + bind(vicmd, VI_SWAP_CASE, "~"); + bind(vicmd, VI_BACKWARD_CHAR, del()); bindArrowKeys(vicmd); return vicmd; @@ -6150,38 +6303,46 @@ public KeyMap viCmd() { public KeyMap menu() { KeyMap menu = new KeyMap<>(); - bind(menu, MENU_COMPLETE, "\t"); - bind(menu, REVERSE_MENU_COMPLETE, key(Capability.back_tab)); - bind(menu, ACCEPT_LINE, "\r", "\n"); + bind(menu, MENU_COMPLETE, "\t"); + bind(menu, REVERSE_MENU_COMPLETE, key(Capability.back_tab)); + bind(menu, ACCEPT_LINE, "\r", "\n"); bindArrowKeys(menu); return menu; } public KeyMap safe() { KeyMap safe = new KeyMap<>(); - bind(safe, SELF_INSERT, range("^@-^?")); - bind(safe, ACCEPT_LINE, "\r", "\n"); - bind(safe, SEND_BREAK, ctrl('G')); + bind(safe, SELF_INSERT, range("^@-^?")); + bind(safe, ACCEPT_LINE, "\r", "\n"); + bind(safe, SEND_BREAK, ctrl('G')); return safe; } + public KeyMap dumb() { + KeyMap dumb = new KeyMap<>(); + bind(dumb, SELF_INSERT, range("^@-^?")); + bind(dumb, ACCEPT_LINE, "\r", "\n"); + bind(dumb, BEEP, ctrl('G')); + return dumb; + } + public KeyMap visual() { KeyMap visual = new KeyMap<>(); - bind(visual, UP_LINE, key(Capability.key_up), "k"); - bind(visual, DOWN_LINE, key(Capability.key_down), "j"); - bind(visual, this::deactivateRegion, esc()); - bind(visual, EXCHANGE_POINT_AND_MARK, "o"); - bind(visual, PUT_REPLACE_SELECTION, "p"); - bind(visual, VI_DELETE, "x"); - bind(visual, VI_OPER_SWAP_CASE, "~"); + bind(visual, UP_LINE, key(Capability.key_up), "k"); + bind(visual, DOWN_LINE, key(Capability.key_down), "j"); + bind(visual, this::deactivateRegion, esc()); + bind(visual, EXCHANGE_POINT_AND_MARK, "o"); + bind(visual, PUT_REPLACE_SELECTION, "p"); + bind(visual, VI_DELETE, "x"); + bind(visual, VI_OPER_SWAP_CASE, "~"); return visual; } public KeyMap viOpp() { KeyMap viOpp = new KeyMap<>(); - bind(viOpp, UP_LINE, key(Capability.key_up), "k"); - bind(viOpp, DOWN_LINE, key(Capability.key_down), "j"); - bind(viOpp, VI_CMD_MODE, esc()); + bind(viOpp, UP_LINE, key(Capability.key_up), "k"); + bind(viOpp, DOWN_LINE, key(Capability.key_down), "j"); + bind(viOpp, VI_CMD_MODE, esc()); return viOpp; } @@ -6210,19 +6371,19 @@ private void bindKeys(KeyMap emacs) { } private void bindArrowKeys(KeyMap map) { - bind(map, UP_LINE_OR_SEARCH, key(Capability.key_up)); - bind(map, DOWN_LINE_OR_SEARCH, key(Capability.key_down)); - bind(map, BACKWARD_CHAR, key(Capability.key_left)); - bind(map, FORWARD_CHAR, key(Capability.key_right)); - bind(map, BEGINNING_OF_LINE, key(Capability.key_home)); - bind(map, END_OF_LINE, key(Capability.key_end)); - bind(map, DELETE_CHAR, key(Capability.key_dc)); - bind(map, KILL_WHOLE_LINE, key(Capability.key_dl)); - bind(map, OVERWRITE_MODE, key(Capability.key_ic)); - bind(map, MOUSE, key(Capability.key_mouse)); - bind(map, BEGIN_PASTE, BRACKETED_PASTE_BEGIN); - bind(map, FOCUS_IN, FOCUS_IN_SEQ); - bind(map, FOCUS_OUT, FOCUS_OUT_SEQ); + bind(map, UP_LINE_OR_SEARCH, key(Capability.key_up)); + bind(map, DOWN_LINE_OR_SEARCH, key(Capability.key_down)); + bind(map, BACKWARD_CHAR, key(Capability.key_left)); + bind(map, FORWARD_CHAR, key(Capability.key_right)); + bind(map, BEGINNING_OF_LINE, key(Capability.key_home)); + bind(map, END_OF_LINE, key(Capability.key_end)); + bind(map, DELETE_CHAR, key(Capability.key_dc)); + bind(map, KILL_WHOLE_LINE, key(Capability.key_dl)); + bind(map, OVERWRITE_MODE, key(Capability.key_ic)); + bind(map, MOUSE, key(Capability.key_mouse)); + bind(map, BEGIN_PASTE, BRACKETED_PASTE_BEGIN); + bind(map, FOCUS_IN, FOCUS_IN_SEQ); + bind(map, FOCUS_OUT, FOCUS_OUT_SEQ); } /** @@ -6231,14 +6392,10 @@ private void bindArrowKeys(KeyMap map) { */ private void bindConsoleChars(KeyMap keyMap, Attributes attr) { if (attr != null) { - rebind(keyMap, BACKWARD_DELETE_CHAR, - del(), (char) attr.getControlChar(ControlChar.VERASE)); - rebind(keyMap, BACKWARD_KILL_WORD, - ctrl('W'), (char) attr.getControlChar(ControlChar.VWERASE)); - rebind(keyMap, KILL_WHOLE_LINE, - ctrl('U'), (char) attr.getControlChar(ControlChar.VKILL)); - rebind(keyMap, QUOTED_INSERT, - ctrl('V'), (char) attr.getControlChar(ControlChar.VLNEXT)); + rebind(keyMap, BACKWARD_DELETE_CHAR, del(), (char) attr.getControlChar(ControlChar.VERASE)); + rebind(keyMap, BACKWARD_KILL_WORD, ctrl('W'), (char) attr.getControlChar(ControlChar.VWERASE)); + rebind(keyMap, KILL_WHOLE_LINE, ctrl('U'), (char) attr.getControlChar(ControlChar.VKILL)); + rebind(keyMap, QUOTED_INSERT, ctrl('V'), (char) attr.getControlChar(ControlChar.VLNEXT)); } } @@ -6250,4 +6407,8 @@ private void rebind(KeyMap keyMap, String operation, String prevBinding } } + @Override + public void zeroOut() { + buf.zeroOut(); + } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java index 0ae9475bd57bd..cdc60dccccfaf 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2020, the original author or authors. + * Copyright (c) 2002-2020, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -13,7 +13,7 @@ public class ReaderUtils { - private ReaderUtils() { } + private ReaderUtils() {} public static boolean isSet(LineReader reader, LineReader.Option option) { return reader != null && reader.isSet(option); @@ -30,8 +30,7 @@ public static boolean getBoolean(LineReader reader, String name, boolean def) { return (Boolean) v; } else if (v != null) { String s = v.toString(); - return s.isEmpty() || s.equalsIgnoreCase("on") - || s.equalsIgnoreCase("1") || s.equalsIgnoreCase("true"); + return s.isEmpty() || s.equalsIgnoreCase("on") || s.equalsIgnoreCase("1") || s.equalsIgnoreCase("true"); } return def; } @@ -77,5 +76,4 @@ public static int distance(String word, String cand) { return Levenshtein.distance(word, cand); } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/SimpleMaskingCallback.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/SimpleMaskingCallback.java index fd6c2663c057e..423497f3dc44a 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/SimpleMaskingCallback.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/SimpleMaskingCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2017, the original author or authors. + * Copyright (c) 2002-2017, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,10 +8,10 @@ */ package jdk.internal.org.jline.reader.impl; -import jdk.internal.org.jline.reader.MaskingCallback; - import java.util.Objects; +import jdk.internal.org.jline.reader.MaskingCallback; + /** * Simple {@link MaskingCallback} that will replace all the characters in the line with the given mask. * If the given mask is equal to {@link LineReaderImpl#NULL_MASK} then the line will be replaced with an empty String. @@ -29,7 +29,7 @@ public String display(String line) { return ""; } else { StringBuilder sb = new StringBuilder(line.length()); - for (int i = line.length(); i-- > 0;) { + for (int i = line.length(); i-- > 0; ) { sb.append((char) mask); } return sb.toString(); @@ -40,5 +40,4 @@ public String display(String line) { public String history(String line) { return null; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/UndoTree.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/UndoTree.java index 0a467f9d7bac7..3b193f9eb65ea 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/UndoTree.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/UndoTree.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -20,6 +20,7 @@ public class UndoTree { private final Node parent; private Node current; + @SuppressWarnings("this-escape") public UndoTree(Consumer s) { state = s; parent = new Node(null); @@ -71,5 +72,4 @@ public Node(T s) { state = s; } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/AggregateCompleter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/AggregateCompleter.java index e8e41088c598e..145823c53cede 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/AggregateCompleter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/AggregateCompleter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -24,9 +24,7 @@ * @author Jason Dillon * @since 2.3 */ -public class AggregateCompleter - implements Completer -{ +public class AggregateCompleter implements Completer { private final Collection completers; /** @@ -78,9 +76,6 @@ public void complete(LineReader reader, final ParsedLine line, final ListJason Dillon * @since 2.3 */ -public class ArgumentCompleter implements Completer -{ +public class ArgumentCompleter implements Completer { private final List completers = new ArrayList<>(); private boolean strict = true; @@ -108,12 +107,12 @@ public void complete(LineReader reader, ParsedLine line, final List c // if we are beyond the end of the completers, just use the last one if (line.wordIndex() >= completers.size()) { completer = completers.get(completers.size() - 1); - } - else { + } else { completer = completers.get(line.wordIndex()); } - // ensure that all the previous completers are successful before allowing this completer to pass (only if strict). + // ensure that all the previous completers are successful before allowing this completer to pass (only if + // strict). for (int i = strictCommand ? 0 : 1; isStrict() && (i < line.wordIndex()); i++) { int idx = i >= completers.size() ? (completers.size() - 1) : i; if (idx == 0 && !strictCommand) { diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/EnumCompleter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/EnumCompleter.java index 6a18d3cfd93a0..a701f2e3560bc 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/EnumCompleter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/EnumCompleter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -19,8 +19,7 @@ * @author Jason Dillon * @since 2.3 */ -public class EnumCompleter extends StringsCompleter -{ +public class EnumCompleter extends StringsCompleter { public EnumCompleter(Class> source) { Objects.requireNonNull(source); for (Enum n : source.getEnumConstants()) { diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/FileNameCompleter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/FileNameCompleter.java index 3d173f72ec7e5..32bf6e3493e6a 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/FileNameCompleter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/FileNameCompleter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -9,6 +9,7 @@ package jdk.internal.org.jline.reader.impl.completer; import java.io.IOException; +import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -41,11 +42,10 @@ * @author Marc Prud'hommeaux * @author Jason Dillon * @since 2.3 - * @deprecated use jdk.internal.org.jline.builtins.Completers$FileNameCompleter instead + * @deprecated use org.jline.builtins.Completers$FileNameCompleter instead */ @Deprecated -public class FileNameCompleter implements Completer -{ +public class FileNameCompleter implements Completer { public void complete(LineReader reader, ParsedLine commandLine, final List candidates) { assert commandLine != null; @@ -72,20 +72,21 @@ public void complete(LineReader reader, ParsedLine commandLine, final List { + try (DirectoryStream directoryStream = Files.newDirectoryStream(current, this::accept)) { + directoryStream.forEach(p -> { String value = curBuf + p.getFileName().toString(); if (Files.isDirectory(p)) { candidates.add(new Candidate( value + (reader.isSet(Option.AUTO_PARAM_SLASH) ? sep : ""), getDisplay(reader.getTerminal(), p), - null, null, + null, + null, reader.isSet(Option.AUTO_REMOVE_SLASH) ? sep : null, null, false)); } else { - candidates.add(new Candidate(value, getDisplay(reader.getTerminal(), p), - null, null, null, null, true)); + candidates.add( + new Candidate(value, getDisplay(reader.getTerminal(), p), null, null, null, null, true)); } }); } catch (IOException e) { @@ -125,5 +126,4 @@ protected String getDisplay(Terminal terminal, Path p) { } return name; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/NullCompleter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/NullCompleter.java index f3efefdc92919..21ce836a32f4e 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/NullCompleter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/NullCompleter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -22,11 +22,8 @@ * @author Jason Dillon * @since 2.3 */ -public final class NullCompleter - implements Completer -{ +public final class NullCompleter implements Completer { public static final NullCompleter INSTANCE = new NullCompleter(); - public void complete(LineReader reader, final ParsedLine line, final List candidates) { - } + public void complete(LineReader reader, final ParsedLine line, final List candidates) {} } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java index 8baac657b93d9..c791ef68e72a4 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2019, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -27,8 +27,7 @@ * @author Jason Dillon * @since 2.3 */ -public class StringsCompleter implements Completer -{ +public class StringsCompleter implements Completer { protected Collection candidates; protected Supplier> stringsSupplier; @@ -54,7 +53,7 @@ public StringsCompleter(Iterable strings) { } } - public StringsCompleter(Candidate ... candidates) { + public StringsCompleter(Candidate... candidates) { this(Arrays.asList(candidates)); } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/SystemCompleter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/SystemCompleter.java index a17a356bf0e5d..2fe6f8df49213 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/SystemCompleter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/SystemCompleter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2020, the original author or authors. + * Copyright (c) 2002-2020, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -22,8 +22,8 @@ * @author Matti Rinta-Nikkola */ public class SystemCompleter implements Completer { - private Map> completers = new HashMap<>(); - private Map aliasCommand = new HashMap<>(); + private Map> completers = new HashMap<>(); + private Map aliasCommand = new HashMap<>(); private StringsCompleter commands; private boolean compiled = false; @@ -44,9 +44,9 @@ public void complete(LineReader reader, ParsedLine commandLine, List commands.complete(reader, commandLine, candidates); } else if (reader.getParser().validVariableName(buffer.substring(0, eq))) { String curBuf = buffer.substring(0, eq + 1); - for (String c: completers.keySet()) { - candidates.add(new Candidate(AttributedString.stripAnsi(curBuf+c) - , c, null, null, null, null, true)); + for (String c : completers.keySet()) { + candidates.add( + new Candidate(AttributedString.stripAnsi(curBuf + c), c, null, null, null, null, true)); } } } else { @@ -81,7 +81,7 @@ public void add(String command, List completers) { } public void add(List commands, Completer completer) { - for (String c: commands) { + for (String c : commands) { add(c, completer); } } @@ -104,22 +104,22 @@ public void add(SystemCompleter other) { if (other.isCompiled()) { throw new IllegalStateException(); } - for (Map.Entry> entry: other.getCompleters().entrySet()) { - for (Completer c: entry.getValue()) { + for (Map.Entry> entry : other.getCompleters().entrySet()) { + for (Completer c : entry.getValue()) { add(entry.getKey(), c); } } addAliases(other.getAliases()); } - public void addAliases(Map aliasCommand) { + public void addAliases(Map aliasCommand) { if (compiled) { throw new IllegalStateException(); } this.aliasCommand.putAll(aliasCommand); } - private Map getAliases() { + private Map getAliases() { return aliasCommand; } @@ -128,7 +128,7 @@ public void compile() { return; } Map> compiledCompleters = new HashMap<>(); - for (Map.Entry> entry: completers.entrySet()) { + for (Map.Entry> entry : completers.entrySet()) { if (entry.getValue().size() == 1) { compiledCompleters.put(entry.getKey(), entry.getValue()); } else { @@ -143,8 +143,7 @@ public void compile() { compiled = true; } - public Map> getCompleters() { + public Map> getCompleters() { return completers; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java index cdd1bc520ddd8..c696b33ba9e5b 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2021, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -41,9 +41,9 @@ public class DefaultHistory implements History { private int offset = 0; private int index = 0; - public DefaultHistory() { - } + public DefaultHistory() {} + @SuppressWarnings("this-escape") public DefaultHistory(LineReader reader) { attach(reader); } @@ -67,8 +67,7 @@ public void attach(LineReader reader) { this.reader = reader; try { load(); - } - catch (IllegalArgumentException | IOException e) { + } catch (IllegalArgumentException | IOException e) { Log.warn("Failed to load history", e); } } @@ -117,13 +116,13 @@ public void read(Path file, boolean checkDuplicates) throws IOException { } } - private String doHistoryFileDataKey (Path path){ + private String doHistoryFileDataKey(Path path) { return path != null ? path.toAbsolutePath().toString() : null; } private HistoryFileData getHistoryFileData(Path path) { String key = doHistoryFileDataKey(path); - if (!historyFiles.containsKey(key)){ + if (!historyFiles.containsKey(key)) { historyFiles.put(key, new HistoryFileData()); } return historyFiles.get(key); @@ -133,7 +132,7 @@ private void setHistoryFileData(Path path, HistoryFileData historyFileData) { historyFiles.put(doHistoryFileDataKey(path), historyFileData); } - private boolean isLineReaderHistory (Path path) throws IOException { + private boolean isLineReaderHistory(Path path) throws IOException { Path lrp = getPath(); if (lrp == null) { return path == null; @@ -141,23 +140,23 @@ private boolean isLineReaderHistory (Path path) throws IOException { return Files.isSameFile(lrp, path); } - private void setLastLoaded(Path path, int lastloaded){ + private void setLastLoaded(Path path, int lastloaded) { getHistoryFileData(path).setLastLoaded(lastloaded); } - private void setEntriesInFile(Path path, int entriesInFile){ + private void setEntriesInFile(Path path, int entriesInFile) { getHistoryFileData(path).setEntriesInFile(entriesInFile); } - private void incEntriesInFile(Path path, int amount){ + private void incEntriesInFile(Path path, int amount) { getHistoryFileData(path).incEntriesInFile(amount); } - private int getLastLoaded(Path path){ + private int getLastLoaded(Path path) { return getHistoryFileData(path).getLastLoaded(); } - private int getEntriesInFile(Path path){ + private int getEntriesInFile(Path path) { return getHistoryFileData(path).getEntriesInFile(); } @@ -168,9 +167,8 @@ protected void addHistoryLine(Path path, String line) { protected void addHistoryLine(Path path, String line, boolean checkDuplicates) { if (reader.isSet(LineReader.Option.HISTORY_TIMESTAMPED)) { int idx = line.indexOf(':'); - final String badHistoryFileSyntax = "Bad history file syntax! " + - "The history file `" + path + "` may be an older history: " + - "please remove it or use a different history file."; + final String badHistoryFileSyntax = "Bad history file syntax! " + "The history file `" + path + + "` may be an older history: " + "please remove it or use a different history file."; if (idx < 0) { throw new IllegalArgumentException(badHistoryFileSyntax); } @@ -183,8 +181,7 @@ protected void addHistoryLine(Path path, String line, boolean checkDuplicates) { String unescaped = unescape(line.substring(idx + 1)); internalAdd(time, unescaped, checkDuplicates); - } - else { + } else { internalAdd(Instant.now(), unescape(line), checkDuplicates); } } @@ -210,8 +207,7 @@ public void write(Path file, boolean incremental) throws IOException { @Override public void append(Path file, boolean incremental) throws IOException { - internalWrite(file != null ? file : getPath(), - incremental ? getLastLoaded(file) : 0); + internalWrite(file != null ? file : getPath(), incremental ? getLastLoaded(file) : 0); } @Override @@ -227,8 +223,11 @@ private void internalWrite(Path path, int from) throws IOException { Files.createDirectories(parent); } // Append new items to the history file - try (BufferedWriter writer = Files.newBufferedWriter(path.toAbsolutePath(), - StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) { + try (BufferedWriter writer = Files.newBufferedWriter( + path.toAbsolutePath(), + StandardOpenOption.WRITE, + StandardOpenOption.APPEND, + StandardOpenOption.CREATE)) { for (Entry entry : items.subList(from, items.size())) { if (isPersistable(entry)) { writer.append(format(entry)); @@ -248,18 +247,23 @@ protected void trimHistory(Path path, int max) throws IOException { Log.trace("Trimming history path: ", path); // Load all history entries LinkedList allItems = new LinkedList<>(); - try (BufferedReader reader = Files.newBufferedReader(path)) { - reader.lines().forEach(l -> { - int idx = l.indexOf(':'); - Instant time = Instant.ofEpochMilli(Long.parseLong(l.substring(0, idx))); - String line = unescape(l.substring(idx + 1)); - allItems.add(createEntry(allItems.size(), time, line)); + try (BufferedReader historyFileReader = Files.newBufferedReader(path)) { + historyFileReader.lines().forEach(l -> { + if (reader.isSet(LineReader.Option.HISTORY_TIMESTAMPED)) { + int idx = l.indexOf(':'); + Instant time = Instant.ofEpochMilli(Long.parseLong(l.substring(0, idx))); + String line = unescape(l.substring(idx + 1)); + allItems.add(createEntry(allItems.size(), time, line)); + } else { + allItems.add(createEntry(allItems.size(), Instant.now(), unescape(l))); + } }); } // Remove duplicates List trimmedItems = doTrimHistory(allItems, max); // Write history - Path temp = Files.createTempFile(path.toAbsolutePath().getParent(), path.getFileName().toString(), ".tmp"); + Path temp = Files.createTempFile( + path.toAbsolutePath().getParent(), path.getFileName().toString(), ".tmp"); try (BufferedWriter writer = Files.newBufferedWriter(temp, StandardOpenOption.WRITE)) { for (Entry entry : trimmedItems) { writer.append(format(entry)); @@ -351,7 +355,7 @@ private String format(Entry entry) { public String get(final int index) { int idx = index - offset; if (idx >= items.size() || idx < 0) { - throw new IllegalArgumentException("IndexOutOfBounds: Index:" + idx +", Size:" + items.size()); + throw new IllegalArgumentException("IndexOutOfBounds: Index:" + idx + ", Size:" + items.size()); } return items.get(idx).line(); } @@ -382,8 +386,7 @@ public void add(Instant time, String line) { if (isSet(reader, LineReader.Option.HISTORY_INCREMENTAL)) { try { save(); - } - catch (IOException e) { + } catch (IOException e) { Log.warn("Failed to save history", e); } } @@ -417,7 +420,7 @@ protected void internalAdd(Instant time, String line) { protected void internalAdd(Instant time, String line, boolean checkDuplicates) { Entry entry = new EntryImpl(offset + items.size(), time, line); if (checkDuplicates) { - for (Entry e: items) { + for (Entry e : items) { if (e.line().trim().equals(line.trim())) { return; } @@ -430,7 +433,7 @@ protected void internalAdd(Instant time, String line, boolean checkDuplicates) { private void maybeResize() { while (size() > getInt(reader, LineReader.HISTORY_SIZE, DEFAULT_HISTORY_SIZE)) { items.removeFirst(); - for (HistoryFileData hfd: historyFiles.values()) { + for (HistoryFileData hfd : historyFiles.values()) { hfd.decLastLoaded(); } offset++; @@ -633,8 +636,7 @@ private static class HistoryFileData { private int lastLoaded = 0; private int entriesInFile = 0; - public HistoryFileData() { - } + public HistoryFileData() {} public HistoryFileData(int lastLoaded, int entriesInFile) { this.lastLoaded = lastLoaded; @@ -667,8 +669,5 @@ public void setEntriesInFile(int entriesInFile) { public void incEntriesInFile(int amount) { entriesInFile = entriesInFile + amount; } - } - } - diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Attributes.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Attributes.java index b1bab287e79be..04959488cf158 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Attributes.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Attributes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -43,63 +43,65 @@ public enum ControlChar { * Input flags - software input processing */ public enum InputFlag { - IGNBRK, /* ignore BREAK condition */ - BRKINT, /* map BREAK to SIGINTR */ - IGNPAR, /* ignore (discard) parity errors */ - PARMRK, /* mark parity and framing errors */ - INPCK, /* enable checking of parity errors */ - ISTRIP, /* strip 8th bit off chars */ - INLCR, /* map NL into CR */ - IGNCR, /* ignore CR */ - ICRNL, /* map CR to NL (ala CRMOD) */ - IXON, /* enable output flow control */ - IXOFF, /* enable input flow control */ - IXANY, /* any char will restart after stop */ - IMAXBEL, /* ring bell on input queue full */ - IUTF8 /* maintain state for UTF-8 VERASE */ + IGNBRK, /* ignore BREAK condition */ + BRKINT, /* map BREAK to SIGINTR */ + IGNPAR, /* ignore (discard) parity errors */ + PARMRK, /* mark parity and framing errors */ + INPCK, /* enable checking of parity errors */ + ISTRIP, /* strip 8th bit off chars */ + INLCR, /* map NL into CR */ + IGNCR, /* ignore CR */ + ICRNL, /* map CR to NL (ala CRMOD) */ + IXON, /* enable output flow control */ + IXOFF, /* enable input flow control */ + IXANY, /* any char will restart after stop */ + IMAXBEL, /* ring bell on input queue full */ + IUTF8, /* maintain state for UTF-8 VERASE */ + + INORMEOL /* normalize end-of-line */ } /* * Output flags - software output processing */ public enum OutputFlag { - OPOST, /* enable following output processing */ - ONLCR, /* map NL to CR-NL (ala CRMOD) */ - OXTABS, /* expand tabs to spaces */ - ONOEOT, /* discard EOT's (^D) on output) */ - OCRNL, /* map CR to NL on output */ - ONOCR, /* no CR output at column 0 */ - ONLRET, /* NL performs CR function */ - OFILL, /* use fill characters for delay */ - NLDLY, /* \n delay */ - TABDLY, /* horizontal tab delay */ - CRDLY, /* \r delay */ - FFDLY, /* form feed delay */ - BSDLY, /* \b delay */ - VTDLY, /* vertical tab delay */ - OFDEL /* fill is DEL, else NUL */ + OPOST, /* enable following output processing */ + ONLCR, /* map NL to CR-NL (ala CRMOD) */ + OXTABS, /* expand tabs to spaces */ + ONOEOT, /* discard EOT's (^D) on output) */ + OCRNL, /* map CR to NL on output */ + ONOCR, /* no CR output at column 0 */ + ONLRET, /* NL performs CR function */ + OFILL, /* use fill characters for delay */ + NLDLY, /* \n delay */ + TABDLY, /* horizontal tab delay */ + CRDLY, /* \r delay */ + FFDLY, /* form feed delay */ + BSDLY, /* \b delay */ + VTDLY, /* vertical tab delay */ + OFDEL /* fill is DEL, else NUL */ } /* * Control flags - hardware control of terminal */ public enum ControlFlag { - CIGNORE, /* ignore control flags */ - CS5, /* 5 bits (pseudo) */ - CS6, /* 6 bits */ - CS7, /* 7 bits */ - CS8, /* 8 bits */ - CSTOPB, /* send 2 stop bits */ - CREAD, /* enable receiver */ - PARENB, /* parity enable */ - PARODD, /* odd parity, else even */ - HUPCL, /* hang up on last close */ - CLOCAL, /* ignore modem status lines */ - CCTS_OFLOW, /* CTS flow control of output */ - CRTS_IFLOW, /* RTS flow control of input */ - CDTR_IFLOW, /* DTR flow control of input */ - CDSR_OFLOW, /* DSR flow control of output */ - CCAR_OFLOW /* DCD flow control of output */ + CIGNORE, /* ignore control flags */ + CS5, /* 5 bits (pseudo) */ + CS6, /* 6 bits */ + CS7, /* 7 bits */ + CS8, /* 8 bits */ + CSTOPB, /* send 2 stop bits */ + CREAD, /* enable receiver */ + PARENB, /* parity enable */ + PARODD, /* odd parity, else even */ + HUPCL, /* hang up on last close */ + CLOCAL, /* ignore modem status lines */ + CCTS_OFLOW, /* CTS flow control of output */ + CRTS_IFLOW, /* RTS flow control of input */ + CDTR_IFLOW, /* DTR flow control of input */ + CDSR_OFLOW, /* DSR flow control of output */ + CCAR_OFLOW /* DCD flow control of output */ } /* @@ -110,23 +112,23 @@ public enum ControlFlag { * input flag. */ public enum LocalFlag { - ECHOKE, /* visual erase for line kill */ - ECHOE, /* visually erase chars */ - ECHOK, /* echo NL after line kill */ - ECHO, /* enable echoing */ - ECHONL, /* echo NL even if ECHO is off */ - ECHOPRT, /* visual erase mode for hardcopy */ - ECHOCTL, /* echo control chars as ^(Char) */ - ISIG, /* enable signals INTR, QUIT, [D]SUSP */ - ICANON, /* canonicalize input lines */ - ALTWERASE, /* use alternate WERASE algorithm */ - IEXTEN, /* enable DISCARD and LNEXT */ - EXTPROC, /* external processing */ - TOSTOP, /* stop background jobs from output */ - FLUSHO, /* output being flushed (state) */ - NOKERNINFO, /* no kernel output from VSTATUS */ - PENDIN, /* XXX retype pending input (state) */ - NOFLSH /* don't flush after interrupt */ + ECHOKE, /* visual erase for line kill */ + ECHOE, /* visually erase chars */ + ECHOK, /* echo NL after line kill */ + ECHO, /* enable echoing */ + ECHONL, /* echo NL even if ECHO is off */ + ECHOPRT, /* visual erase mode for hardcopy */ + ECHOCTL, /* echo control chars as ^(Char) */ + ISIG, /* enable signals INTR, QUIT, [D]SUSP */ + ICANON, /* canonicalize input lines */ + ALTWERASE, /* use alternate WERASE algorithm */ + IEXTEN, /* enable DISCARD and LNEXT */ + EXTPROC, /* external processing */ + TOSTOP, /* stop background jobs from output */ + FLUSHO, /* output being flushed (state) */ + NOKERNINFO, /* no kernel output from VSTATUS */ + PENDIN, /* XXX retype pending input (state) */ + NOFLSH /* don't flush after interrupt */ } final EnumSet iflag = EnumSet.noneOf(InputFlag.class); @@ -135,9 +137,9 @@ public enum LocalFlag { final EnumSet lflag = EnumSet.noneOf(LocalFlag.class); final EnumMap cchars = new EnumMap<>(ControlChar.class); - public Attributes() { - } + public Attributes() {} + @SuppressWarnings("this-escape") public Attributes(Attributes attr) { copy(attr); } @@ -310,13 +312,12 @@ public void copy(Attributes attributes) { @Override public String toString() { - return "Attributes[" + - "lflags: " + append(lflag) + ", " + - "iflags: " + append(iflag) + ", " + - "oflags: " + append(oflag) + ", " + - "cflags: " + append(cflag) + ", " + - "cchars: " + append(EnumSet.allOf(ControlChar.class), this::display) + - "]"; + return "Attributes[" + "lflags: " + + append(lflag) + ", " + "iflags: " + + append(iflag) + ", " + "oflags: " + + append(oflag) + ", " + "cflags: " + + append(cflag) + ", " + "cchars: " + + append(EnumSet.allOf(ControlChar.class), this::display) + "]"; } private String display(ControlChar c) { @@ -345,5 +346,4 @@ private > String append(EnumSet set) { private > String append(EnumSet set, Function toString) { return set.stream().map(toString).collect(Collectors.joining(" ")); } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Cursor.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Cursor.java index c561349cb0ff0..9bc3d902849ed 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Cursor.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Cursor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/MouseEvent.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/MouseEvent.java index df59e3f41b250..059b43c51ae07 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/MouseEvent.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/MouseEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -71,12 +71,11 @@ public int getY() { @Override public String toString() { - return "MouseEvent[" + - "type=" + type + - ", button=" + button + - ", modifiers=" + modifiers + - ", x=" + x + - ", y=" + y + - ']'; + return "MouseEvent[" + "type=" + + type + ", button=" + + button + ", modifiers=" + + modifiers + ", x=" + + x + ", y=" + + y + ']'; } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Size.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Size.java index 29870ad626cd5..fde3bf35959ed 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Size.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Size.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -13,9 +13,9 @@ public class Size { private int rows; private int cols; - public Size() { - } + public Size() {} + @SuppressWarnings("this-escape") public Size(int columns, int rows) { this(); setColumns(columns); @@ -50,7 +50,7 @@ public void setRows(int rows) { * @return the cursor position */ public int cursorPos(int row, int col) { - return row * (cols+1) + col; + return row * (cols + 1) + col; } public void copy(Size size) { diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java index 38ffb029f604a..054915e133d2a 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -34,6 +34,7 @@ public interface Terminal extends Closeable, Flushable { * Type used for dumb terminals. */ String TYPE_DUMB = "dumb"; + String TYPE_DUMB_COLOR = "dumb-color"; String getName(); @@ -42,6 +43,9 @@ public interface Terminal extends Closeable, Flushable { // Signal support // + /** + * Types of signals. + */ enum Signal { INT, QUIT, @@ -51,16 +55,55 @@ enum Signal { WINCH } + /** + * The SignalHandler defines the interface used to trap signals and perform specific behaviors. + * @see Terminal.Signal + * @see Terminal#handle(Signal, SignalHandler) + */ interface SignalHandler { + /** + * The {@code SIG_DFL} value can be used to specify that the JVM default behavior + * should be used to handle this signal. + */ SignalHandler SIG_DFL = NativeSignalHandler.SIG_DFL; + + /** + * The {@code SIG_IGN} value can be used to ignore this signal and not perform + * any special processing. + */ SignalHandler SIG_IGN = NativeSignalHandler.SIG_IGN; + /** + * Handle the signal. + * @param signal the signal + */ void handle(Signal signal); } + /** + * Registers a handler for the given {@link Signal}. + *

      + * Note that the JVM does not easily allow catching the {@link Signal#QUIT} signal, which causes a thread dump + * to be displayed. This signal is mainly used when connecting through an SSH socket to a virtual terminal. + * + * @param signal the signal to register a handler for + * @param handler the handler + * @return the previous signal handler + */ SignalHandler handle(Signal signal, SignalHandler handler); + /** + * Raise the specific signal. + * This is not method usually called by non system terminals. + * When accessing a terminal through a SSH or Telnet connection, signals may be + * conveyed by the protocol and thus need to be raised when reaching the terminal code. + * The terminals do that automatically when the terminal input stream has a character + * mapped to {@link Attributes.ControlChar#VINTR}, {@link Attributes.ControlChar#VQUIT}, + * or {@link Attributes.ControlChar#VSUSP}. + * + * @param signal the signal to raise + */ void raise(Signal signal); // @@ -180,8 +223,21 @@ interface SignalHandler { boolean echo(boolean echo); + /** + * Returns the terminal attributes. + * The returned object can be safely modified + * further used in a call to {@link #setAttributes(Attributes)}. + * + * @return the terminal attributes. + */ Attributes getAttributes(); + /** + * Set the terminal attributes. + * The terminal will perform a copy of the given attributes. + * + * @param attr the new attributes + */ void setAttributes(Attributes attr); /** @@ -334,5 +390,4 @@ enum MouseTracking { * Color support */ ColorPalette getPalette(); - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java index 8c047e373422e..3b02ec2d8d8ac 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2021, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,9 +8,6 @@ */ package jdk.internal.org.jline.terminal; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -19,11 +16,16 @@ import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.ServiceLoader; +import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.stream.Collectors; @@ -31,8 +33,10 @@ import jdk.internal.org.jline.terminal.impl.AbstractPosixTerminal; import jdk.internal.org.jline.terminal.impl.AbstractTerminal; -import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal; import jdk.internal.org.jline.terminal.impl.DumbTerminal; +import jdk.internal.org.jline.terminal.impl.DumbTerminalProvider; +import jdk.internal.org.jline.terminal.spi.SystemStream; +import jdk.internal.org.jline.terminal.spi.TerminalExt; import jdk.internal.org.jline.terminal.spi.TerminalProvider; import jdk.internal.org.jline.utils.Log; import jdk.internal.org.jline.utils.OSUtils; @@ -49,16 +53,30 @@ public final class TerminalBuilder { public static final String PROP_ENCODING = "org.jline.terminal.encoding"; public static final String PROP_CODEPAGE = "org.jline.terminal.codepage"; public static final String PROP_TYPE = "org.jline.terminal.type"; - public static final String PROP_JNA = "org.jline.terminal.jna"; - public static final String PROP_JANSI = "org.jline.terminal.jansi"; - public static final String PROP_EXEC = "org.jline.terminal.exec"; - public static final String PROP_DUMB = "org.jline.terminal.dumb"; + public static final String PROP_PROVIDER = "org.jline.terminal.provider"; + public static final String PROP_PROVIDERS = "org.jline.terminal.providers"; + public static final String PROP_PROVIDER_FFM = "ffm"; + public static final String PROP_PROVIDER_JNI = "jni"; + public static final String PROP_PROVIDER_JANSI = "jansi"; + public static final String PROP_PROVIDER_JNA = "jna"; + public static final String PROP_PROVIDER_EXEC = "exec"; + public static final String PROP_PROVIDER_DUMB = "dumb"; + public static final String PROP_PROVIDERS_DEFAULT = String.join( + ",", PROP_PROVIDER_FFM, PROP_PROVIDER_JNI, PROP_PROVIDER_JANSI, PROP_PROVIDER_JNA, PROP_PROVIDER_EXEC); + public static final String PROP_FFM = "org.jline.terminal." + PROP_PROVIDER_FFM; + public static final String PROP_JNI = "org.jline.terminal." + PROP_PROVIDER_JNI; + public static final String PROP_JANSI = "org.jline.terminal." + PROP_PROVIDER_JANSI; + public static final String PROP_JNA = "org.jline.terminal." + PROP_PROVIDER_JNA; + public static final String PROP_EXEC = "org.jline.terminal." + PROP_PROVIDER_EXEC; + public static final String PROP_DUMB = "org.jline.terminal." + PROP_PROVIDER_DUMB; public static final String PROP_DUMB_COLOR = "org.jline.terminal.dumb.color"; public static final String PROP_OUTPUT = "org.jline.terminal.output"; public static final String PROP_OUTPUT_OUT = "out"; public static final String PROP_OUTPUT_ERR = "err"; public static final String PROP_OUTPUT_OUT_ERR = "out-err"; public static final String PROP_OUTPUT_ERR_OUT = "err-out"; + public static final String PROP_OUTPUT_FORCED_OUT = "forced-out"; + public static final String PROP_OUTPUT_FORCED_ERR = "forced-err"; // // Other system properties controlling various jline parts @@ -68,6 +86,32 @@ public final class TerminalBuilder { public static final String PROP_COLOR_DISTANCE = "org.jline.utils.colorDistance"; public static final String PROP_DISABLE_ALTERNATE_CHARSET = "org.jline.utils.disableAlternateCharset"; + // + // System properties controlling how FileDescriptor are create. + // The value can be a comma separated list of defined mechanisms. + // + public static final String PROP_FILE_DESCRIPTOR_CREATION_MODE = "org.jline.terminal.pty.fileDescriptorCreationMode"; + public static final String PROP_FILE_DESCRIPTOR_CREATION_MODE_NATIVE = "native"; + public static final String PROP_FILE_DESCRIPTOR_CREATION_MODE_REFLECTION = "reflection"; + public static final String PROP_FILE_DESCRIPTOR_CREATION_MODE_DEFAULT = + String.join(",", PROP_FILE_DESCRIPTOR_CREATION_MODE_REFLECTION, PROP_FILE_DESCRIPTOR_CREATION_MODE_NATIVE); + + // + // System properties controlling how RedirectPipe are created. + // The value can be a comma separated list of defined mechanisms. + // + public static final String PROP_REDIRECT_PIPE_CREATION_MODE = "org.jline.terminal.exec.redirectPipeCreationMode"; + public static final String PROP_REDIRECT_PIPE_CREATION_MODE_NATIVE = "native"; + public static final String PROP_REDIRECT_PIPE_CREATION_MODE_REFLECTION = "reflection"; + public static final String PROP_REDIRECT_PIPE_CREATION_MODE_DEFAULT = + String.join(",", PROP_REDIRECT_PIPE_CREATION_MODE_REFLECTION, PROP_REDIRECT_PIPE_CREATION_MODE_NATIVE); + + public static final Set DEPRECATED_PROVIDERS = + Collections.unmodifiableSet(new HashSet<>(Arrays.asList(PROP_PROVIDER_JNA, PROP_PROVIDER_JANSI))); + + public static final String PROP_DISABLE_DEPRECATED_PROVIDER_WARNING = + "org.jline.terminal.disableDeprecatedProviderWarning"; + // // Terminal output control // @@ -75,7 +119,9 @@ public enum SystemOutput { SysOut, SysErr, SysOutOrSysErr, - SysErrOrSysOut + SysErrOrSysOut, + ForcedSysOut, + ForcedSysErr } /** @@ -115,20 +161,23 @@ public static TerminalBuilder builder() { private int codepage; private Boolean system; private SystemOutput systemOutput; + private String provider; + private String providers; private Boolean jna; private Boolean jansi; + private Boolean jni; private Boolean exec; + private Boolean ffm; private Boolean dumb; private Boolean color; private Attributes attributes; private Size size; - private boolean nativeSignals = false; + private boolean nativeSignals = true; private Function inputStreamWrapper = in -> in; private Terminal.SignalHandler signalHandler = Terminal.SignalHandler.SIG_DFL; private boolean paused = false; - private TerminalBuilder() { - } + private TerminalBuilder() {} public TerminalBuilder name(String name) { this.name = name; @@ -160,6 +209,16 @@ public TerminalBuilder systemOutput(SystemOutput systemOutput) { return this; } + public TerminalBuilder provider(String provider) { + this.provider = provider; + return this; + } + + public TerminalBuilder providers(String providers) { + this.providers = providers; + return this; + } + public TerminalBuilder jna(boolean jna) { this.jna = jna; return this; @@ -170,11 +229,21 @@ public TerminalBuilder jansi(boolean jansi) { return this; } + public TerminalBuilder jni(boolean jni) { + this.jni = jni; + return this; + } + public TerminalBuilder exec(boolean exec) { this.exec = exec; return this; } + public TerminalBuilder ffm(boolean ffm) { + this.ffm = ffm; + return this; + } + public TerminalBuilder dumb(boolean dumb) { this.dumb = dumb; return this; @@ -280,6 +349,12 @@ public TerminalBuilder nativeSignals(boolean nativeSignals) { return this; } + /** + * Determines the default value for signal handlers. + * All signals will be mapped to the given handler. + * @param signalHandler the default signal handler + * @return The builder + */ public TerminalBuilder signalHandler(Terminal.SignalHandler signalHandler) { this.signalHandler = signalHandler; return this; @@ -305,6 +380,11 @@ public TerminalBuilder paused(boolean paused) { return this; } + /** + * Builds the terminal. + * @return the newly created terminal, never {@code null} + * @throws IOException if an error occurs + */ public Terminal build() throws IOException { Terminal override = TERMINAL_OVERRIDE.get(); Terminal terminal = override != null ? override : doBuild(); @@ -313,7 +393,8 @@ public Terminal build() throws IOException { } Log.debug(() -> "Using terminal " + terminal.getClass().getSimpleName()); if (terminal instanceof AbstractPosixTerminal) { - Log.debug(() -> "Using pty " + ((AbstractPosixTerminal) terminal).getPty().getClass().getSimpleName()); + Log.debug(() -> "Using pty " + + ((AbstractPosixTerminal) terminal).getPty().getClass().getSimpleName()); } return terminal; } @@ -323,144 +404,76 @@ private Terminal doBuild() throws IOException { if (name == null) { name = "JLine terminal"; } - Charset encoding = this.encoding; - if (encoding == null) { - String charsetName = System.getProperty(PROP_ENCODING); - if (charsetName != null && Charset.isSupported(charsetName)) { - encoding = Charset.forName(charsetName); - } - } - if (encoding == null) { - int codepage = this.codepage; - if (codepage <= 0) { - String str = System.getProperty(PROP_CODEPAGE); - if (str != null) { - codepage = Integer.parseInt(str); - } - } - if (codepage >= 0) { - encoding = getCodepageCharset(codepage); - } else { - encoding = StandardCharsets.UTF_8; - } - } - String type = this.type; - if (type == null) { - type = System.getProperty(PROP_TYPE); - } - if (type == null) { - type = System.getenv("TERM"); - } - Boolean jna = this.jna; - if (jna == null) { - jna = getBoolean(PROP_JNA, true); - } - Boolean jansi = this.jansi; - if (jansi == null) { - jansi = getBoolean(PROP_JANSI, true); - } - Boolean exec = this.exec; - if (exec == null) { - exec = getBoolean(PROP_EXEC, true); + Charset encoding = computeEncoding(); + String type = computeType(); + + String provider = this.provider; + if (provider == null) { + provider = System.getProperty(PROP_PROVIDER, null); } + + boolean forceDumb = + (DumbTerminal.TYPE_DUMB.equals(type) || type != null && type.startsWith(DumbTerminal.TYPE_DUMB_COLOR)) + || (provider != null && provider.equals(PROP_PROVIDER_DUMB)); Boolean dumb = this.dumb; if (dumb == null) { dumb = getBoolean(PROP_DUMB, null); } IllegalStateException exception = new IllegalStateException("Unable to create a terminal"); - List providers = new ArrayList<>(); - if (jna) { - try { - TerminalProvider provider = TerminalProvider.load("jna"); - providers.add(provider); - } catch (Throwable t) { - Log.debug("Unable to load JNA support: ", t); - exception.addSuppressed(t); - } - } - if (jansi) { - try { - TerminalProvider provider = TerminalProvider.load("jansi"); - providers.add(provider); - } catch (Throwable t) { - Log.debug("Unable to load JANSI support: ", t); - exception.addSuppressed(t); - } - } - if (exec) - { - try { - TerminalProvider provider = TerminalProvider.load("exec"); - providers.add(provider); - } catch (Throwable t) { - Log.debug("Unable to load EXEC support: ", t); - exception.addSuppressed(t); - } - } - + List providers = getProviders(provider, exception); Terminal terminal = null; if ((system != null && system) || (system == null && in == null && out == null)) { - if (system != null && ((in != null && !in.equals(System.in)) || - (out != null && !out.equals(System.out) && !out.equals(System.err)))) { + if (system != null + && ((in != null && !in.equals(System.in)) + || (out != null && !out.equals(System.out) && !out.equals(System.err)))) { throw new IllegalArgumentException("Cannot create a system terminal using non System streams"); } if (attributes != null || size != null) { Log.warn("Attributes and size fields are ignored when creating a system terminal"); } - if (out != null) { - if (out.equals(System.out)) { - systemOutput = SystemOutput.SysOut; - } else if (out.equals(System.err)) { - systemOutput = SystemOutput.SysErr; - } - } - if (systemOutput == null) { - String str = System.getProperty(PROP_OUTPUT); - if (str != null) { - switch (str.trim().toLowerCase(Locale.ROOT)) { - case PROP_OUTPUT_OUT: systemOutput = SystemOutput.SysOut; break; - case PROP_OUTPUT_ERR: systemOutput = SystemOutput.SysErr; break; - case PROP_OUTPUT_OUT_ERR: systemOutput = SystemOutput.SysOutOrSysErr; break; - case PROP_OUTPUT_ERR_OUT: systemOutput = SystemOutput.SysErrOrSysOut; break; - default: - Log.debug("Unsupported value for " + PROP_OUTPUT + ": " + str + ". Supported values are: " - + String.join(", ", PROP_OUTPUT_OUT, PROP_OUTPUT_ERR, PROP_OUTPUT_OUT_ERR,PROP_OUTPUT_ERR_OUT) - + "."); - } - } - } - if (systemOutput == null) { - systemOutput = SystemOutput.SysOutOrSysErr; - } - Map system = Stream.of(TerminalProvider.Stream.values()) - .collect(Collectors.toMap(stream -> stream, stream -> providers.stream().anyMatch(p -> p.isSystemStream(stream)))); - TerminalProvider.Stream console = select(system, systemOutput); + SystemOutput systemOutput = computeSystemOutput(); + Map system = Stream.of(SystemStream.values()) + .collect(Collectors.toMap( + stream -> stream, stream -> providers.stream().anyMatch(p -> p.isSystemStream(stream)))); + SystemStream systemStream = select(system, systemOutput); - if (system.get(TerminalProvider.Stream.Input) && console != null) { + if (!forceDumb && system.get(SystemStream.Input) && systemStream != null) { if (attributes != null || size != null) { Log.warn("Attributes and size fields are ignored when creating a system terminal"); } boolean ansiPassThrough = OSUtils.IS_CONEMU; // Cygwin defaults to XTERM, but actually supports 256 colors, // so if the value comes from the environment, change it to xterm-256color - if ((OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) && "xterm".equals(type) - && this.type == null && System.getProperty(PROP_TYPE) == null) { + if ((OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) + && "xterm".equals(type) + && this.type == null + && System.getProperty(PROP_TYPE) == null) { type = "xterm-256color"; } - for ( TerminalProvider provider : providers) { + for (TerminalProvider prov : providers) { if (terminal == null) { try { - terminal = provider.sysTerminal(name, type, ansiPassThrough, encoding, - nativeSignals, signalHandler, paused, console, inputStreamWrapper); + terminal = prov.sysTerminal( + name, + type, + ansiPassThrough, + encoding, + nativeSignals, + signalHandler, + paused, + systemStream, + inputStreamWrapper); } catch (Throwable t) { - Log.debug("Error creating " + provider.name() + " based terminal: ", t.getMessage(), t); + Log.debug("Error creating " + prov.name() + " based terminal: ", t.getMessage(), t); exception.addSuppressed(t); } } } - if (terminal == null && OSUtils.IS_WINDOWS && !jna && !jansi && (dumb == null || !dumb)) { - throw new IllegalStateException("Unable to create a system terminal. On windows, either " - + "JNA or JANSI library is required. Make sure to add one of those in the classpath."); + if (terminal == null && OSUtils.IS_WINDOWS && providers.isEmpty() && (dumb == null || !dumb)) { + throw new IllegalStateException( + "Unable to create a system terminal. On Windows, either JLine's native libraries, JNA " + + "or Jansi library is required. Make sure to add one of those in the classpath.", + exception); } } if (terminal instanceof AbstractTerminal) { @@ -468,54 +481,42 @@ private Terminal doBuild() throws IOException { if (SYSTEM_TERMINAL.compareAndSet(null, t)) { t.setOnClose(() -> SYSTEM_TERMINAL.compareAndSet(t, null)); } else { - exception.addSuppressed(new IllegalStateException("A system terminal is already running. " + - "Make sure to use the created system Terminal on the LineReaderBuilder if you're using one " + - "or that previously created system Terminals have been correctly closed.")); + exception.addSuppressed(new IllegalStateException("A system terminal is already running. " + + "Make sure to use the created system Terminal on the LineReaderBuilder if you're using one " + + "or that previously created system Terminals have been correctly closed.")); terminal.close(); terminal = null; } } - if (terminal == null && (dumb == null || dumb)) { - // forced colored dumb terminal - Boolean color = this.color; - if (color == null) { - color = getBoolean(PROP_DUMB_COLOR, false); - // detect emacs using the env variable - if (!color) { - String emacs = System.getenv("INSIDE_EMACS"); - color = emacs != null && emacs.contains("comint"); - } - // detect Intellij Idea - if (!color) { - String command = getParentProcessCommand(); - color = command != null && command.contains("idea"); - } - if (!color) { - color = system.get(TerminalProvider.Stream.Output) && System.getenv("TERM") != null; - } - if (!color && dumb == null) { - if (Log.isDebugEnabled()) { - Log.warn("input is tty: {}", system.get(TerminalProvider.Stream.Input)); - Log.warn("output is tty: {}", system.get(TerminalProvider.Stream.Output)); - Log.warn("error is tty: {}", system.get(TerminalProvider.Stream.Error)); - Log.warn("Creating a dumb terminal", exception); - } else { - Log.warn("Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)"); - } + if (terminal == null && (forceDumb || dumb == null || dumb)) { + if (!forceDumb && dumb == null) { + if (Log.isDebugEnabled()) { + Log.warn("input is tty: " + system.get(SystemStream.Input)); + Log.warn("output is tty: " + system.get(SystemStream.Output)); + Log.warn("error is tty: " + system.get(SystemStream.Error)); + Log.warn("Creating a dumb terminal", exception); + } else { + Log.warn( + "Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)"); } } - terminal = new DumbTerminal(name, color ? Terminal.TYPE_DUMB_COLOR : Terminal.TYPE_DUMB, - new FileInputStream(FileDescriptor.in), - new FileOutputStream(console == TerminalProvider.Stream.Output ? FileDescriptor.out : FileDescriptor.err), - encoding, signalHandler); + type = getDumbTerminalType(dumb, systemStream); + terminal = new DumbTerminalProvider() + .sysTerminal(name, type, false, encoding, nativeSignals, signalHandler, paused, systemStream, inputStreamWrapper); + if (OSUtils.IS_WINDOWS) { + Attributes attr = terminal.getAttributes(); + attr.setInputFlag(Attributes.InputFlag.IGNCR, true); + terminal.setAttributes(attr); + } } } else { - for ( TerminalProvider provider : providers) { + for (TerminalProvider prov : providers) { if (terminal == null) { try { - terminal = provider.newTerminal(name, type, inputStreamWrapper.apply(in), out, encoding, signalHandler, paused, attributes, size); + terminal = prov.newTerminal( + name, type, in, out, encoding, signalHandler, paused, attributes, size); } catch (Throwable t) { - Log.debug("Error creating " + provider.name() + " based terminal: ", t.getMessage(), t); + Log.debug("Error creating " + prov.name() + " based terminal: ", t.getMessage(), t); exception.addSuppressed(t); } } @@ -524,25 +525,220 @@ private Terminal doBuild() throws IOException { if (terminal == null) { throw exception; } + if (terminal instanceof TerminalExt) { + TerminalExt te = (TerminalExt) terminal; + if (DEPRECATED_PROVIDERS.contains(te.getProvider().name()) + && !getBoolean(PROP_DISABLE_DEPRECATED_PROVIDER_WARNING, false)) { + Log.warn("The terminal provider " + te.getProvider().name() + + " has been deprecated, check your configuration. This warning can be disabled by setting the system property " + + PROP_DISABLE_DEPRECATED_PROVIDER_WARNING + " to true."); + } + } return terminal; } - private TerminalProvider.Stream select(Map system, SystemOutput systemOutput) { + private String getDumbTerminalType(Boolean dumb, SystemStream systemStream) { + // forced colored dumb terminal + Boolean color = this.color; + if (color == null) { + color = getBoolean(PROP_DUMB_COLOR, null); + } + if (dumb == null) { + // detect emacs using the env variable + if (color == null) { + String emacs = System.getenv("INSIDE_EMACS"); + if (emacs != null && emacs.contains("comint")) { + color = true; + } + } + // detect Intellij Idea + if (color == null) { + // using the env variable on windows + String ideHome = System.getenv("IDE_HOME"); + if (ideHome != null) { + color = true; + } else { + // using the parent process command on unix/mac + String command = getParentProcessCommand(); + if (command != null && command.endsWith("/idea")) { + color = true; + } + } + } + if (color == null) { + color = systemStream != null && System.getenv("TERM") != null; + } + } else { + if (color == null) { + color = false; + } + } + return color ? Terminal.TYPE_DUMB_COLOR : Terminal.TYPE_DUMB; + } + + public SystemOutput computeSystemOutput() { + SystemOutput systemOutput = null; + if (out != null) { + if (out.equals(System.out)) { + systemOutput = SystemOutput.SysOut; + } else if (out.equals(System.err)) { + systemOutput = SystemOutput.SysErr; + } + } + if (systemOutput == null) { + systemOutput = this.systemOutput; + } + if (systemOutput == null) { + String str = System.getProperty(PROP_OUTPUT); + if (str != null) { + switch (str.trim().toLowerCase(Locale.ROOT)) { + case PROP_OUTPUT_OUT: + systemOutput = SystemOutput.SysOut; + break; + case PROP_OUTPUT_ERR: + systemOutput = SystemOutput.SysErr; + break; + case PROP_OUTPUT_OUT_ERR: + systemOutput = SystemOutput.SysOutOrSysErr; + break; + case PROP_OUTPUT_ERR_OUT: + systemOutput = SystemOutput.SysErrOrSysOut; + break; + case PROP_OUTPUT_FORCED_OUT: + systemOutput = SystemOutput.ForcedSysOut; + break; + case PROP_OUTPUT_FORCED_ERR: + systemOutput = SystemOutput.ForcedSysErr; + break; + default: + Log.debug("Unsupported value for " + PROP_OUTPUT + ": " + str + ". Supported values are: " + + String.join( + ", ", + PROP_OUTPUT_OUT, + PROP_OUTPUT_ERR, + PROP_OUTPUT_OUT_ERR, + PROP_OUTPUT_ERR_OUT) + + "."); + } + } + } + if (systemOutput == null) { + systemOutput = SystemOutput.SysOutOrSysErr; + } + return systemOutput; + } + + public String computeType() { + String type = this.type; + if (type == null) { + type = System.getProperty(PROP_TYPE); + } + if (type == null) { + type = System.getenv("TERM"); + } + return type; + } + + public Charset computeEncoding() { + Charset encoding = this.encoding; + if (encoding == null) { + String charsetName = System.getProperty(PROP_ENCODING); + if (charsetName != null && Charset.isSupported(charsetName)) { + encoding = Charset.forName(charsetName); + } + } + if (encoding == null) { + int codepage = this.codepage; + if (codepage <= 0) { + String str = System.getProperty(PROP_CODEPAGE); + if (str != null) { + codepage = Integer.parseInt(str); + } + } + if (codepage >= 0) { + encoding = getCodepageCharset(codepage); + } else { + encoding = StandardCharsets.UTF_8; + } + } + return encoding; + } + + /** + * Get the list of available terminal providers. + * This list is sorted according to the {@link #PROP_PROVIDERS} system property. + * @param provider if not {@code null}, only this provider will be checked + * @param exception if a provider throws an exception, it will be added to this exception as a suppressed exception + * @return a list of terminal providers + */ + public List getProviders(String provider, IllegalStateException exception) { + List providers = new ArrayList<>(); + // Check ffm provider + checkProvider(provider, exception, providers, ffm, PROP_FFM, PROP_PROVIDER_FFM); + // Check jni provider + checkProvider(provider, exception, providers, jni, PROP_JNI, PROP_PROVIDER_JNI); + // Check jansi provider + checkProvider(provider, exception, providers, jansi, PROP_JANSI, PROP_PROVIDER_JANSI); + // Check jna provider + checkProvider(provider, exception, providers, jna, PROP_JNA, PROP_PROVIDER_JNA); + // Check exec provider + checkProvider(provider, exception, providers, exec, PROP_EXEC, PROP_PROVIDER_EXEC); + // Order providers + List order = Arrays.asList( + (this.providers != null ? this.providers : System.getProperty(PROP_PROVIDERS, PROP_PROVIDERS_DEFAULT)) + .split(",")); + providers.sort(Comparator.comparing(l -> { + int idx = order.indexOf(l.name()); + return idx >= 0 ? idx : Integer.MAX_VALUE; + })); + String names = providers.stream().map(TerminalProvider::name).collect(Collectors.joining(", ")); + Log.debug("Available providers: " + names); + return providers; + } + + private void checkProvider( + String provider, + IllegalStateException exception, + List providers, + Boolean load, + String property, + String name) { + Boolean doLoad = provider != null ? (Boolean) name.equals(provider) : load; + if (doLoad == null) { + doLoad = getBoolean(property, true); + } + if (doLoad) { + try { + TerminalProvider prov = TerminalProvider.load(name); + prov.isSystemStream(SystemStream.Output); + providers.add(prov); + } catch (Throwable t) { + Log.debug("Unable to load " + name + " provider: ", t); + exception.addSuppressed(t); + } + } + } + + private SystemStream select(Map system, SystemOutput systemOutput) { switch (systemOutput) { case SysOut: - return select(system, TerminalProvider.Stream.Output); + return select(system, SystemStream.Output); case SysErr: - return select(system, TerminalProvider.Stream.Error); + return select(system, SystemStream.Error); case SysOutOrSysErr: - return select(system, TerminalProvider.Stream.Output, TerminalProvider.Stream.Error); + return select(system, SystemStream.Output, SystemStream.Error); case SysErrOrSysOut: - return select(system, TerminalProvider.Stream.Error, TerminalProvider.Stream.Output); + return select(system, SystemStream.Error, SystemStream.Output); + case ForcedSysOut: + return SystemStream.Output; + case ForcedSysErr: + return SystemStream.Error; } return null; } - private static TerminalProvider.Stream select(Map system, TerminalProvider.Stream... streams) { - for (TerminalProvider.Stream s : streams) { + private static SystemStream select(Map system, SystemStream... streams) { + for (SystemStream s : streams) { if (system.get(s)) { return s; } @@ -557,7 +753,9 @@ private static String getParentProcessCommand() { Object parent = ((Optional) phClass.getMethod("parent").invoke(current)).orElse(null); Method infoMethod = phClass.getMethod("info"); Object info = infoMethod.invoke(parent); - Object command = ((Optional) infoMethod.getReturnType().getMethod("command").invoke(info)).orElse(null); + Object command = ((Optional) + infoMethod.getReturnType().getMethod("command").invoke(info)) + .orElse(null); return (String) command; } catch (Throwable t) { return null; @@ -582,7 +780,7 @@ private static S load(Class clazz) { private static final int UTF8_CODE_PAGE = 65001; private static Charset getCodepageCharset(int codepage) { - //http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html + // http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html if (codepage == UTF8_CODE_PAGE) { return StandardCharsets.UTF_8; } @@ -629,5 +827,4 @@ private static Charset getCodepageCharset(int codepage) { public static void setTerminalOverride(final Terminal terminal) { TERMINAL_OVERRIDE.set(terminal); } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPosixTerminal.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPosixTerminal.java index b5f55b80f1e31..5d000d070837b 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPosixTerminal.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPosixTerminal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -18,6 +18,8 @@ import jdk.internal.org.jline.terminal.Cursor; import jdk.internal.org.jline.terminal.Size; import jdk.internal.org.jline.terminal.spi.Pty; +import jdk.internal.org.jline.terminal.spi.SystemStream; +import jdk.internal.org.jline.terminal.spi.TerminalProvider; public abstract class AbstractPosixTerminal extends AbstractTerminal { @@ -28,7 +30,8 @@ public AbstractPosixTerminal(String name, String type, Pty pty) throws IOExcepti this(name, type, pty, null, SignalHandler.SIG_DFL); } - public AbstractPosixTerminal(String name, String type, Pty pty, Charset encoding, SignalHandler signalHandler) throws IOException { + public AbstractPosixTerminal(String name, String type, Pty pty, Charset encoding, SignalHandler signalHandler) + throws IOException { super(name, type, encoding, signalHandler); Objects.requireNonNull(pty); this.pty = pty; @@ -82,4 +85,13 @@ public Cursor getCursorPosition(IntConsumer discarded) { return CursorSupport.getCursorPosition(this, discarded); } + @Override + public TerminalProvider getProvider() { + return getPty().getProvider(); + } + + @Override + public SystemStream getSystemStream() { + return getPty().getSystemStream(); + } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPty.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPty.java index 0feab84fc7e99..3fbe63f3c5d6b 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPty.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPty.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2019, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,20 +8,39 @@ */ package jdk.internal.org.jline.terminal.impl; -import jdk.internal.org.jline.terminal.Attributes; -import jdk.internal.org.jline.terminal.spi.Pty; -import jdk.internal.org.jline.utils.NonBlockingInputStream; - +import java.io.FileDescriptor; +import java.io.FilterInputStream; import java.io.IOError; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; +import java.lang.reflect.Field; + +//import jdk.internal.org.jline.nativ.JLineLibrary; +//import jdk.internal.org.jline.nativ.JLineNativeLoader; +import jdk.internal.org.jline.terminal.Attributes; +import jdk.internal.org.jline.terminal.spi.Pty; +import jdk.internal.org.jline.terminal.spi.SystemStream; +import jdk.internal.org.jline.terminal.spi.TerminalProvider; +import jdk.internal.org.jline.utils.NonBlockingInputStream; +import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_FILE_DESCRIPTOR_CREATION_MODE; +import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_FILE_DESCRIPTOR_CREATION_MODE_DEFAULT; +import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_FILE_DESCRIPTOR_CREATION_MODE_NATIVE; +import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_FILE_DESCRIPTOR_CREATION_MODE_REFLECTION; import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_NON_BLOCKING_READS; public abstract class AbstractPty implements Pty { + protected final TerminalProvider provider; + protected final SystemStream systemStream; private Attributes current; + private boolean skipNextLf; + + public AbstractPty(TerminalProvider provider, SystemStream systemStream) { + this.provider = provider; + this.systemStream = systemStream; + } @Override public void setAttr(Attributes attr) throws IOException { @@ -32,10 +51,32 @@ public void setAttr(Attributes attr) throws IOException { @Override public InputStream getSlaveInput() throws IOException { InputStream si = doGetSlaveInput(); + InputStream nsi = new FilterInputStream(si) { + @Override + public int read() throws IOException { + for (; ; ) { + int c = super.read(); + if (current.getInputFlag(Attributes.InputFlag.INORMEOL)) { + if (c == '\r') { + skipNextLf = true; + c = '\n'; + } else if (c == '\n') { + if (skipNextLf) { + skipNextLf = false; + continue; + } + } else { + skipNextLf = false; + } + } + return c; + } + } + }; if (Boolean.parseBoolean(System.getProperty(PROP_NON_BLOCKING_READS, "true"))) { - return new PtyInputStream(si); + return new PtyInputStream(nsi); } else { - return si; + return nsi; } } @@ -49,6 +90,16 @@ protected void checkInterrupted() throws InterruptedIOException { } } + @Override + public TerminalProvider getProvider() { + return provider; + } + + @Override + public SystemStream getSystemStream() { + return systemStream; + } + class PtyInputStream extends NonBlockingInputStream { final InputStream in; int c = 0; @@ -102,4 +153,103 @@ private void setNonBlocking() { } } + private static FileDescriptorCreator fileDescriptorCreator; + + protected static FileDescriptor newDescriptor(int fd) { + if (fileDescriptorCreator == null) { + String str = + System.getProperty(PROP_FILE_DESCRIPTOR_CREATION_MODE, PROP_FILE_DESCRIPTOR_CREATION_MODE_DEFAULT); + String[] modes = str.split(","); + IllegalStateException ise = new IllegalStateException("Unable to create FileDescriptor"); + for (String mode : modes) { + try { + switch (mode) { + case PROP_FILE_DESCRIPTOR_CREATION_MODE_NATIVE: + fileDescriptorCreator = null;//new NativeFileDescriptorCreator(); + break; + case PROP_FILE_DESCRIPTOR_CREATION_MODE_REFLECTION: + fileDescriptorCreator = new ReflectionFileDescriptorCreator(); + break; + } + } catch (Throwable t) { + // ignore + ise.addSuppressed(t); + } + if (fileDescriptorCreator != null) { + break; + } + } + if (fileDescriptorCreator == null) { + throw ise; + } + } + return fileDescriptorCreator.newDescriptor(fd); + } + + interface FileDescriptorCreator { + FileDescriptor newDescriptor(int fd); + } + + /* + * Class that could be used on OpenJDK 17. However, it requires the following JVM option + * --add-exports java.base/jdk.internal.access=ALL-UNNAMED + * so the benefit does not seem important enough to warrant the problems caused + * by access the jdk.internal.access package at compile time, which itself requires + * custom compiler options and a different maven module, or at least a different compile + * phase with a JDK 17 compiler. + * So, just keep the ReflectionFileDescriptorCreator for now. + * + static class Jdk17FileDescriptorCreator implements FileDescriptorCreator { + private final jdk.internal.access.JavaIOFileDescriptorAccess fdAccess; + Jdk17FileDescriptorCreator() { + fdAccess = jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess(); + } + + @Override + public FileDescriptor newDescriptor(int fd) { + FileDescriptor descriptor = new FileDescriptor(); + fdAccess.set(descriptor, fd); + return descriptor; + } + } + */ + + /** + * Reflection based file descriptor creator. + * This requires the following option + * --add-opens java.base/java.io=ALL-UNNAMED + */ + static class ReflectionFileDescriptorCreator implements FileDescriptorCreator { + private final Field fileDescriptorField; + + ReflectionFileDescriptorCreator() throws Exception { + Field field = FileDescriptor.class.getDeclaredField("fd"); + field.setAccessible(true); + fileDescriptorField = field; + } + + @Override + public FileDescriptor newDescriptor(int fd) { + FileDescriptor descriptor = new FileDescriptor(); + try { + fileDescriptorField.set(descriptor, fd); + } catch (IllegalAccessException e) { + // This should not happen as the field has been set accessible + throw new IllegalStateException(e); + } + return descriptor; + } + } + +// static class NativeFileDescriptorCreator implements FileDescriptorCreator { +// NativeFileDescriptorCreator() { +// // Force load the library +// JLineNativeLoader.initialize(); +// } +// +// @Override +// public FileDescriptor newDescriptor(int fd) { +// return JLineLibrary.newFileDescriptor(fd); +// } +// } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java index e92233c7b4f47..4fb49b29b3f27 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2021, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -27,7 +27,7 @@ import jdk.internal.org.jline.terminal.Attributes.LocalFlag; import jdk.internal.org.jline.terminal.Cursor; import jdk.internal.org.jline.terminal.MouseEvent; -import jdk.internal.org.jline.terminal.Terminal; +import jdk.internal.org.jline.terminal.spi.TerminalExt; import jdk.internal.org.jline.utils.ColorPalette; import jdk.internal.org.jline.utils.Curses; import jdk.internal.org.jline.utils.InfoCmp; @@ -35,7 +35,7 @@ import jdk.internal.org.jline.utils.Log; import jdk.internal.org.jline.utils.Status; -public abstract class AbstractTerminal implements Terminal { +public abstract class AbstractTerminal implements TerminalExt { protected final String name; protected final String type; @@ -44,7 +44,7 @@ public abstract class AbstractTerminal implements Terminal { protected final Set bools = new HashSet<>(); protected final Map ints = new HashMap<>(); protected final Map strings = new HashMap<>(); - protected final ColorPalette palette = new ColorPalette(this); + protected final ColorPalette palette; protected Status status; protected Runnable onClose; @@ -52,10 +52,13 @@ public AbstractTerminal(String name, String type) throws IOException { this(name, type, null, SignalHandler.SIG_DFL); } - public AbstractTerminal(String name, String type, Charset encoding, SignalHandler signalHandler) throws IOException { + @SuppressWarnings("this-escape") + public AbstractTerminal(String name, String type, Charset encoding, SignalHandler signalHandler) + throws IOException { this.name = name; this.type = type != null ? type : "ansi"; this.encoding = encoding != null ? encoding : Charset.defaultCharset(); + this.palette = new ColorPalette(this); for (Signal signal : Signal.values()) { handlers.put(signal, signalHandler); } @@ -85,12 +88,13 @@ public SignalHandler handle(Signal signal, SignalHandler handler) { public void raise(Signal signal) { Objects.requireNonNull(signal); SignalHandler handler = handlers.get(signal); - if (handler != SignalHandler.SIG_DFL && handler != SignalHandler.SIG_IGN) { + if (handler == SignalHandler.SIG_DFL) { + if (status != null && signal == Signal.WINCH) { + status.resize(); + } + } else if (handler != SignalHandler.SIG_IGN) { handler.handle(signal); } - if (status != null && signal == Signal.WINCH) { - status.resize(); - } } public final void close() throws IOException { @@ -105,8 +109,7 @@ public final void close() throws IOException { protected void doClose() throws IOException { if (status != null) { - status.update(null); - flush(); + status.close(); } } @@ -126,7 +129,7 @@ protected void echoSignal(Signal signal) { if (cc != null) { int vcc = getAttributes().getControlChar(cc); if (vcc > 0 && vcc < 32) { - writer().write(new char[]{'^', (char) (vcc + '@')}, 0, 2); + writer().write(new char[] {'^', (char) (vcc + '@')}, 0, 2); } } } @@ -217,8 +220,7 @@ public Cursor getCursorPosition(IntConsumer discarded) { } private MouseEvent lastMouseEvent = new MouseEvent( - MouseEvent.Type.Moved, MouseEvent.Button.NoButton, - EnumSet.noneOf(MouseEvent.Modifier.class), 0, 0); + MouseEvent.Type.Moved, MouseEvent.Button.NoButton, EnumSet.noneOf(MouseEvent.Modifier.class), 0, 0); @Override public boolean hasMouseSupport() { @@ -268,16 +270,13 @@ public boolean canPauseResume() { } @Override - public void pause() { - } + public void pause() {} @Override - public void pause(boolean wait) throws InterruptedException { - } + public void pause(boolean wait) throws InterruptedException {} @Override - public void resume() { - } + public void resume() {} @Override public boolean paused() { diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsConsoleWriter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsConsoleWriter.java index 0c36902a1a772..301b89eb7963f 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsConsoleWriter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsConsoleWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2017, the original author or authors. + * Copyright (c) 2002-2017, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -29,11 +29,8 @@ public void write(char[] cbuf, int off, int len) throws IOException { } @Override - public void flush() { - } + public void flush() {} @Override - public void close() { - } - + public void close() {} } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java index 57cfb5d50b431..03c42fb19f7de 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2023, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,8 +8,20 @@ */ package jdk.internal.org.jline.terminal.impl; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + import jdk.internal.org.jline.terminal.Attributes; import jdk.internal.org.jline.terminal.Size; +import jdk.internal.org.jline.terminal.spi.SystemStream; +import jdk.internal.org.jline.terminal.spi.TerminalProvider; import jdk.internal.org.jline.utils.Curses; import jdk.internal.org.jline.utils.InfoCmp; import jdk.internal.org.jline.utils.Log; @@ -21,17 +33,6 @@ import jdk.internal.org.jline.utils.Signals; import jdk.internal.org.jline.utils.WriterOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.Writer; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - /** * The AbstractWindowsTerminal is used as the base class for windows terminal. * Due to windows limitations, mostly the missing support for ansi sequences, @@ -44,7 +45,7 @@ * the writer() becomes the primary output, while the output() is bridged * to the writer() using a WriterOutputStream wrapper. */ -public abstract class AbstractWindowsTerminal extends AbstractTerminal { +public abstract class AbstractWindowsTerminal extends AbstractTerminal { public static final String TYPE_WINDOWS = "windows"; public static final String TYPE_WINDOWS_256_COLOR = "windows-256color"; @@ -56,12 +57,13 @@ public abstract class AbstractWindowsTerminal extends AbstractTerminal { private static final int UTF8_CODE_PAGE = 65001; protected static final int ENABLE_PROCESSED_INPUT = 0x0001; - protected static final int ENABLE_LINE_INPUT = 0x0002; - protected static final int ENABLE_ECHO_INPUT = 0x0004; - protected static final int ENABLE_WINDOW_INPUT = 0x0008; - protected static final int ENABLE_MOUSE_INPUT = 0x0010; - protected static final int ENABLE_INSERT_MODE = 0x0020; + protected static final int ENABLE_LINE_INPUT = 0x0002; + protected static final int ENABLE_ECHO_INPUT = 0x0004; + protected static final int ENABLE_WINDOW_INPUT = 0x0008; + protected static final int ENABLE_MOUSE_INPUT = 0x0010; + protected static final int ENABLE_INSERT_MODE = 0x0020; protected static final int ENABLE_QUICK_EDIT_MODE = 0x0040; + protected static final int ENABLE_EXTENDED_FLAGS = 0x0080; protected final Writer slaveInputPipe; protected final InputStream input; @@ -71,7 +73,12 @@ public abstract class AbstractWindowsTerminal extends AbstractTerminal { protected final Map nativeHandlers = new HashMap<>(); protected final ShutdownHooks.Task closer; protected final Attributes attributes = new Attributes(); - protected final int originalConsoleMode; + protected final Console inConsole; + protected final Console outConsole; + protected final int originalInConsoleMode; + protected final int originalOutConsoleMode; + private final TerminalProvider provider; + private final SystemStream systemStream; protected final Object lock = new Object(); protected boolean paused = true; @@ -80,21 +87,42 @@ public abstract class AbstractWindowsTerminal extends AbstractTerminal { protected MouseTracking tracking = MouseTracking.Off; protected boolean focusTracking = false; private volatile boolean closing; - - public AbstractWindowsTerminal(Writer writer, String name, String type, Charset encoding, boolean nativeSignals, SignalHandler signalHandler, Function inputStreamWrapper) throws IOException { + protected boolean skipNextLf; + + @SuppressWarnings("this-escape") + public AbstractWindowsTerminal( + TerminalProvider provider, + SystemStream systemStream, + Writer writer, + String name, + String type, + Charset encoding, + boolean nativeSignals, + SignalHandler signalHandler, + Console inConsole, + int inConsoleMode, + Console outConsole, + int outConsoleMode, + Function inputStreamWrapper) + throws IOException { super(name, type, encoding, signalHandler); + this.provider = provider; + this.systemStream = systemStream; NonBlockingPumpReader reader = NonBlocking.nonBlockingPumpReader(); this.slaveInputPipe = reader.getWriter(); this.input = inputStreamWrapper.apply(NonBlocking.nonBlockingStream(reader, encoding())); this.reader = NonBlocking.nonBlocking(name, input, encoding()); this.writer = new PrintWriter(writer); this.output = new WriterOutputStream(writer, encoding()); + this.inConsole = inConsole; + this.outConsole = outConsole; parseInfoCmp(); // Attributes - originalConsoleMode = getConsoleMode(); + this.originalInConsoleMode = inConsoleMode; + this.originalOutConsoleMode = outConsoleMode; attributes.setLocalFlag(Attributes.LocalFlag.ISIG, true); attributes.setControlChar(Attributes.ControlChar.VINTR, ctrl('C')); - attributes.setControlChar(Attributes.ControlChar.VEOF, ctrl('D')); + attributes.setControlChar(Attributes.ControlChar.VEOF, ctrl('D')); attributes.setControlChar(Attributes.ControlChar.VSUSP, ctrl('Z')); // Handle signals if (nativeSignals) { @@ -148,7 +176,7 @@ public OutputStream output() { } public Attributes getAttributes() { - int mode = getConsoleMode(); + int mode = getConsoleMode(inConsole); if ((mode & ENABLE_ECHO_INPUT) != 0) { attributes.setLocalFlag(Attributes.LocalFlag.ECHO, true); } @@ -173,8 +201,11 @@ protected void updateConsoleMode() { } if (tracking != MouseTracking.Off) { mode |= ENABLE_MOUSE_INPUT; + // mouse events not send with quick edit mode + // to disable ENABLE_QUICK_EDIT_MODE just set extended flag + mode |= ENABLE_EXTENDED_FLAGS; } - setConsoleMode(mode); + setConsoleMode(inConsole, mode); } protected int ctrl(char key) { @@ -197,23 +228,26 @@ protected void doClose() throws IOException { } reader.close(); writer.close(); - setConsoleMode(originalConsoleMode); + setConsoleMode(inConsole, originalInConsoleMode); + setConsoleMode(outConsole, originalOutConsoleMode); } static final int SHIFT_FLAG = 0x01; - static final int ALT_FLAG = 0x02; - static final int CTRL_FLAG = 0x04; - - static final int RIGHT_ALT_PRESSED = 0x0001; - static final int LEFT_ALT_PRESSED = 0x0002; - static final int RIGHT_CTRL_PRESSED = 0x0004; - static final int LEFT_CTRL_PRESSED = 0x0008; - static final int SHIFT_PRESSED = 0x0010; - static final int NUMLOCK_ON = 0x0020; - static final int SCROLLLOCK_ON = 0x0040; - static final int CAPSLOCK_ON = 0x0080; - - protected void processKeyEvent(final boolean isKeyDown, final short virtualKeyCode, char ch, final int controlKeyState) throws IOException { + static final int ALT_FLAG = 0x02; + static final int CTRL_FLAG = 0x04; + + static final int RIGHT_ALT_PRESSED = 0x0001; + static final int LEFT_ALT_PRESSED = 0x0002; + static final int RIGHT_CTRL_PRESSED = 0x0004; + static final int LEFT_CTRL_PRESSED = 0x0008; + static final int SHIFT_PRESSED = 0x0010; + static final int NUMLOCK_ON = 0x0020; + static final int SCROLLLOCK_ON = 0x0040; + static final int CAPSLOCK_ON = 0x0080; + + protected void processKeyEvent( + final boolean isKeyDown, final short virtualKeyCode, char ch, final int controlKeyState) + throws IOException { final boolean isCtrl = (controlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) > 0; final boolean isAlt = (controlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) > 0; final boolean isShift = (controlKeyState & SHIFT_PRESSED) > 0; @@ -222,11 +256,13 @@ protected void processKeyEvent(final boolean isKeyDown, final short virtualKeyCo // Pressing "Alt Gr" is translated to Alt-Ctrl, hence it has to be checked that Ctrl is _not_ pressed, // otherwise inserting of "Alt Gr" codes on non-US keyboards would yield errors if (ch != 0 - && (controlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED | SHIFT_PRESSED)) - == (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)) { + && (controlKeyState + & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) + == (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)) { processInputChar(ch); } else { - final String keySeq = getEscapeSequence(virtualKeyCode, (isCtrl ? CTRL_FLAG : 0) + (isAlt ? ALT_FLAG : 0) + (isShift ? SHIFT_FLAG : 0)); + final String keySeq = getEscapeSequence( + virtualKeyCode, (isCtrl ? CTRL_FLAG : 0) + (isAlt ? ALT_FLAG : 0) + (isShift ? SHIFT_FLAG : 0)); if (keySeq != null) { for (char c : keySeq.toCharArray()) { processInputChar(c); @@ -240,7 +276,7 @@ protected void processKeyEvent(final boolean isKeyDown, final short virtualKeyCo * 4). Ctrl + Space(0x20) : uchar=0x20 * 5). Ctrl + : uchar=0 * 6). Ctrl + Alt + : uchar=0 - */ + */ if (ch > 0) { if (isAlt) { processInputChar('\033'); @@ -254,10 +290,10 @@ protected void processKeyEvent(final boolean isKeyDown, final short virtualKeyCo } else { processInputChar(ch); } - } else if (isCtrl) { //Handles the ctrl key events(uchar=0) + } else if (isCtrl) { // Handles the ctrl key events(uchar=0) if (virtualKeyCode >= 'A' && virtualKeyCode <= 'Z') { ch = (char) (virtualKeyCode - 0x40); - } else if (virtualKeyCode == 191) { //? + } else if (virtualKeyCode == 191) { // ? ch = 127; } if (ch > 0) { @@ -275,7 +311,7 @@ protected void processKeyEvent(final boolean isKeyDown, final short virtualKeyCo else { // support ALT+NumPad input method if (virtualKeyCode == 0x12 /*VK_MENU ALT key*/ && ch > 0) { - processInputChar(ch); // no such combination in Windows + processInputChar(ch); // no such combination in Windows } } } @@ -468,7 +504,19 @@ public void processInputChar(char c) throws IOException { raise(Signal.INFO); } } - if (c == '\r') { + if (attributes.getInputFlag(Attributes.InputFlag.INORMEOL)) { + if (c == '\r') { + skipNextLf = true; + c = '\n'; + } else if (c == '\n') { + if (skipNextLf) { + skipNextLf = false; + return; + } + } else { + skipNextLf = false; + } + } else if (c == '\r') { if (attributes.getInputFlag(Attributes.InputFlag.IGNCR)) { return; } @@ -478,10 +526,10 @@ public void processInputChar(char c) throws IOException { } else if (c == '\n' && attributes.getInputFlag(Attributes.InputFlag.INLCR)) { c = '\r'; } -// if (attributes.getLocalFlag(Attributes.LocalFlag.ECHO)) { -// processOutputByte(c); -// masterOutput.flush(); -// } + // if (attributes.getLocalFlag(Attributes.LocalFlag.ECHO)) { + // processOutputByte(c); + // masterOutput.flush(); + // } slaveInputPipe.write(c); } @@ -492,9 +540,9 @@ public boolean trackMouse(MouseTracking tracking) { return true; } - protected abstract int getConsoleMode(); + protected abstract int getConsoleMode(Console console); - protected abstract void setConsoleMode(int mode); + protected abstract void setConsoleMode(Console console, int mode); /** * Read a single input event from the input buffer and process it. @@ -504,5 +552,13 @@ public boolean trackMouse(MouseTracking tracking) { */ protected abstract boolean processConsoleInput() throws IOException; -} + @Override + public TerminalProvider getProvider() { + return provider; + } + @Override + public SystemStream getSystemStream() { + return systemStream; + } +} diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/CursorSupport.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/CursorSupport.java index ef22cd0a7d877..14253d8c1d19a 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/CursorSupport.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/CursorSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,17 +8,17 @@ */ package jdk.internal.org.jline.terminal.impl; -import jdk.internal.org.jline.terminal.Cursor; -import jdk.internal.org.jline.terminal.Terminal; -import jdk.internal.org.jline.utils.Curses; -import jdk.internal.org.jline.utils.InfoCmp; - import java.io.IOError; import java.io.IOException; import java.util.function.IntConsumer; import java.util.regex.Matcher; import java.util.regex.Pattern; +import jdk.internal.org.jline.terminal.Cursor; +import jdk.internal.org.jline.terminal.Terminal; +import jdk.internal.org.jline.utils.Curses; +import jdk.internal.org.jline.utils.InfoCmp; + public class CursorSupport { public static Cursor getCursorPosition(Terminal terminal, IntConsumer discarded) { @@ -105,5 +105,4 @@ public static Cursor getCursorPosition(Terminal terminal, IntConsumer discarded) throw new IOError(e); } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/Diag.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/Diag.java index dfa784c29f30c..dc54ea2157d8d 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/Diag.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/Diag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, the original author or authors. + * Copyright (c) 2022, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -17,6 +17,7 @@ import jdk.internal.org.jline.terminal.Attributes; import jdk.internal.org.jline.terminal.Terminal; +import jdk.internal.org.jline.terminal.spi.SystemStream; import jdk.internal.org.jline.terminal.spi.TerminalProvider; import jdk.internal.org.jline.utils.OSUtils; @@ -26,7 +27,7 @@ public static void main(String[] args) { diag(System.out); } - static void diag(PrintStream out) { + public static void diag(PrintStream out) { out.println("System properties"); out.println("================="); out.println("os.name = " + System.getProperty("os.name")); @@ -50,6 +51,17 @@ static void diag(PrintStream out) { out.println("IS_OSX = " + OSUtils.IS_OSX); out.println(); + // FFM + out.println("FFM Support"); + out.println("================="); + try { + TerminalProvider provider = TerminalProvider.load("ffm"); + testProvider(out, provider); + } catch (Throwable t) { + out.println("FFM support not available: " + t); + } + out.println(); + out.println("JnaSupport"); out.println("================="); try { @@ -60,13 +72,23 @@ static void diag(PrintStream out) { } out.println(); - out.println("JansiSupport"); + out.println("Jansi2Support"); out.println("================="); try { TerminalProvider provider = TerminalProvider.load("jansi"); testProvider(out, provider); } catch (Throwable t) { - out.println("Jansi support not available: " + t); + out.println("Jansi 2 support not available: " + t); + } + out.println(); + + out.println("JniSupport"); + out.println("================="); + try { + TerminalProvider provider = TerminalProvider.load("jni"); + testProvider(out, provider); + } catch (Throwable t) { + out.println("JNI support not available: " + t); } out.println(); @@ -83,32 +105,45 @@ static void diag(PrintStream out) { private static void testProvider(PrintStream out, TerminalProvider provider) { try { - out.println("StdIn stream = " + provider.isSystemStream(TerminalProvider.Stream.Input)); - out.println("StdOut stream = " + provider.isSystemStream(TerminalProvider.Stream.Output)); - out.println("StdErr stream = " + provider.isSystemStream(TerminalProvider.Stream.Error)); + out.println("StdIn stream = " + provider.isSystemStream(SystemStream.Input)); + out.println("StdOut stream = " + provider.isSystemStream(SystemStream.Output)); + out.println("StdErr stream = " + provider.isSystemStream(SystemStream.Error)); } catch (Throwable t2) { out.println("Unable to check stream: " + t2); } try { - out.println("StdIn stream name = " + provider.systemStreamName(TerminalProvider.Stream.Input)); - out.println("StdOut stream name = " + provider.systemStreamName(TerminalProvider.Stream.Output)); - out.println("StdErr stream name = " + provider.systemStreamName(TerminalProvider.Stream.Error)); + out.println("StdIn stream name = " + provider.systemStreamName(SystemStream.Input)); + out.println("StdOut stream name = " + provider.systemStreamName(SystemStream.Output)); + out.println("StdErr stream name = " + provider.systemStreamName(SystemStream.Error)); } catch (Throwable t2) { out.println("Unable to check stream names: " + t2); } - try (Terminal terminal = provider.sysTerminal("diag", "xterm", false, StandardCharsets.UTF_8, - false, Terminal.SignalHandler.SIG_DFL, false, TerminalProvider.Stream.Output, input -> input) ) { + try (Terminal terminal = provider.sysTerminal( + "diag", + "xterm", + false, + StandardCharsets.UTF_8, + false, + Terminal.SignalHandler.SIG_DFL, + false, + SystemStream.Output, + input -> input)) { if (terminal != null) { Attributes attr = terminal.enterRawMode(); try { out.println("Terminal size: " + terminal.getSize()); - ForkJoinTask t = new ForkJoinPool(1).submit(() -> terminal.reader().read(1) ); + ForkJoinTask t = + new ForkJoinPool(1).submit(() -> terminal.reader().read(1)); int r = t.get(1000, TimeUnit.MILLISECONDS); StringBuilder sb = new StringBuilder(); sb.append("The terminal seems to work: "); sb.append("terminal ").append(terminal.getClass().getName()); if (terminal instanceof AbstractPosixTerminal) { - sb.append(" with pty ").append(((AbstractPosixTerminal) terminal).getPty().getClass().getName()); + sb.append(" with pty ") + .append(((AbstractPosixTerminal) terminal) + .getPty() + .getClass() + .getName()); } out.println(sb); } catch (Throwable t3) { @@ -129,5 +164,4 @@ private static void testProvider(PrintStream out, TerminalProvider provider) { static S load(Class clazz) { return ServiceLoader.load(clazz, clazz.getClassLoader()).iterator().next(); } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/DumbTerminal.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/DumbTerminal.java index 0b8cb57cbbf24..2922132c1bfad 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/DumbTerminal.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/DumbTerminal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -14,38 +14,58 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.Charset; +import java.util.function.Function; import jdk.internal.org.jline.terminal.Attributes; import jdk.internal.org.jline.terminal.Attributes.ControlChar; import jdk.internal.org.jline.terminal.Size; +import jdk.internal.org.jline.terminal.spi.SystemStream; +import jdk.internal.org.jline.terminal.spi.TerminalProvider; import jdk.internal.org.jline.utils.NonBlocking; import jdk.internal.org.jline.utils.NonBlockingInputStream; import jdk.internal.org.jline.utils.NonBlockingReader; public class DumbTerminal extends AbstractTerminal { + private final TerminalProvider provider; + private final SystemStream systemStream; private final NonBlockingInputStream input; private final OutputStream output; private final NonBlockingReader reader; private final PrintWriter writer; private final Attributes attributes; private final Size size; + private boolean skipNextLf; - public DumbTerminal(InputStream in, OutputStream out) throws IOException { - this(TYPE_DUMB, TYPE_DUMB, in, out, null); + public DumbTerminal(InputStream in, OutputStream out, Function inputStreamWrapper) throws IOException { + this(TYPE_DUMB, TYPE_DUMB, in, out, null, inputStreamWrapper); } - public DumbTerminal(String name, String type, InputStream in, OutputStream out, Charset encoding) throws IOException { - this(name, type, in, out, encoding, SignalHandler.SIG_DFL); + public DumbTerminal(String name, String type, InputStream in, OutputStream out, Charset encoding, Function inputStreamWrapper) + throws IOException { + this(null, null, name, type, in, out, encoding, SignalHandler.SIG_DFL, inputStreamWrapper); } - public DumbTerminal(String name, String type, InputStream in, OutputStream out, Charset encoding, SignalHandler signalHandler) throws IOException { + @SuppressWarnings("this-escape") + public DumbTerminal( + TerminalProvider provider, + SystemStream systemStream, + String name, + String type, + InputStream in, + OutputStream out, + Charset encoding, + SignalHandler signalHandler, + Function inputStreamWrapper) + throws IOException { super(name, type, encoding, signalHandler); - NonBlockingInputStream nbis = NonBlocking.nonBlocking(getName(), in); + this.provider = provider; + this.systemStream = systemStream; + NonBlockingInputStream nbis = NonBlocking.nonBlocking(getName(), inputStreamWrapper.apply(in)); this.input = new NonBlockingInputStream() { @Override public int read(long timeout, boolean isPeek) throws IOException { - for (;;) { + for (; ; ) { int c = nbis.read(timeout, isPeek); if (attributes.getLocalFlag(Attributes.LocalFlag.ISIG)) { if (c == attributes.getControlChar(ControlChar.VINTR)) { @@ -62,7 +82,19 @@ public int read(long timeout, boolean isPeek) throws IOException { continue; } } - if (c == '\r') { + if (attributes.getInputFlag(Attributes.InputFlag.INORMEOL)) { + if (c == '\r') { + skipNextLf = true; + c = '\n'; + } else if (c == '\n') { + if (skipNextLf) { + skipNextLf = false; + continue; + } + } else { + skipNextLf = false; + } + } else if (c == '\r') { if (attributes.getInputFlag(Attributes.InputFlag.IGNCR)) { continue; } @@ -80,10 +112,10 @@ public int read(long timeout, boolean isPeek) throws IOException { this.reader = NonBlocking.nonBlocking(getName(), input, encoding()); this.writer = new PrintWriter(new OutputStreamWriter(output, encoding())); this.attributes = new Attributes(); - this.attributes.setControlChar(ControlChar.VERASE, (char) 127); + this.attributes.setControlChar(ControlChar.VERASE, (char) 127); this.attributes.setControlChar(ControlChar.VWERASE, (char) 23); - this.attributes.setControlChar(ControlChar.VKILL, (char) 21); - this.attributes.setControlChar(ControlChar.VLNEXT, (char) 22); + this.attributes.setControlChar(ControlChar.VKILL, (char) 21); + this.attributes.setControlChar(ControlChar.VLNEXT, (char) 22); this.size = new Size(); parseInfoCmp(); } @@ -107,9 +139,7 @@ public OutputStream output() { } public Attributes getAttributes() { - Attributes attr = new Attributes(); - attr.copy(attributes); - return attr; + return new Attributes(attributes); } public void setAttributes(Attributes attr) { @@ -126,4 +156,13 @@ public void setSize(Size sz) { size.copy(sz); } + @Override + public TerminalProvider getProvider() { + return provider; + } + + @Override + public SystemStream getSystemStream() { + return systemStream; + } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/DumbTerminalProvider.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/DumbTerminalProvider.java new file mode 100644 index 0000000000000..b99185d3dc94f --- /dev/null +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/DumbTerminalProvider.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package jdk.internal.org.jline.terminal.impl; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.function.Function; + +import jdk.internal.org.jline.terminal.Attributes; +import jdk.internal.org.jline.terminal.Size; +import jdk.internal.org.jline.terminal.Terminal; +import jdk.internal.org.jline.terminal.TerminalBuilder; +import jdk.internal.org.jline.terminal.spi.SystemStream; +import jdk.internal.org.jline.terminal.spi.TerminalProvider; + +public class DumbTerminalProvider implements TerminalProvider { + + @Override + public String name() { + return TerminalBuilder.PROP_PROVIDER_DUMB; + } + + @Override + public Terminal sysTerminal( + String name, + String type, + boolean ansiPassThrough, + Charset encoding, + boolean nativeSignals, + Terminal.SignalHandler signalHandler, + boolean paused, + SystemStream systemStream, + Function inputStreamWrapper) + throws IOException { + return new DumbTerminal( + this, + systemStream, + name, + type, + new FileInputStream(FileDescriptor.in), + new FileOutputStream(systemStream == SystemStream.Error ? FileDescriptor.err : FileDescriptor.out), + encoding, + signalHandler, + inputStreamWrapper); + } + + @Override + public Terminal newTerminal( + String name, + String type, + InputStream masterInput, + OutputStream masterOutput, + Charset encoding, + Terminal.SignalHandler signalHandler, + boolean paused, + Attributes attributes, + Size size) + throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSystemStream(SystemStream stream) { + return false; + } + + @Override + public String systemStreamName(SystemStream stream) { + return null; + } + + //@Override + //public int systemStreamWidth(SystemStream stream) { + // return 0; + //} + + @Override + public String toString() { + return "TerminalProvider[" + name() + "]"; + } +} diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExecPty.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExecPty.java deleted file mode 100644 index 5377b4acdb8a2..0000000000000 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExecPty.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * https://opensource.org/licenses/BSD-3-Clause - */ -package jdk.internal.org.jline.terminal.impl; - -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.FileDescriptor; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import jdk.internal.org.jline.terminal.Attributes; -import jdk.internal.org.jline.terminal.Attributes.ControlChar; -import jdk.internal.org.jline.terminal.Attributes.ControlFlag; -import jdk.internal.org.jline.terminal.Attributes.InputFlag; -import jdk.internal.org.jline.terminal.Attributes.LocalFlag; -import jdk.internal.org.jline.terminal.Attributes.OutputFlag; -import jdk.internal.org.jline.terminal.Size; -import jdk.internal.org.jline.terminal.spi.TerminalProvider; -import jdk.internal.org.jline.terminal.spi.Pty; -import jdk.internal.org.jline.utils.OSUtils; - -import static jdk.internal.org.jline.utils.ExecHelper.exec; - -public class ExecPty extends AbstractPty implements Pty { - - private final String name; - private final TerminalProvider.Stream console; - - public static Pty current(TerminalProvider.Stream console) throws IOException { - try { - String result = exec(true, OSUtils.TTY_COMMAND); - if (console != TerminalProvider.Stream.Output && console != TerminalProvider.Stream.Error) { - throw new IllegalArgumentException("console should be Output or Error: " + console); - } - return new ExecPty(result.trim(), console); - } catch (IOException e) { - throw new IOException("Not a tty", e); - } - } - - protected ExecPty(String name, TerminalProvider.Stream console) { - this.name = name; - this.console = console; - } - - @Override - public void close() throws IOException { - } - - public String getName() { - return name; - } - - @Override - public InputStream getMasterInput() { - throw new UnsupportedOperationException(); - } - - @Override - public OutputStream getMasterOutput() { - throw new UnsupportedOperationException(); - } - - @Override - protected InputStream doGetSlaveInput() throws IOException { - return console != null - ? new FileInputStream(FileDescriptor.in) - : new FileInputStream(getName()); - } - - @Override - public OutputStream getSlaveOutput() throws IOException { - return console == TerminalProvider.Stream.Output - ? new FileOutputStream(FileDescriptor.out) - : console == TerminalProvider.Stream.Error - ? new FileOutputStream(FileDescriptor.err) - : new FileOutputStream(getName()); - } - - @Override - public Attributes getAttr() throws IOException { - String cfg = doGetConfig(); - return doGetAttr(cfg); - } - - @Override - protected void doSetAttr(Attributes attr) throws IOException { - List commands = getFlagsToSet(attr, getAttr()); - if (!commands.isEmpty()) { - commands.add(0, OSUtils.STTY_COMMAND); - if (console == null) { - commands.add(1, OSUtils.STTY_F_OPTION); - commands.add(2, getName()); - } - exec(console != null, commands.toArray(new String[0])); - } - } - - protected List getFlagsToSet(Attributes attr, Attributes current) { - List commands = new ArrayList<>(); - for (InputFlag flag : InputFlag.values()) { - if (attr.getInputFlag(flag) != current.getInputFlag(flag)) { - commands.add((attr.getInputFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); - } - } - for (OutputFlag flag : OutputFlag.values()) { - if (attr.getOutputFlag(flag) != current.getOutputFlag(flag)) { - commands.add((attr.getOutputFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); - } - } - for (ControlFlag flag : ControlFlag.values()) { - if (attr.getControlFlag(flag) != current.getControlFlag(flag)) { - commands.add((attr.getControlFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); - } - } - for (LocalFlag flag : LocalFlag.values()) { - if (attr.getLocalFlag(flag) != current.getLocalFlag(flag)) { - commands.add((attr.getLocalFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); - } - } - String undef = System.getProperty("os.name").toLowerCase().startsWith("hp") ? "^-" : "undef"; - for (ControlChar cchar : ControlChar.values()) { - int v = attr.getControlChar(cchar); - if (v >= 0 && v != current.getControlChar(cchar)) { - String str = ""; - commands.add(cchar.name().toLowerCase().substring(1)); - if (cchar == ControlChar.VMIN || cchar == ControlChar.VTIME) { - commands.add(Integer.toString(v)); - } - else if (v == 0) { - commands.add(undef); - } - else { - if (v >= 128) { - v -= 128; - str += "M-"; - } - if (v < 32 || v == 127) { - v ^= 0x40; - str += "^"; - } - str += (char) v; - commands.add(str); - } - } - } - return commands; - } - - @Override - public Size getSize() throws IOException { - String cfg = doGetConfig(); - return doGetSize(cfg); - } - - protected String doGetConfig() throws IOException { - return console != null - ? exec(true, OSUtils.STTY_COMMAND, "-a") - : exec(false, OSUtils.STTY_COMMAND, OSUtils.STTY_F_OPTION, getName(), "-a"); - } - - static Attributes doGetAttr(String cfg) throws IOException { - Attributes attributes = new Attributes(); - for (InputFlag flag : InputFlag.values()) { - Boolean value = doGetFlag(cfg, flag); - if (value != null) { - attributes.setInputFlag(flag, value); - } - } - for (OutputFlag flag : OutputFlag.values()) { - Boolean value = doGetFlag(cfg, flag); - if (value != null) { - attributes.setOutputFlag(flag, value); - } - } - for (ControlFlag flag : ControlFlag.values()) { - Boolean value = doGetFlag(cfg, flag); - if (value != null) { - attributes.setControlFlag(flag, value); - } - } - for (LocalFlag flag : LocalFlag.values()) { - Boolean value = doGetFlag(cfg, flag); - if (value != null) { - attributes.setLocalFlag(flag, value); - } - } - for (ControlChar cchar : ControlChar.values()) { - String name = cchar.name().toLowerCase().substring(1); - if ("reprint".endsWith(name)) { - name = "(?:reprint|rprnt)"; - } - Matcher matcher = Pattern.compile("[\\s;]" + name + "\\s*=\\s*(.+?)[\\s;]").matcher(cfg); - if (matcher.find()) { - attributes.setControlChar(cchar, parseControlChar(matcher.group(1).toUpperCase())); - } - } - return attributes; - } - - private static Boolean doGetFlag(String cfg, Enum flag) { - Matcher matcher = Pattern.compile("(?:^|[\\s;])(\\-?" + flag.name().toLowerCase() + ")(?:[\\s;]|$)").matcher(cfg); - return matcher.find() ? !matcher.group(1).startsWith("-") : null; - } - - static int parseControlChar(String str) { - // undef - if ("".equals(str)) { - return -1; - } - // del - if ("DEL".equalsIgnoreCase(str)) { - return 127; - } - // octal - if (str.charAt(0) == '0') { - return Integer.parseInt(str, 8); - } - // decimal - if (str.charAt(0) >= '1' && str.charAt(0) <= '9') { - return Integer.parseInt(str, 10); - } - // control char - if (str.charAt(0) == '^') { - if (str.charAt(1) == '?') { - return 127; - } else { - return str.charAt(1) - 64; - } - } else if (str.charAt(0) == 'M' && str.charAt(1) == '-') { - if (str.charAt(2) == '^') { - if (str.charAt(3) == '?') { - return 127 + 128; - } else { - return str.charAt(3) - 64 + 128; - } - } else { - return str.charAt(2) + 128; - } - } else { - return str.charAt(0); - } - } - - static Size doGetSize(String cfg) throws IOException { - return new Size(doGetInt("columns", cfg), doGetInt("rows", cfg)); - } - - static int doGetInt(String name, String cfg) throws IOException { - String[] patterns = new String[] { - "\\b([0-9]+)\\s+" + name + "\\b", - "\\b" + name + "\\s+([0-9]+)\\b", - "\\b" + name + "\\s*=\\s*([0-9]+)\\b" - }; - for (String pattern : patterns) { - Matcher matcher = Pattern.compile(pattern).matcher(cfg); - if (matcher.find()) { - return Integer.parseInt(matcher.group(1)); - } - } - throw new IOException("Unable to parse " + name); - } - - @Override - public void setSize(Size size) throws IOException { - if (console != null) { - exec(true, - OSUtils.STTY_COMMAND, - "columns", Integer.toString(size.getColumns()), - "rows", Integer.toString(size.getRows())); - } else { - exec(false, - OSUtils.STTY_COMMAND, - OSUtils.STTY_F_OPTION, getName(), - "columns", Integer.toString(size.getColumns()), - "rows", Integer.toString(size.getRows())); - } - } - - @Override - public String toString() { - return "ExecPty[" + getName() + (console != null ? ", system]" : "]"); - } - -} diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExternalTerminal.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExternalTerminal.java index 2814b52c7ccd3..5033fd61279f2 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExternalTerminal.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExternalTerminal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,10 +8,6 @@ */ package jdk.internal.org.jline.terminal.impl; -import jdk.internal.org.jline.terminal.Attributes; -import jdk.internal.org.jline.terminal.Cursor; -import jdk.internal.org.jline.terminal.Size; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -19,6 +15,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.IntConsumer; +import jdk.internal.org.jline.terminal.Attributes; +import jdk.internal.org.jline.terminal.Cursor; +import jdk.internal.org.jline.terminal.Size; +import jdk.internal.org.jline.terminal.spi.TerminalProvider; + /** * Console implementation with embedded line disciplined. * @@ -32,45 +33,59 @@ */ public class ExternalTerminal extends LineDisciplineTerminal { + private final TerminalProvider provider; protected final AtomicBoolean closed = new AtomicBoolean(); protected final InputStream masterInput; protected final Object lock = new Object(); protected boolean paused = true; protected Thread pumpThread; - public ExternalTerminal(String name, String type, - InputStream masterInput, - OutputStream masterOutput, - Charset encoding) throws IOException { - this(name, type, masterInput, masterOutput, encoding, SignalHandler.SIG_DFL); + public ExternalTerminal( + String name, String type, InputStream masterInput, OutputStream masterOutput, Charset encoding) + throws IOException { + this(null, name, type, masterInput, masterOutput, encoding, SignalHandler.SIG_DFL); } - public ExternalTerminal(String name, String type, - InputStream masterInput, - OutputStream masterOutput, - Charset encoding, - SignalHandler signalHandler) throws IOException { - this(name, type, masterInput, masterOutput, encoding, signalHandler, false); + public ExternalTerminal( + TerminalProvider provider, + String name, + String type, + InputStream masterInput, + OutputStream masterOutput, + Charset encoding, + SignalHandler signalHandler) + throws IOException { + this(provider, name, type, masterInput, masterOutput, encoding, signalHandler, false); } - public ExternalTerminal(String name, String type, - InputStream masterInput, - OutputStream masterOutput, - Charset encoding, - SignalHandler signalHandler, - boolean paused) throws IOException { - this(name, type, masterInput, masterOutput, encoding, signalHandler, paused, null, null); + public ExternalTerminal( + TerminalProvider provider, + String name, + String type, + InputStream masterInput, + OutputStream masterOutput, + Charset encoding, + SignalHandler signalHandler, + boolean paused) + throws IOException { + this(provider, name, type, masterInput, masterOutput, encoding, signalHandler, paused, null, null); } - public ExternalTerminal(String name, String type, - InputStream masterInput, - OutputStream masterOutput, - Charset encoding, - SignalHandler signalHandler, - boolean paused, - Attributes attributes, - Size size) throws IOException { + @SuppressWarnings("this-escape") + public ExternalTerminal( + TerminalProvider provider, + String name, + String type, + InputStream masterInput, + OutputStream masterOutput, + Charset encoding, + SignalHandler signalHandler, + boolean paused, + Attributes attributes, + Size size) + throws IOException { super(name, type, masterOutput, encoding, signalHandler); + this.provider = provider; this.masterInput = masterInput; if (attributes != null) { setAttributes(attributes); @@ -171,4 +186,8 @@ public Cursor getCursorPosition(IntConsumer discarded) { return CursorSupport.getCursorPosition(this, discarded); } + @Override + public TerminalProvider getProvider() { + return provider; + } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/LineDisciplineTerminal.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/LineDisciplineTerminal.java index 0f290379bbf50..4e7a396bbc304 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/LineDisciplineTerminal.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/LineDisciplineTerminal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -14,6 +14,7 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.Charset; +import java.util.EnumSet; import java.util.Objects; import jdk.internal.org.jline.terminal.Attributes; @@ -23,6 +24,8 @@ import jdk.internal.org.jline.terminal.Attributes.OutputFlag; import jdk.internal.org.jline.terminal.Size; import jdk.internal.org.jline.terminal.Terminal; +import jdk.internal.org.jline.terminal.spi.SystemStream; +import jdk.internal.org.jline.terminal.spi.TerminalProvider; import jdk.internal.org.jline.utils.NonBlocking; import jdk.internal.org.jline.utils.NonBlockingPumpInputStream; import jdk.internal.org.jline.utils.NonBlockingReader; @@ -45,21 +48,6 @@ */ public class LineDisciplineTerminal extends AbstractTerminal { - private static final String DEFAULT_TERMINAL_ATTRIBUTES = - "speed 9600 baud; 24 rows; 80 columns;\n" + - "lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl\n" + - "\t-echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo\n" + - "\t-extproc\n" + - "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8\n" + - "\t-ignbrk brkint -inpck -ignpar -parmrk\n" + - "oflags: opost onlcr -oxtabs -onocr -onlret\n" + - "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow\n" + - "\t-dtrflow -mdmbuf\n" + - "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = ;\n" + - "\teol2 = ; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;\n" + - "\tmin = 1; quit = ^\\; reprint = ^R; start = ^Q; status = ^T;\n" + - "\tstop = ^S; susp = ^Z; time = 0; werase = ^W;\n"; - private static final int PIPE_SIZE = 1024; /* @@ -84,20 +72,20 @@ public class LineDisciplineTerminal extends AbstractTerminal { * Console data */ protected final Attributes attributes; + protected final Size size; - public LineDisciplineTerminal(String name, - String type, - OutputStream masterOutput, - Charset encoding) throws IOException { + protected boolean skipNextLf; + + public LineDisciplineTerminal(String name, String type, OutputStream masterOutput, Charset encoding) + throws IOException { this(name, type, masterOutput, encoding, SignalHandler.SIG_DFL); } - public LineDisciplineTerminal(String name, - String type, - OutputStream masterOutput, - Charset encoding, - SignalHandler signalHandler) throws IOException { + @SuppressWarnings("this-escape") + public LineDisciplineTerminal( + String name, String type, OutputStream masterOutput, Charset encoding, SignalHandler signalHandler) + throws IOException { super(name, type, encoding, signalHandler); NonBlockingPumpInputStream input = NonBlocking.nonBlockingPumpInputStream(PIPE_SIZE); this.slaveInputPipe = input.getOutputStream(); @@ -106,11 +94,66 @@ public LineDisciplineTerminal(String name, this.slaveOutput = new FilteringOutputStream(); this.slaveWriter = new PrintWriter(new OutputStreamWriter(slaveOutput, encoding())); this.masterOutput = masterOutput; - this.attributes = ExecPty.doGetAttr(DEFAULT_TERMINAL_ATTRIBUTES); + this.attributes = getDefaultTerminalAttributes(); this.size = new Size(160, 50); parseInfoCmp(); } + private static Attributes getDefaultTerminalAttributes() { + // speed 9600 baud; 24 rows; 80 columns; + // lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl + // -echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo + // -extproc + // iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8 + // -ignbrk brkint -inpck -ignpar -parmrk + // oflags: opost onlcr -oxtabs -onocr -onlret + // cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow + // -dtrflow -mdmbuf + // cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = ; + // eol2 = ; erase = ^?; intr = ^C; kill = ^U; lnext = ^V; + // min = 1; quit = ^\\; reprint = ^R; start = ^Q; status = ^T; + // stop = ^S; susp = ^Z; time = 0; werase = ^W; + Attributes attr = new Attributes(); + attr.setLocalFlags(EnumSet.of( + LocalFlag.ICANON, + LocalFlag.ISIG, + LocalFlag.IEXTEN, + LocalFlag.ECHO, + LocalFlag.ECHOE, + LocalFlag.ECHOKE, + LocalFlag.ECHOCTL, + LocalFlag.PENDIN)); + attr.setInputFlags(EnumSet.of( + InputFlag.ICRNL, + InputFlag.IXON, + InputFlag.IXANY, + InputFlag.IMAXBEL, + InputFlag.IUTF8, + InputFlag.BRKINT)); + attr.setOutputFlags(EnumSet.of(OutputFlag.OPOST, OutputFlag.ONLCR)); + attr.setControlChar(ControlChar.VDISCARD, ctrl('O')); + attr.setControlChar(ControlChar.VDSUSP, ctrl('Y')); + attr.setControlChar(ControlChar.VEOF, ctrl('D')); + attr.setControlChar(ControlChar.VERASE, ctrl('?')); + attr.setControlChar(ControlChar.VINTR, ctrl('C')); + attr.setControlChar(ControlChar.VKILL, ctrl('U')); + attr.setControlChar(ControlChar.VLNEXT, ctrl('V')); + attr.setControlChar(ControlChar.VMIN, 1); + attr.setControlChar(ControlChar.VQUIT, ctrl('\\')); + attr.setControlChar(ControlChar.VREPRINT, ctrl('R')); + attr.setControlChar(ControlChar.VSTART, ctrl('Q')); + attr.setControlChar(ControlChar.VSTATUS, ctrl('T')); + attr.setControlChar(ControlChar.VSTOP, ctrl('S')); + attr.setControlChar(ControlChar.VSUSP, ctrl('Z')); + attr.setControlChar(ControlChar.VTIME, 0); + attr.setControlChar(ControlChar.VWERASE, ctrl('W')); + return attr; + } + + private static int ctrl(char c) { + return c == '?' ? 177 : c - 64; + } + public NonBlockingReader reader() { return slaveReader; } @@ -130,9 +173,7 @@ public OutputStream output() { } public Attributes getAttributes() { - Attributes attr = new Attributes(); - attr.copy(attributes); - return attr; + return new Attributes(attributes); } public void setAttributes(Attributes attr) { @@ -149,9 +190,9 @@ public void setSize(Size sz) { size.copy(sz); } - @Override + @Override public void raise(Signal signal) { - Objects.requireNonNull(signal); + Objects.requireNonNull(signal); // Do not call clear() atm as this can cause // deadlock between reading / writing threads // TODO: any way to fix that ? @@ -214,7 +255,19 @@ protected boolean doProcessInputByte(int c) throws IOException { raise(Signal.INFO); } } - if (c == '\r') { + if (attributes.getInputFlag(InputFlag.INORMEOL)) { + if (c == '\r') { + skipNextLf = true; + c = '\n'; + } else if (c == '\n') { + if (skipNextLf) { + skipNextLf = false; + return false; + } + } else { + skipNextLf = false; + } + } else if (c == '\r') { if (attributes.getInputFlag(InputFlag.IGNCR)) { return false; } @@ -273,6 +326,16 @@ protected void doClose() throws IOException { } } + @Override + public TerminalProvider getProvider() { + return null; + } + + @Override + public SystemStream getSystemStream() { + return null; + } + private class FilteringOutputStream extends OutputStream { @Override public void write(int b) throws IOException { @@ -284,13 +347,12 @@ public void write(int b) throws IOException { public void write(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); - } else if ((off < 0) || (off > b.length) || (len < 0) || - ((off + len) > b.length) || ((off + len) < 0)) { + } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } - for (int i = 0 ; i < len ; i++) { + for (int i = 0; i < len; i++) { processOutputByte(b[off + i]); } flush(); diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/MouseSupport.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/MouseSupport.java index 89ae6e5b74a57..eef25c90d8f1c 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/MouseSupport.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/MouseSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,11 +8,6 @@ */ package jdk.internal.org.jline.terminal.impl; -import jdk.internal.org.jline.terminal.MouseEvent; -import jdk.internal.org.jline.terminal.Terminal; -import jdk.internal.org.jline.utils.InfoCmp; -import jdk.internal.org.jline.utils.InputStreamReader; - import java.io.EOFException; import java.io.IOError; import java.io.IOException; @@ -20,6 +15,11 @@ import java.util.EnumSet; import java.util.function.IntSupplier; +import jdk.internal.org.jline.terminal.MouseEvent; +import jdk.internal.org.jline.terminal.Terminal; +import jdk.internal.org.jline.utils.InfoCmp; +import jdk.internal.org.jline.utils.InputStreamReader; + public class MouseSupport { public static boolean hasMouseSupport(Terminal terminal) { @@ -78,7 +78,8 @@ public static MouseEvent readMouse(IntSupplier reader, MouseEvent last) { case 0: button = MouseEvent.Button.Button1; if (last.getButton() == button - && (last.getType() == MouseEvent.Type.Pressed || last.getType() == MouseEvent.Type.Dragged)) { + && (last.getType() == MouseEvent.Type.Pressed + || last.getType() == MouseEvent.Type.Dragged)) { type = MouseEvent.Type.Dragged; } else { type = MouseEvent.Type.Pressed; @@ -87,7 +88,8 @@ public static MouseEvent readMouse(IntSupplier reader, MouseEvent last) { case 1: button = MouseEvent.Button.Button2; if (last.getButton() == button - && (last.getType() == MouseEvent.Type.Pressed || last.getType() == MouseEvent.Type.Dragged)) { + && (last.getType() == MouseEvent.Type.Pressed + || last.getType() == MouseEvent.Type.Dragged)) { type = MouseEvent.Type.Dragged; } else { type = MouseEvent.Type.Pressed; @@ -96,7 +98,8 @@ public static MouseEvent readMouse(IntSupplier reader, MouseEvent last) { case 2: button = MouseEvent.Button.Button3; if (last.getButton() == button - && (last.getType() == MouseEvent.Type.Pressed || last.getType() == MouseEvent.Type.Dragged)) { + && (last.getType() == MouseEvent.Type.Pressed + || last.getType() == MouseEvent.Type.Dragged)) { type = MouseEvent.Type.Dragged; } else { type = MouseEvent.Type.Pressed; @@ -134,5 +137,4 @@ private static int readExt(Terminal terminal) { throw new IOError(e); } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/NativeSignalHandler.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/NativeSignalHandler.java index 00c584c6cb931..bba177794a5e0 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/NativeSignalHandler.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/NativeSignalHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -17,8 +17,7 @@ public final class NativeSignalHandler implements SignalHandler { public static final NativeSignalHandler SIG_IGN = new NativeSignalHandler(); - private NativeSignalHandler() { - } + private NativeSignalHandler() {} public void handle(Signal signal) { throw new UnsupportedOperationException(); diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixPtyTerminal.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixPtyTerminal.java index f85e3fe0b0c8b..d34103e4d5e1d 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixPtyTerminal.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixPtyTerminal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -38,15 +38,34 @@ public class PosixPtyTerminal extends AbstractPosixTerminal { private Thread outputPumpThread; private boolean paused = true; - public PosixPtyTerminal(String name, String type, Pty pty, InputStream in, OutputStream out, Charset encoding) throws IOException { + public PosixPtyTerminal(String name, String type, Pty pty, InputStream in, OutputStream out, Charset encoding) + throws IOException { this(name, type, pty, in, out, encoding, SignalHandler.SIG_DFL); } - public PosixPtyTerminal(String name, String type, Pty pty, InputStream in, OutputStream out, Charset encoding, SignalHandler signalHandler) throws IOException { + public PosixPtyTerminal( + String name, + String type, + Pty pty, + InputStream in, + OutputStream out, + Charset encoding, + SignalHandler signalHandler) + throws IOException { this(name, type, pty, in, out, encoding, signalHandler, false); } - public PosixPtyTerminal(String name, String type, Pty pty, InputStream in, OutputStream out, Charset encoding, SignalHandler signalHandler, boolean paused) throws IOException { + @SuppressWarnings("this-escape") + public PosixPtyTerminal( + String name, + String type, + Pty pty, + InputStream in, + OutputStream out, + Charset encoding, + SignalHandler signalHandler, + boolean paused) + throws IOException { super(name, type, pty, encoding, signalHandler); this.in = Objects.requireNonNull(in); this.out = Objects.requireNonNull(out); @@ -113,7 +132,7 @@ public void pause(boolean wait) throws InterruptedException { if (p1 != null) { p1.join(); } - if (p2 !=null) { + if (p2 != null) { p2.join(); } } @@ -167,7 +186,7 @@ public void close() throws IOException { private void pumpIn() { try { - for (;;) { + for (; ; ) { synchronized (lock) { if (paused) { inputPumpThread = null; @@ -193,7 +212,7 @@ private void pumpIn() { private void pumpOut() { try { - for (;;) { + for (; ; ) { synchronized (lock) { if (paused) { outputPumpThread = null; @@ -221,5 +240,4 @@ private void pumpOut() { // Ignore } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixSysTerminal.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixSysTerminal.java index a55f3d4a748c7..135ed0bb0c04b 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixSysTerminal.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixSysTerminal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -18,8 +18,9 @@ import java.util.Map; import java.util.function.Function; -import jdk.internal.org.jline.utils.NonBlocking; import jdk.internal.org.jline.terminal.spi.Pty; +import jdk.internal.org.jline.utils.FastBufferedOutputStream; +import jdk.internal.org.jline.utils.NonBlocking; import jdk.internal.org.jline.utils.NonBlockingInputStream; import jdk.internal.org.jline.utils.NonBlockingReader; import jdk.internal.org.jline.utils.ShutdownHooks; @@ -35,12 +36,14 @@ public class PosixSysTerminal extends AbstractPosixTerminal { protected final Map nativeHandlers = new HashMap<>(); protected final Task closer; - public PosixSysTerminal(String name, String type, Pty pty, Charset encoding, - boolean nativeSignals, SignalHandler signalHandler, - Function inputStreamWrapper) throws IOException { + @SuppressWarnings("this-escape") + public PosixSysTerminal( + String name, String type, Pty pty, Charset encoding, boolean nativeSignals, SignalHandler signalHandler, + Function inputStreamWrapper) + throws IOException { super(name, type, pty, encoding, signalHandler); this.input = NonBlocking.nonBlocking(getName(), inputStreamWrapper.apply(pty.getSlaveInput())); - this.output = pty.getSlaveOutput(); + this.output = new FastBufferedOutputStream(pty.getSlaveOutput()); this.reader = NonBlocking.nonBlocking(getName(), input, encoding()); this.writer = new PrintWriter(new OutputStreamWriter(output, encoding())); parseInfoCmp(); @@ -98,5 +101,4 @@ protected void doClose() throws IOException { // Do not call reader.close() reader.shutdown(); } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/exec/ExecPty.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/exec/ExecPty.java new file mode 100644 index 0000000000000..c0a35cbd6a219 --- /dev/null +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/exec/ExecPty.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2002-2016, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package jdk.internal.org.jline.terminal.impl.exec; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.internal.org.jline.terminal.Attributes; +import jdk.internal.org.jline.terminal.Attributes.ControlChar; +import jdk.internal.org.jline.terminal.Attributes.ControlFlag; +import jdk.internal.org.jline.terminal.Attributes.InputFlag; +import jdk.internal.org.jline.terminal.Attributes.LocalFlag; +import jdk.internal.org.jline.terminal.Attributes.OutputFlag; +import jdk.internal.org.jline.terminal.Size; +import jdk.internal.org.jline.terminal.impl.AbstractPty; +import jdk.internal.org.jline.terminal.spi.Pty; +import jdk.internal.org.jline.terminal.spi.SystemStream; +import jdk.internal.org.jline.terminal.spi.TerminalProvider; +import jdk.internal.org.jline.utils.OSUtils; + +import static jdk.internal.org.jline.utils.ExecHelper.exec; + +public class ExecPty extends AbstractPty implements Pty { + + private final String name; + + public static Pty current(TerminalProvider provider, SystemStream systemStream) throws IOException { + try { + String result = exec(true, OSUtils.TTY_COMMAND); + if (systemStream != SystemStream.Output && systemStream != SystemStream.Error) { + throw new IllegalArgumentException("systemStream should be Output or Error: " + systemStream); + } + return new ExecPty(provider, systemStream, result.trim()); + } catch (IOException e) { + throw new IOException("Not a tty", e); + } + } + + protected ExecPty(TerminalProvider provider, SystemStream systemStream, String name) { + super(provider, systemStream); + this.name = name; + } + + @Override + public void close() throws IOException {} + + public String getName() { + return name; + } + + @Override + public InputStream getMasterInput() { + throw new UnsupportedOperationException(); + } + + @Override + public OutputStream getMasterOutput() { + throw new UnsupportedOperationException(); + } + + @Override + protected InputStream doGetSlaveInput() throws IOException { + return systemStream != null ? new FileInputStream(FileDescriptor.in) : new FileInputStream(getName()); + } + + @Override + public OutputStream getSlaveOutput() throws IOException { + return systemStream == SystemStream.Output + ? new FileOutputStream(FileDescriptor.out) + : systemStream == SystemStream.Error + ? new FileOutputStream(FileDescriptor.err) + : new FileOutputStream(getName()); + } + + @Override + public Attributes getAttr() throws IOException { + String cfg = doGetConfig(); + return doGetAttr(cfg); + } + + @Override + protected void doSetAttr(Attributes attr) throws IOException { + List commands = getFlagsToSet(attr, getAttr()); + if (!commands.isEmpty()) { + commands.add(0, OSUtils.STTY_COMMAND); + if (systemStream == null) { + commands.add(1, OSUtils.STTY_F_OPTION); + commands.add(2, getName()); + } + try { + exec(systemStream != null, commands.toArray(new String[0])); + } catch (IOException e) { + // Handle partial failures with GNU stty, see #97 + if (e.toString().contains("unable to perform all requested operations")) { + commands = getFlagsToSet(attr, getAttr()); + if (!commands.isEmpty()) { + throw new IOException("Could not set the following flags: " + String.join(", ", commands), e); + } + } else { + throw e; + } + } + } + } + + protected List getFlagsToSet(Attributes attr, Attributes current) { + List commands = new ArrayList<>(); + for (InputFlag flag : InputFlag.values()) { + if (attr.getInputFlag(flag) != current.getInputFlag(flag) && flag != InputFlag.INORMEOL) { + commands.add((attr.getInputFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); + } + } + for (OutputFlag flag : OutputFlag.values()) { + if (attr.getOutputFlag(flag) != current.getOutputFlag(flag)) { + commands.add((attr.getOutputFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); + } + } + for (ControlFlag flag : ControlFlag.values()) { + if (attr.getControlFlag(flag) != current.getControlFlag(flag)) { + commands.add((attr.getControlFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); + } + } + for (LocalFlag flag : LocalFlag.values()) { + if (attr.getLocalFlag(flag) != current.getLocalFlag(flag)) { + commands.add((attr.getLocalFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase()); + } + } + String undef = System.getProperty("os.name").toLowerCase().startsWith("hp") ? "^-" : "undef"; + for (ControlChar cchar : ControlChar.values()) { + int v = attr.getControlChar(cchar); + if (v >= 0 && v != current.getControlChar(cchar)) { + String str = ""; + commands.add(cchar.name().toLowerCase().substring(1)); + if (cchar == ControlChar.VMIN || cchar == ControlChar.VTIME) { + commands.add(Integer.toString(v)); + } else if (v == 0) { + commands.add(undef); + } else { + if (v >= 128) { + v -= 128; + str += "M-"; + } + if (v < 32 || v == 127) { + v ^= 0x40; + str += "^"; + } + str += (char) v; + commands.add(str); + } + } + } + return commands; + } + + @Override + public Size getSize() throws IOException { + String cfg = doGetConfig(); + return doGetSize(cfg); + } + + protected String doGetConfig() throws IOException { + return systemStream != null + ? exec(true, OSUtils.STTY_COMMAND, "-a") + : exec(false, OSUtils.STTY_COMMAND, OSUtils.STTY_F_OPTION, getName(), "-a"); + } + + public static Attributes doGetAttr(String cfg) throws IOException { + Attributes attributes = new Attributes(); + for (InputFlag flag : InputFlag.values()) { + Boolean value = doGetFlag(cfg, flag); + if (value != null) { + attributes.setInputFlag(flag, value); + } + } + for (OutputFlag flag : OutputFlag.values()) { + Boolean value = doGetFlag(cfg, flag); + if (value != null) { + attributes.setOutputFlag(flag, value); + } + } + for (ControlFlag flag : ControlFlag.values()) { + Boolean value = doGetFlag(cfg, flag); + if (value != null) { + attributes.setControlFlag(flag, value); + } + } + for (LocalFlag flag : LocalFlag.values()) { + Boolean value = doGetFlag(cfg, flag); + if (value != null) { + attributes.setLocalFlag(flag, value); + } + } + for (ControlChar cchar : ControlChar.values()) { + String name = cchar.name().toLowerCase().substring(1); + if ("reprint".endsWith(name)) { + name = "(?:reprint|rprnt)"; + } + Matcher matcher = + Pattern.compile("[\\s;]" + name + "\\s*=\\s*(.+?)[\\s;]").matcher(cfg); + if (matcher.find()) { + attributes.setControlChar( + cchar, parseControlChar(matcher.group(1).toUpperCase())); + } + } + return attributes; + } + + private static Boolean doGetFlag(String cfg, Enum flag) { + Matcher matcher = Pattern.compile("(?:^|[\\s;])(\\-?" + flag.name().toLowerCase() + ")(?:[\\s;]|$)") + .matcher(cfg); + return matcher.find() ? !matcher.group(1).startsWith("-") : null; + } + + static int parseControlChar(String str) { + // undef + if ("".equals(str)) { + return -1; + } + // del + if ("DEL".equalsIgnoreCase(str)) { + return 127; + } + // octal + if (str.charAt(0) == '0') { + return Integer.parseInt(str, 8); + } + // decimal + if (str.charAt(0) >= '1' && str.charAt(0) <= '9') { + return Integer.parseInt(str, 10); + } + // control char + if (str.charAt(0) == '^') { + if (str.charAt(1) == '?') { + return 127; + } else { + return str.charAt(1) - 64; + } + } else if (str.charAt(0) == 'M' && str.charAt(1) == '-') { + if (str.charAt(2) == '^') { + if (str.charAt(3) == '?') { + return 127 + 128; + } else { + return str.charAt(3) - 64 + 128; + } + } else { + return str.charAt(2) + 128; + } + } else { + return str.charAt(0); + } + } + + static Size doGetSize(String cfg) throws IOException { + return new Size(doGetInt("columns", cfg), doGetInt("rows", cfg)); + } + + static int doGetInt(String name, String cfg) throws IOException { + String[] patterns = new String[] { + "\\b([0-9]+)\\s+" + name + "\\b", "\\b" + name + "\\s+([0-9]+)\\b", "\\b" + name + "\\s*=\\s*([0-9]+)\\b" + }; + for (String pattern : patterns) { + Matcher matcher = Pattern.compile(pattern).matcher(cfg); + if (matcher.find()) { + return Integer.parseInt(matcher.group(1)); + } + } + throw new IOException("Unable to parse " + name); + } + + @Override + public void setSize(Size size) throws IOException { + if (systemStream != null) { + exec( + true, + OSUtils.STTY_COMMAND, + "columns", + Integer.toString(size.getColumns()), + "rows", + Integer.toString(size.getRows())); + } else { + exec( + false, + OSUtils.STTY_COMMAND, + OSUtils.STTY_F_OPTION, + getName(), + "columns", + Integer.toString(size.getColumns()), + "rows", + Integer.toString(size.getRows())); + } + } + + @Override + public String toString() { + return "ExecPty[" + getName() + (systemStream != null ? ", system]" : "]"); + } +} diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/exec/ExecTerminalProvider.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/exec/ExecTerminalProvider.java index 8bdeede2e1b3a..36ff4a63579cb 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/exec/ExecTerminalProvider.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/exec/ExecTerminalProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, the original author or authors. + * Copyright (c) 2022, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -17,117 +17,249 @@ import java.nio.charset.Charset; import java.util.function.Function; +//import jdk.internal.org.jline.nativ.JLineLibrary; +//import jdk.internal.org.jline.nativ.JLineNativeLoader; import jdk.internal.org.jline.terminal.Attributes; import jdk.internal.org.jline.terminal.Size; import jdk.internal.org.jline.terminal.Terminal; -import jdk.internal.org.jline.terminal.impl.ExecPty; +import jdk.internal.org.jline.terminal.TerminalBuilder; import jdk.internal.org.jline.terminal.impl.ExternalTerminal; import jdk.internal.org.jline.terminal.impl.PosixSysTerminal; import jdk.internal.org.jline.terminal.spi.Pty; +import jdk.internal.org.jline.terminal.spi.SystemStream; import jdk.internal.org.jline.terminal.spi.TerminalProvider; import jdk.internal.org.jline.utils.ExecHelper; +import jdk.internal.org.jline.utils.Log; import jdk.internal.org.jline.utils.OSUtils; -public class ExecTerminalProvider implements TerminalProvider -{ +import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_REDIRECT_PIPE_CREATION_MODE; +import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_REDIRECT_PIPE_CREATION_MODE_DEFAULT; +import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_REDIRECT_PIPE_CREATION_MODE_NATIVE; +import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_REDIRECT_PIPE_CREATION_MODE_REFLECTION; + +public class ExecTerminalProvider implements TerminalProvider { + + private static boolean warned; public String name() { - return "exec"; + return TerminalBuilder.PROP_PROVIDER_EXEC; } - public Pty current(Stream consoleStream) throws IOException { - return ExecPty.current(consoleStream); + public Pty current(SystemStream systemStream) throws IOException { + return ExecPty.current(this, systemStream); } @Override - public Terminal sysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, - boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused, - Stream consoleStream, Function inputStreamWrapper) throws IOException { + public Terminal sysTerminal( + String name, + String type, + boolean ansiPassThrough, + Charset encoding, + boolean nativeSignals, + Terminal.SignalHandler signalHandler, + boolean paused, + SystemStream systemStream, + Function inputStreamWrapper) + throws IOException { if (OSUtils.IS_WINDOWS) { - return winSysTerminal(name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, consoleStream, inputStreamWrapper ); + return winSysTerminal( + name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, systemStream, inputStreamWrapper); } else { - return posixSysTerminal(name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, consoleStream, inputStreamWrapper ); + return posixSysTerminal( + name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, systemStream, inputStreamWrapper); } } - public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, - boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused, - Stream consoleStream, Function inputStreamWrapper ) throws IOException { + public Terminal winSysTerminal( + String name, + String type, + boolean ansiPassThrough, + Charset encoding, + boolean nativeSignals, + Terminal.SignalHandler signalHandler, + boolean paused, + SystemStream systemStream, + Function inputStreamWrapper) + throws IOException { if (OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) { - Pty pty = current(consoleStream); + Pty pty = current(systemStream); return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler, inputStreamWrapper); } else { return null; } } - public Terminal posixSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, - boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused, - Stream consoleStream, Function inputStreamWrapper) throws IOException { - Pty pty = current(consoleStream); + public Terminal posixSysTerminal( + String name, + String type, + boolean ansiPassThrough, + Charset encoding, + boolean nativeSignals, + Terminal.SignalHandler signalHandler, + boolean paused, + SystemStream systemStream, + Function inputStreamWrapper) + throws IOException { + Pty pty = current(systemStream); return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler, inputStreamWrapper); } @Override - public Terminal newTerminal(String name, String type, InputStream in, OutputStream out, - Charset encoding, Terminal.SignalHandler signalHandler, boolean paused, - Attributes attributes, Size size) throws IOException - { - return new ExternalTerminal(name, type, in, out, encoding, signalHandler, paused, attributes, size); + public Terminal newTerminal( + String name, + String type, + InputStream in, + OutputStream out, + Charset encoding, + Terminal.SignalHandler signalHandler, + boolean paused, + Attributes attributes, + Size size) + throws IOException { + return new ExternalTerminal(this, name, type, in, out, encoding, signalHandler, paused, attributes, size); } @Override - public boolean isSystemStream(Stream stream) { + public boolean isSystemStream(SystemStream stream) { try { - return isWindowsSystemStream(stream) || isPosixSystemStream(stream); + return isPosixSystemStream(stream) || isWindowsSystemStream(stream); } catch (Throwable t) { return false; } } - public boolean isWindowsSystemStream(Stream stream) { - return systemStreamName( stream ) != null; + public boolean isWindowsSystemStream(SystemStream stream) { + return systemStreamName(stream) != null; } - public boolean isPosixSystemStream(Stream stream) { + public boolean isPosixSystemStream(SystemStream stream) { try { Process p = new ProcessBuilder(OSUtils.TEST_COMMAND, "-t", Integer.toString(stream.ordinal())) - .inheritIO().start(); + .inheritIO() + .start(); return p.waitFor() == 0; } catch (Throwable t) { + Log.debug("ExecTerminalProvider failed 'test -t' for " + stream, t); // ignore } return false; } @Override - public String systemStreamName(Stream stream) { + public String systemStreamName(SystemStream stream) { try { - ProcessBuilder.Redirect input = stream == Stream.Input - ? ProcessBuilder.Redirect.INHERIT - : getRedirect(stream == Stream.Output ? FileDescriptor.out : FileDescriptor.err); - Process p = new ProcessBuilder(OSUtils.TTY_COMMAND).redirectInput(input).start(); + ProcessBuilder.Redirect input = stream == SystemStream.Input + ? ProcessBuilder.Redirect.INHERIT + : newDescriptor(stream == SystemStream.Output ? FileDescriptor.out : FileDescriptor.err); + Process p = + new ProcessBuilder(OSUtils.TTY_COMMAND).redirectInput(input).start(); String result = ExecHelper.waitAndCapture(p); if (p.exitValue() == 0) { return result.trim(); } } catch (Throwable t) { + if ("java.lang.reflect.InaccessibleObjectException" + .equals(t.getClass().getName()) + && !warned) { + Log.warn( + "The ExecTerminalProvider requires the JVM options: '--add-opens java.base/java.lang=ALL-UNNAMED'"); + warned = true; + } // ignore } return null; } - private ProcessBuilder.Redirect getRedirect(FileDescriptor fd) throws ReflectiveOperationException { - // This is not really allowed, but this is the only way to redirect the output or error stream - // to the input. This is definitely not something you'd usually want to do, but in the case of - // the `tty` utility, it provides a way to get - Class rpi = Class.forName("java.lang.ProcessBuilder$RedirectPipeImpl"); - Constructor cns = rpi.getDeclaredConstructor(); - cns.setAccessible(true); - ProcessBuilder.Redirect input = (ProcessBuilder.Redirect) cns.newInstance(); - Field f = rpi.getDeclaredField("fd"); - f.setAccessible(true); - f.set(input, fd); - return input; +// @Override +// public int systemStreamWidth(SystemStream stream) { +// try (ExecPty pty = new ExecPty(this, stream, null)) { +// return pty.getSize().getColumns(); +// } catch (Throwable t) { +// return -1; +// } +// } + + private static RedirectPipeCreator redirectPipeCreator; + + protected static ProcessBuilder.Redirect newDescriptor(FileDescriptor fd) { + if (redirectPipeCreator == null) { + String str = System.getProperty(PROP_REDIRECT_PIPE_CREATION_MODE, PROP_REDIRECT_PIPE_CREATION_MODE_DEFAULT); + String[] modes = str.split(","); + IllegalStateException ise = new IllegalStateException("Unable to create RedirectPipe"); + for (String mode : modes) { + try { + switch (mode) { + case PROP_REDIRECT_PIPE_CREATION_MODE_NATIVE: + redirectPipeCreator = null;//new NativeRedirectPipeCreator(); + break; + case PROP_REDIRECT_PIPE_CREATION_MODE_REFLECTION: + redirectPipeCreator = new ReflectionRedirectPipeCreator(); + break; + } + } catch (Throwable t) { + // ignore + ise.addSuppressed(t); + } + if (redirectPipeCreator != null) { + break; + } + } + if (redirectPipeCreator == null) { + throw ise; + } + } + return redirectPipeCreator.newRedirectPipe(fd); + } + + interface RedirectPipeCreator { + ProcessBuilder.Redirect newRedirectPipe(FileDescriptor fd); + } + + /** + * Reflection based file descriptor creator. + * This requires the following option + * --add-opens java.base/java.lang=ALL-UNNAMED + */ + static class ReflectionRedirectPipeCreator implements RedirectPipeCreator { + private final Constructor constructor; + private final Field fdField; + + @SuppressWarnings("unchecked") + ReflectionRedirectPipeCreator() throws Exception { + Class rpi = Class.forName("java.lang.ProcessBuilder$RedirectPipeImpl"); + constructor = (Constructor) rpi.getDeclaredConstructor(); + constructor.setAccessible(true); + fdField = rpi.getDeclaredField("fd"); + fdField.setAccessible(true); + } + + @Override + public ProcessBuilder.Redirect newRedirectPipe(FileDescriptor fd) { + try { + ProcessBuilder.Redirect input = constructor.newInstance(); + fdField.set(input, fd); + return input; + } catch (ReflectiveOperationException e) { + // This should not happen as the field has been set accessible + throw new IllegalStateException(e); + } + } + } + +// static class NativeRedirectPipeCreator implements RedirectPipeCreator { +// public NativeRedirectPipeCreator() { +// // Force load the library +// JLineNativeLoader.initialize(); +// } +// +// @Override +// public ProcessBuilder.Redirect newRedirectPipe(FileDescriptor fd) { +// return JLineLibrary.newRedirectPipe(fd); +// } +// } + + @Override + public String toString() { + return "TerminalProvider[" + name() + "]"; } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/Pty.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/Pty.java index b1e22056702ca..d72e0e669d102 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/Pty.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/Pty.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -34,4 +34,7 @@ public interface Pty extends Closeable { void setSize(Size size) throws IOException; + SystemStream getSystemStream(); + + TerminalProvider getProvider(); } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/SystemStream.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/SystemStream.java new file mode 100644 index 0000000000000..5f95024829556 --- /dev/null +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/SystemStream.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package jdk.internal.org.jline.terminal.spi; + +public enum SystemStream { + Input, + Output, + Error +} diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/TerminalExt.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/TerminalExt.java new file mode 100644 index 0000000000000..00cfb2e9d475e --- /dev/null +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/TerminalExt.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package jdk.internal.org.jline.terminal.spi; + +import jdk.internal.org.jline.terminal.Terminal; + +/** + * The {@code TerminalExt} interface is implemented by {@code Terminal}s + * and provides access to the Terminal's internals. + */ +public interface TerminalExt extends Terminal { + + /** + * Returns the {@code TerminalProvider} that created this terminal + * or {@code null} if the terminal was created with no provider. + */ + TerminalProvider getProvider(); + + /** + * The underlying system stream, may be {@link SystemStream#Output}, + * {@link SystemStream#Error}, or {@code null} if this terminal is not bound + * to a system stream. + */ + SystemStream getSystemStream(); +} diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/TerminalProvider.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/TerminalProvider.java index 69f353c571905..272df6fedac6c 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/TerminalProvider.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/TerminalProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, the original author or authors. + * Copyright (c) 2022, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -11,10 +11,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.URL; import java.nio.charset.Charset; import java.util.Properties; -import java.util.ServiceLoader; import java.util.function.Function; import jdk.internal.org.jline.terminal.Attributes; @@ -22,30 +20,39 @@ import jdk.internal.org.jline.terminal.Terminal; import jdk.internal.org.jline.terminal.impl.exec.ExecTerminalProvider; -public interface TerminalProvider -{ - - enum Stream { - Input, - Output, - Error - } +public interface TerminalProvider { String name(); - Terminal sysTerminal(String name, String type, boolean ansiPassThrough, - Charset encoding, boolean nativeSignals, - Terminal.SignalHandler signalHandler, boolean paused, - Stream consoleStream, Function inputStreamWrapper) throws IOException; + Terminal sysTerminal( + String name, + String type, + boolean ansiPassThrough, + Charset encoding, + boolean nativeSignals, + Terminal.SignalHandler signalHandler, + boolean paused, + SystemStream systemStream, + Function inputStreamWrapper) + throws IOException; - Terminal newTerminal(String name, String type, - InputStream masterInput, OutputStream masterOutput, - Charset encoding, Terminal.SignalHandler signalHandler, - boolean paused, Attributes attributes, Size size) throws IOException; + Terminal newTerminal( + String name, + String type, + InputStream masterInput, + OutputStream masterOutput, + Charset encoding, + Terminal.SignalHandler signalHandler, + boolean paused, + Attributes attributes, + Size size) + throws IOException; - boolean isSystemStream(Stream stream); + boolean isSystemStream(SystemStream stream); - String systemStreamName(Stream stream); + String systemStreamName(SystemStream stream); + + //int systemStreamWidth(SystemStream stream); static TerminalProvider load(String name) throws IOException { switch (name) { @@ -62,7 +69,7 @@ static TerminalProvider load(String name) throws IOException { if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } - InputStream is = cl.getResourceAsStream( "META-INF/services/org/jline/terminal/provider/" + name); + InputStream is = cl.getResourceAsStream("META-INF/services/org/jline/terminal/provider/" + name); if (is != null) { Properties props = new Properties(); try { @@ -71,14 +78,13 @@ static TerminalProvider load(String name) throws IOException { if (className == null) { throw new IOException("No class defined in terminal provider file " + name); } - Class clazz = cl.loadClass( className ); + Class clazz = cl.loadClass(className); return (TerminalProvider) clazz.getConstructor().newInstance(); - } catch ( Exception e ) { - throw new IOException("Unable to load terminal provider " + name, e); + } catch (Exception e) { + throw new IOException("Unable to load terminal provider " + name + ": " + e.getMessage(), e); } } else { throw new IOException("Unable to find terminal provider " + name); } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AnsiWriter.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AnsiWriter.java index 958d0ec74e94e..302406dde89d9 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AnsiWriter.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AnsiWriter.java @@ -1,17 +1,10 @@ /* - * Copyright (C) 2009-2018 the original author(s). + * Copyright (c) 2009-2018, the original author(s). * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * https://opensource.org/licenses/BSD-3-Clause */ package jdk.internal.org.jline.utils; @@ -252,12 +245,10 @@ private void reset(boolean skipBuffer) throws IOException { * @throws IOException if no more non-null values left */ private int getNextOptionInt(Iterator optionsIterator) throws IOException { - for (;;) { - if (!optionsIterator.hasNext()) - throw new IllegalArgumentException(); + for (; ; ) { + if (!optionsIterator.hasNext()) throw new IllegalArgumentException(); Object arg = optionsIterator.next(); - if (arg != null) - return (Integer) arg; + if (arg != null) return (Integer) arg; } } @@ -346,27 +337,21 @@ private boolean processEscapeCommand(ArrayList options, int command) thr int g = getNextOptionInt(optionsIterator); int b = getNextOptionInt(optionsIterator); if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) { - if (value == 38) - processSetForegroundColorExt(r, g, b); - else - processSetBackgroundColorExt(r, g, b); + if (value == 38) processSetForegroundColorExt(r, g, b); + else processSetBackgroundColorExt(r, g, b); } else { throw new IllegalArgumentException(); } - } - else if (arg2or5 == 5) { + } else if (arg2or5 == 5) { // 256 color style like `esc[38;5;m` int paletteIndex = getNextOptionInt(optionsIterator); if (paletteIndex >= 0 && paletteIndex <= 255) { - if (value == 38) - processSetForegroundColorExt(paletteIndex); - else - processSetBackgroundColorExt(paletteIndex); + if (value == 38) processSetForegroundColorExt(paletteIndex); + else processSetBackgroundColorExt(paletteIndex); } else { throw new IllegalArgumentException(); } - } - else { + } else { throw new IllegalArgumentException(); } } else { @@ -449,47 +434,41 @@ private boolean processOperatingSystemCommand(ArrayList options) throws * Process CSI u ANSI code, corresponding to RCP \u2013 Restore Cursor Position * @throws IOException if an error occurs */ - protected void processRestoreCursorPosition() throws IOException { - } + protected void processRestoreCursorPosition() throws IOException {} /** * Process CSI s ANSI code, corresponding to SCP \u2013 Save Cursor Position * @throws IOException if an error occurs */ - protected void processSaveCursorPosition() throws IOException { - } + protected void processSaveCursorPosition() throws IOException {} /** * Process CSI s ANSI code, corresponding to IL \u2013 Insert Line * @param optionInt the option * @throws IOException if an error occurs */ - protected void processInsertLine(int optionInt) throws IOException { - } + protected void processInsertLine(int optionInt) throws IOException {} /** * Process CSI s ANSI code, corresponding to DL \u2013 Delete Line * @param optionInt the option * @throws IOException if an error occurs */ - protected void processDeleteLine(int optionInt) throws IOException { - } + protected void processDeleteLine(int optionInt) throws IOException {} /** * Process CSI n T ANSI code, corresponding to SD \u2013 Scroll Down * @param optionInt the option * @throws IOException if an error occurs */ - protected void processScrollDown(int optionInt) throws IOException { - } + protected void processScrollDown(int optionInt) throws IOException {} /** * Process CSI n U ANSI code, corresponding to SU \u2013 Scroll Up * @param optionInt the option * @throws IOException if an error occurs */ - protected void processScrollUp(int optionInt) throws IOException { - } + protected void processScrollUp(int optionInt) throws IOException {} protected static final int ERASE_SCREEN_TO_END = 0; protected static final int ERASE_SCREEN_TO_BEGINING = 1; @@ -500,8 +479,7 @@ protected void processScrollUp(int optionInt) throws IOException { * @param eraseOption the erase option * @throws IOException if an error occurs */ - protected void processEraseScreen(int eraseOption) throws IOException { - } + protected void processEraseScreen(int eraseOption) throws IOException {} protected static final int ERASE_LINE_TO_END = 0; protected static final int ERASE_LINE_TO_BEGINING = 1; @@ -512,25 +490,27 @@ protected void processEraseScreen(int eraseOption) throws IOException { * @param eraseOption the erase option * @throws IOException if an error occurs */ - protected void processEraseLine(int eraseOption) throws IOException { - } + protected void processEraseLine(int eraseOption) throws IOException {} + + protected static final int ATTRIBUTE_INTENSITY_BOLD = 1; // Intensity: Bold + protected static final int ATTRIBUTE_INTENSITY_FAINT = 2; // Intensity; Faint not widely supported + protected static final int ATTRIBUTE_ITALIC = 3; // Italic; on not widely supported. Sometimes treated as inverse. + protected static final int ATTRIBUTE_UNDERLINE = 4; // Underline; Single + protected static final int ATTRIBUTE_BLINK_SLOW = 5; // Blink; Slow less than 150 per minute + protected static final int ATTRIBUTE_BLINK_FAST = 6; // Blink; Rapid MS-DOS ANSI.SYS; 150 per minute or more + protected static final int ATTRIBUTE_NEGATIVE_ON = + 7; // Image; Negative inverse or reverse; swap foreground and background + protected static final int ATTRIBUTE_CONCEAL_ON = 8; // Conceal on + protected static final int ATTRIBUTE_UNDERLINE_DOUBLE = 21; // Underline; Double not widely supported + protected static final int ATTRIBUTE_INTENSITY_NORMAL = 22; // Intensity; Normal not bold and not faint + protected static final int ATTRIBUTE_UNDERLINE_OFF = 24; // Underline; None + protected static final int ATTRIBUTE_BLINK_OFF = 25; // Blink; off - protected static final int ATTRIBUTE_INTENSITY_BOLD = 1; // Intensity: Bold - protected static final int ATTRIBUTE_INTENSITY_FAINT = 2; // Intensity; Faint not widely supported - protected static final int ATTRIBUTE_ITALIC = 3; // Italic; on not widely supported. Sometimes treated as inverse. - protected static final int ATTRIBUTE_UNDERLINE = 4; // Underline; Single - protected static final int ATTRIBUTE_BLINK_SLOW = 5; // Blink; Slow less than 150 per minute - protected static final int ATTRIBUTE_BLINK_FAST = 6; // Blink; Rapid MS-DOS ANSI.SYS; 150 per minute or more - protected static final int ATTRIBUTE_NEGATIVE_ON = 7; // Image; Negative inverse or reverse; swap foreground and background - protected static final int ATTRIBUTE_CONCEAL_ON = 8; // Conceal on - protected static final int ATTRIBUTE_UNDERLINE_DOUBLE = 21; // Underline; Double not widely supported - protected static final int ATTRIBUTE_INTENSITY_NORMAL = 22; // Intensity; Normal not bold and not faint - protected static final int ATTRIBUTE_UNDERLINE_OFF = 24; // Underline; None - protected static final int ATTRIBUTE_BLINK_OFF = 25; // Blink; off @Deprecated protected static final int ATTRIBUTE_NEGATIVE_Off = 27; // Image; Positive + protected static final int ATTRIBUTE_NEGATIVE_OFF = 27; // Image; Positive - protected static final int ATTRIBUTE_CONCEAL_OFF = 28; // Reveal conceal off + protected static final int ATTRIBUTE_CONCEAL_OFF = 28; // Reveal conceal off /** * process SGR other than 0 (reset), 30-39 (foreground), @@ -546,8 +526,7 @@ protected void processEraseLine(int eraseOption) throws IOException { * @see #processDefaultTextColor() * @see #processDefaultBackgroundColor() */ - protected void processSetAttribute(int attribute) throws IOException { - } + protected void processSetAttribute(int attribute) throws IOException {} protected static final int BLACK = 0; protected static final int RED = 1; @@ -584,8 +563,7 @@ protected void processSetForegroundColor(int color, boolean bright) throws IOExc * @param paletteIndex the text color in the palette * @throws IOException if an error occurs */ - protected void processSetForegroundColorExt(int paletteIndex) throws IOException { - } + protected void processSetForegroundColorExt(int paletteIndex) throws IOException {} /** * process SGR 38 corresponding to extended set text color (foreground) @@ -625,8 +603,7 @@ protected void processSetBackgroundColor(int color, boolean bright) throws IOExc * @param paletteIndex the background color in the palette * @throws IOException if an error occurs */ - protected void processSetBackgroundColorExt(int paletteIndex) throws IOException { - } + protected void processSetBackgroundColorExt(int paletteIndex) throws IOException {} /** * process SGR 48 corresponding to extended set background color @@ -644,22 +621,19 @@ protected void processSetBackgroundColorExt(int r, int g, int b) throws IOExcept * process SGR 39 corresponding to Default text color (foreground) * @throws IOException if an error occurs */ - protected void processDefaultTextColor() throws IOException { - } + protected void processDefaultTextColor() throws IOException {} /** * process SGR 49 corresponding to Default background color * @throws IOException if an error occurs */ - protected void processDefaultBackgroundColor() throws IOException { - } + protected void processDefaultBackgroundColor() throws IOException {} /** * process SGR 0 corresponding to Reset / Normal * @throws IOException if an error occurs */ - protected void processAttributeRest() throws IOException { - } + protected void processAttributeRest() throws IOException {} /** * process CSI n ; m H corresponding to CUP \u2013 Cursor Position or @@ -668,24 +642,21 @@ protected void processAttributeRest() throws IOException { * @param col the column * @throws IOException if an error occurs */ - protected void processCursorTo(int row, int col) throws IOException { - } + protected void processCursorTo(int row, int col) throws IOException {} /** * process CSI n G corresponding to CHA \u2013 Cursor Horizontal Absolute * @param x the column * @throws IOException if an error occurs */ - protected void processCursorToColumn(int x) throws IOException { - } + protected void processCursorToColumn(int x) throws IOException {} /** * process CSI n F corresponding to CPL \u2013 Cursor Previous Line * @param count line count * @throws IOException if an error occurs */ - protected void processCursorUpLine(int count) throws IOException { - } + protected void processCursorUpLine(int count) throws IOException {} /** * process CSI n E corresponding to CNL \u2013 Cursor Next Line @@ -704,8 +675,7 @@ protected void processCursorDownLine(int count) throws IOException { * @param count the count * @throws IOException if an error occurs */ - protected void processCursorLeft(int count) throws IOException { - } + protected void processCursorLeft(int count) throws IOException {} /** * process CSI n C corresponding to CUF \u2013 Cursor Forward @@ -724,19 +694,16 @@ protected void processCursorRight(int count) throws IOException { * @param count the count * @throws IOException if an error occurs */ - protected void processCursorDown(int count) throws IOException { - } + protected void processCursorDown(int count) throws IOException {} /** * process CSI n A corresponding to CUU \u2013 Cursor Up * @param count the count * @throws IOException if an error occurs */ - protected void processCursorUp(int count) throws IOException { - } + protected void processCursorUp(int count) throws IOException {} - protected void processUnknownExtension(ArrayList options, int command) { - } + protected void processUnknownExtension(ArrayList options, int command) {} /** * process OSC 0;text BEL corresponding to Change Window and Icon label @@ -751,23 +718,20 @@ protected void processChangeIconNameAndWindowTitle(String label) { * process OSC 1;text BEL corresponding to Change Icon label * @param name the icon name */ - protected void processChangeIconName(String name) { - } + protected void processChangeIconName(String name) {} /** * process OSC 2;text BEL corresponding to Change Window title * @param title the title */ - protected void processChangeWindowTitle(String title) { - } + protected void processChangeWindowTitle(String title) {} /** * Process unknown OSC command. * @param command the command * @param param the param */ - protected void processUnknownOperatingSystemCommand(int command, String param) { - } + protected void processUnknownOperatingSystemCommand(int command, String param) {} /** * Process character set sequence. @@ -781,17 +745,13 @@ private boolean processCharsetSelect(ArrayList options) throws IOExcepti return true; } - protected void processCharsetSelect(int set, char seq) { - } + protected void processCharsetSelect(int set, char seq) {} private int optionInt(ArrayList options, int index) { - if (options.size() <= index) - throw new IllegalArgumentException(); + if (options.size() <= index) throw new IllegalArgumentException(); Object value = options.get(index); - if (value == null) - throw new IllegalArgumentException(); - if (!value.getClass().equals(Integer.class)) - throw new IllegalArgumentException(); + if (value == null) throw new IllegalArgumentException(); + if (!value.getClass().equals(Integer.class)) throw new IllegalArgumentException(); return (Integer) value; } @@ -828,5 +788,4 @@ public void close() throws IOException { flush(); super.close(); } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java index ccb0aaf689b86..6386f5bba3901 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2021, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -15,6 +15,7 @@ import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal; import jdk.internal.org.jline.utils.InfoCmp.Capability; +import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_DISABLE_ALTERNATE_CHARSET; import static jdk.internal.org.jline.utils.AttributedStyle.BG_COLOR; import static jdk.internal.org.jline.utils.AttributedStyle.BG_COLOR_EXP; import static jdk.internal.org.jline.utils.AttributedStyle.FG_COLOR; @@ -30,12 +31,11 @@ import static jdk.internal.org.jline.utils.AttributedStyle.F_FOREGROUND; import static jdk.internal.org.jline.utils.AttributedStyle.F_FOREGROUND_IND; import static jdk.internal.org.jline.utils.AttributedStyle.F_FOREGROUND_RGB; +import static jdk.internal.org.jline.utils.AttributedStyle.F_HIDDEN; import static jdk.internal.org.jline.utils.AttributedStyle.F_INVERSE; import static jdk.internal.org.jline.utils.AttributedStyle.F_ITALIC; import static jdk.internal.org.jline.utils.AttributedStyle.F_UNDERLINE; -import static jdk.internal.org.jline.utils.AttributedStyle.F_HIDDEN; import static jdk.internal.org.jline.utils.AttributedStyle.MASK; -import static jdk.internal.org.jline.terminal.TerminalBuilder.PROP_DISABLE_ALTERNATE_CHARSET; public abstract class AttributedCharSequence implements CharSequence { @@ -120,6 +120,7 @@ public String toAnsi(int colors, ForceMode force, ColorPalette palette, String a char c = charAt(i); if (altIn != null && altOut != null) { char pc = c; + // @spotless:off switch (c) { case '\u2518': c = 'j'; break; case '\u2510': c = 'k'; break; @@ -133,15 +134,16 @@ public String toAnsi(int colors, ForceMode force, ColorPalette palette, String a case '\u252C': c = 'w'; break; case '\u2502': c = 'x'; break; } + // @spotless:on boolean oldalt = alt; alt = c != pc; if (oldalt ^ alt) { sb.append(alt ? altIn : altOut); } } - long s = styleCodeAt(i) & ~F_HIDDEN; // The hidden flag does not change the ansi styles + long s = styleCodeAt(i) & ~F_HIDDEN; // The hidden flag does not change the ansi styles if (style != s) { - long d = (style ^ s) & MASK; + long d = (style ^ s) & MASK; long fg = (s & F_FOREGROUND) != 0 ? s & (FG_COLOR | F_FOREGROUND) : 0; long bg = (s & F_BACKGROUND) != 0 ? s & (BG_COLOR | F_BACKGROUND) : 0; if (s == 0) { @@ -172,16 +174,16 @@ public String toAnsi(int colors, ForceMode force, ColorPalette palette, String a if (fg > 0) { int rounded = -1; if ((fg & F_FOREGROUND_RGB) != 0) { - int r = (int)(fg >> (FG_COLOR_EXP + 16)) & 0xFF; - int g = (int)(fg >> (FG_COLOR_EXP + 8)) & 0xFF; - int b = (int)(fg >> FG_COLOR_EXP) & 0xFF; + int r = (int) (fg >> (FG_COLOR_EXP + 16)) & 0xFF; + int g = (int) (fg >> (FG_COLOR_EXP + 8)) & 0xFF; + int b = (int) (fg >> FG_COLOR_EXP) & 0xFF; if (colors >= HIGH_COLORS) { first = attr(sb, "38;2;" + r + ";" + g + ";" + b, first); } else { rounded = palette.round(r, g, b); } } else if ((fg & F_FOREGROUND_IND) != 0) { - rounded = palette.round((int)(fg >> FG_COLOR_EXP) & 0xFF); + rounded = palette.round((int) (fg >> FG_COLOR_EXP) & 0xFF); } if (rounded >= 0) { if (colors >= HIGH_COLORS && force == ForceMode.ForceTrueColors) { @@ -211,16 +213,16 @@ public String toAnsi(int colors, ForceMode force, ColorPalette palette, String a if (bg > 0) { int rounded = -1; if ((bg & F_BACKGROUND_RGB) != 0) { - int r = (int)(bg >> (BG_COLOR_EXP + 16)) & 0xFF; - int g = (int)(bg >> (BG_COLOR_EXP + 8)) & 0xFF; - int b = (int)(bg >> BG_COLOR_EXP) & 0xFF; + int r = (int) (bg >> (BG_COLOR_EXP + 16)) & 0xFF; + int g = (int) (bg >> (BG_COLOR_EXP + 8)) & 0xFF; + int b = (int) (bg >> BG_COLOR_EXP) & 0xFF; if (colors >= HIGH_COLORS) { first = attr(sb, "48;2;" + r + ";" + g + ";" + b, first); } else { rounded = palette.round(r, g, b); } } else if ((bg & F_BACKGROUND_IND) != 0) { - rounded = palette.round((int)(bg >> BG_COLOR_EXP) & 0xFF); + rounded = palette.round((int) (bg >> BG_COLOR_EXP) & 0xFF); } if (rounded >= 0) { if (colors >= HIGH_COLORS && force == ForceMode.ForceTrueColors) { @@ -243,8 +245,7 @@ public String toAnsi(int colors, ForceMode force, ColorPalette palette, String a background = bg; } if ((d & (F_BOLD | F_FAINT)) != 0) { - if ( (d & F_BOLD) != 0 && (s & F_BOLD) == 0 - || (d & F_FAINT) != 0 && (s & F_FAINT) == 0) { + if ((d & F_BOLD) != 0 && (s & F_BOLD) == 0 || (d & F_FAINT) != 0 && (s & F_FAINT) == 0) { first = attr(sb, "22", first); } if ((d & F_BOLD) != 0 && (s & F_BOLD) != 0) { @@ -360,8 +361,7 @@ public int columnLength() { int len = length(); for (int cur = 0; cur < len; ) { int cp = codePointAt(cur); - if (!isHidden(cur)) - cols += WCWidth.wcwidth(cp); + if (!isHidden(cur)) cols += WCWidth.wcwidth(cp); cur += Character.charCount(cp); } return cols; @@ -382,8 +382,7 @@ public AttributedString columnSubSequence(int start, int stop) { int end = begin; while (end < this.length()) { int cp = codePointAt(end); - if (cp == '\n') - break; + if (cp == '\n') break; int w = isHidden(end) ? 0 : WCWidth.wcwidth(cp); if (col + w > stop) { break; @@ -407,7 +406,7 @@ public List columnSplitLength(int columns, boolean includeNewl int cp = codePointAt(cur); int w = isHidden(cur) ? 0 : WCWidth.wcwidth(cp); if (cp == '\n') { - strings.add(subSequence(beg, includeNewlines ? cur+1 : cur)); + strings.add(subSequence(beg, includeNewlines ? cur + 1 : cur)); beg = cur + 1; col = 0; } else if ((col += w) > columns) { @@ -429,5 +428,4 @@ public String toString() { public AttributedString toAttributedString() { return substring(0, length()); } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java index 47492b72e7648..3c70a6d0e7bf4 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -15,6 +15,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import jdk.internal.org.jline.terminal.Terminal; + /** * Attributed string. * Instances of this class are immutables. @@ -103,11 +105,28 @@ public static AttributedString fromAnsi(String ansi, int tabs) { } public static AttributedString fromAnsi(String ansi, List tabs) { + return fromAnsi(ansi, tabs, null, null); + } + + public static AttributedString fromAnsi(String ansi, Terminal terminal) { + String alternateIn, alternateOut; + if (!DISABLE_ALTERNATE_CHARSET) { + alternateIn = Curses.tputs(terminal.getStringCapability(InfoCmp.Capability.enter_alt_charset_mode)); + alternateOut = Curses.tputs(terminal.getStringCapability(InfoCmp.Capability.exit_alt_charset_mode)); + } else { + alternateIn = null; + alternateOut = null; + } + return fromAnsi(ansi, Arrays.asList(0), alternateIn, alternateOut); + } + + public static AttributedString fromAnsi(String ansi, List tabs, String altIn, String altOut) { if (ansi == null) { return null; } return new AttributedStringBuilder(ansi.length()) .tabs(tabs) + .altCharset(altIn, altOut) .ansiAppend(ansi) .toAttributedString(); } @@ -116,9 +135,7 @@ public static String stripAnsi(String ansi) { if (ansi == null) { return null; } - return new AttributedStringBuilder(ansi.length()) - .ansiAppend(ansi) - .toString(); + return new AttributedStringBuilder(ansi.length()).ansiAppend(ansi).toString(); } @Override @@ -162,7 +179,7 @@ public AttributedString styleMatches(Pattern pattern, AttributedStyle style) { } result = matcher.find(); } while (result); - return new AttributedString(buffer, newstyle, start , end); + return new AttributedString(buffer, newstyle, start, end); } return this; } @@ -179,15 +196,16 @@ && arrEq(buffer, that.buffer, start, that.start, end - start) private boolean arrEq(char[] a1, char[] a2, int s1, int s2, int l) { for (int i = 0; i < l; i++) { - if (a1[s1+i] != a2[s2+i]) { + if (a1[s1 + i] != a2[s2 + i]) { return false; } } return true; } + private boolean arrEq(long[] a1, long[] a2, int s1, int s2, int l) { for (int i = 0; i < l; i++) { - if (a1[s1+i] != a2[s2+i]) { + if (a1[s1 + i] != a2[s2 + i]) { return false; } } @@ -221,5 +239,4 @@ public static AttributedString join(AttributedString delimiter, Iterable 0) { + append(s); + } + return this; + } + public AttributedStringBuilder append(CharSequence csq, AttributedStyle style) { return append(new AttributedString(csq, style)); } @@ -117,12 +125,12 @@ public AttributedStringBuilder style(AttributedStyle style) { return this; } - public AttributedStringBuilder style(Function style) { + public AttributedStringBuilder style(Function style) { current = style.apply(current); return this; } - public AttributedStringBuilder styled(Function style, CharSequence cs) { + public AttributedStringBuilder styled(Function style, CharSequence cs) { return styled(style, sb -> sb.append(cs)); } @@ -130,7 +138,8 @@ public AttributedStringBuilder styled(AttributedStyle style, CharSequence cs) { return styled(s -> style, sb -> sb.append(cs)); } - public AttributedStringBuilder styled(Function style, Consumer consumer) { + public AttributedStringBuilder styled( + Function style, Consumer consumer) { AttributedStyle prev = current; current = style.apply(prev); consumer.accept(this); @@ -338,23 +347,90 @@ public AttributedStringBuilder ansiAppend(String ansi) { // This is not a SGR code, so ignore ansiState = 0; } - } else if (c == '\t' && tabs.defined()) { - insertTab(current); } else { - ensureCapacity(length + 1); - buffer[length] = c; - style[length] = this.current.getStyle(); - if (c == '\n') { - lastLineLength = 0; + if (ansiState >= 1) { + ensureCapacity(length + 1); + buffer[length++] = 27; + if (ansiState >= 2) { + ensureCapacity(length + 1); + buffer[length++] = '['; + } + ansiState = 0; + } + if (c == '\t' && tabs.defined()) { + insertTab(current); } else { - lastLineLength++; + ensureCapacity(length + 1); + if (inAltCharset) { + switch (c) { + case 'j': + c = '\u2518'; + break; + case 'k': + c = '\u2510'; + break; + case 'l': + c = '\u250C'; + break; + case 'm': + c = '\u2514'; + break; + case 'n': + c = '\u253C'; + break; + case 'q': + c = '\u2500'; + break; + case 't': + c = '\u251C'; + break; + case 'u': + c = '\u2524'; + break; + case 'v': + c = '\u2534'; + break; + case 'w': + c = '\u252C'; + break; + case 'x': + c = '\u2502'; + break; + } + } + buffer[length] = c; + style[length] = this.current.getStyle(); + if (c == '\n') { + lastLineLength = 0; + } else { + lastLineLength++; + } + length++; + if (altIn != null && altOut != null) { + char[] alt = inAltCharset ? altOut : altIn; + if (equals(buffer, length - alt.length, alt, 0, alt.length)) { + inAltCharset = !inAltCharset; + length -= alt.length; + } + } } - length++; } } return this; } + private static boolean equals(char[] a, int aFromIndex, char[] b, int bFromIndex, int length) { + if (aFromIndex < 0 || bFromIndex < 0 || aFromIndex + length > a.length || bFromIndex + length > b.length) { + return false; + } + for (int i = 0; i < length; i++) { + if (a[aFromIndex + i] != b[bFromIndex + i]) { + return false; + } + } + return true; + } + protected void insertTab(AttributedStyle s) { int nb = tabs.spaces(lastLineLength); ensureCapacity(length + nb); @@ -393,6 +469,15 @@ public AttributedStringBuilder tabs(List tabs) { return this; } + public AttributedStringBuilder altCharset(String altIn, String altOut) { + if (length > 0) { + throw new IllegalStateException("Cannot change alternative charset after appending text"); + } + this.altIn = altIn != null ? altIn.toCharArray() : null; + this.altOut = altOut != null ? altOut.toCharArray() : null; + return this; + } + public AttributedStringBuilder styleMatches(Pattern pattern, AttributedStyle s) { Matcher matcher = pattern.matcher(this); while (matcher.find()) { @@ -416,7 +501,7 @@ public AttributedStringBuilder styleMatches(Pattern pattern, List tabs = new ArrayList<>(); private int lastStop = 0; private int lastSize = 0; @@ -428,7 +513,7 @@ public TabStops(int tabs) { public TabStops(List tabs) { this.tabs = tabs; int p = 0; - for (int s: tabs) { + for (int s : tabs) { if (s <= p) { continue; } @@ -447,7 +532,7 @@ int spaces(int lastLineLength) { if (lastLineLength >= lastStop) { out = lastSize - (lastLineLength - lastStop) % lastSize; } else { - for (int s: tabs) { + for (int s : tabs) { if (s > lastLineLength) { out = s - lastLineLength; break; @@ -456,7 +541,5 @@ int spaces(int lastLineLength) { } return out; } - } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java index 902e2e1f3b849..7845e99e0b0f9 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2021, the original author or authors. + * Copyright (c) 2002-2021, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -15,39 +15,39 @@ */ public class AttributedStyle { - public static final int BLACK = 0; - public static final int RED = 1; - public static final int GREEN = 2; - public static final int YELLOW = 3; - public static final int BLUE = 4; - public static final int MAGENTA = 5; - public static final int CYAN = 6; - public static final int WHITE = 7; - - public static final int BRIGHT = 8; - - static final long F_BOLD = 0x00000001; - static final long F_FAINT = 0x00000002; - static final long F_ITALIC = 0x00000004; - static final long F_UNDERLINE = 0x00000008; - static final long F_BLINK = 0x00000010; - static final long F_INVERSE = 0x00000020; - static final long F_CONCEAL = 0x00000040; - static final long F_CROSSED_OUT = 0x00000080; - static final long F_FOREGROUND_IND = 0x00000100; - static final long F_FOREGROUND_RGB = 0x00000200; - static final long F_FOREGROUND = F_FOREGROUND_IND | F_FOREGROUND_RGB; - static final long F_BACKGROUND_IND = 0x00000400; - static final long F_BACKGROUND_RGB = 0x00000800; - static final long F_BACKGROUND = F_BACKGROUND_IND | F_BACKGROUND_RGB; - static final long F_HIDDEN = 0x00001000; - - static final long MASK = 0x00001FFF; - - static final int FG_COLOR_EXP = 15; - static final int BG_COLOR_EXP = 39; - static final long FG_COLOR = 0xFFFFFFL << FG_COLOR_EXP; - static final long BG_COLOR = 0xFFFFFFL << BG_COLOR_EXP; + public static final int BLACK = 0; + public static final int RED = 1; + public static final int GREEN = 2; + public static final int YELLOW = 3; + public static final int BLUE = 4; + public static final int MAGENTA = 5; + public static final int CYAN = 6; + public static final int WHITE = 7; + + public static final int BRIGHT = 8; + + static final long F_BOLD = 0x00000001; + static final long F_FAINT = 0x00000002; + static final long F_ITALIC = 0x00000004; + static final long F_UNDERLINE = 0x00000008; + static final long F_BLINK = 0x00000010; + static final long F_INVERSE = 0x00000020; + static final long F_CONCEAL = 0x00000040; + static final long F_CROSSED_OUT = 0x00000080; + static final long F_FOREGROUND_IND = 0x00000100; + static final long F_FOREGROUND_RGB = 0x00000200; + static final long F_FOREGROUND = F_FOREGROUND_IND | F_FOREGROUND_RGB; + static final long F_BACKGROUND_IND = 0x00000400; + static final long F_BACKGROUND_RGB = 0x00000800; + static final long F_BACKGROUND = F_BACKGROUND_IND | F_BACKGROUND_RGB; + static final long F_HIDDEN = 0x00001000; + + static final long MASK = 0x00001FFF; + + static final int FG_COLOR_EXP = 15; + static final int BG_COLOR_EXP = 39; + static final long FG_COLOR = 0xFFFFFFL << FG_COLOR_EXP; + static final long BG_COLOR = 0xFFFFFFL << BG_COLOR_EXP; public static final AttributedStyle DEFAULT = new AttributedStyle(); public static final AttributedStyle BOLD = DEFAULT.bold(); @@ -70,8 +70,9 @@ public AttributedStyle(AttributedStyle s) { public AttributedStyle(long style, long mask) { this.style = style; - this.mask = mask & MASK | ((style & F_FOREGROUND) != 0 ? FG_COLOR : 0) - | ((style & F_BACKGROUND) != 0 ? BG_COLOR : 0); + this.mask = mask & MASK + | ((style & F_FOREGROUND) != 0 ? FG_COLOR : 0) + | ((style & F_BACKGROUND) != 0 ? BG_COLOR : 0); } public AttributedStyle bold() { @@ -176,7 +177,9 @@ public AttributedStyle crossedOutDefault() { } public AttributedStyle foreground(int color) { - return new AttributedStyle(style & ~FG_COLOR | F_FOREGROUND_IND | (((long) color << FG_COLOR_EXP) & FG_COLOR), mask | F_FOREGROUND_IND); + return new AttributedStyle( + style & ~FG_COLOR | F_FOREGROUND_IND | (((long) color << FG_COLOR_EXP) & FG_COLOR), + mask | F_FOREGROUND_IND); } public AttributedStyle foreground(int r, int g, int b) { @@ -184,7 +187,9 @@ public AttributedStyle foreground(int r, int g, int b) { } public AttributedStyle foregroundRgb(int color) { - return new AttributedStyle(style & ~FG_COLOR | F_FOREGROUND_RGB | ((((long) color & 0xFFFFFF) << FG_COLOR_EXP) & FG_COLOR), mask | F_FOREGROUND_RGB); + return new AttributedStyle( + style & ~FG_COLOR | F_FOREGROUND_RGB | ((((long) color & 0xFFFFFF) << FG_COLOR_EXP) & FG_COLOR), + mask | F_FOREGROUND_RGB); } public AttributedStyle foregroundOff() { @@ -196,7 +201,9 @@ public AttributedStyle foregroundDefault() { } public AttributedStyle background(int color) { - return new AttributedStyle(style & ~BG_COLOR | F_BACKGROUND_IND | (((long) color << BG_COLOR_EXP) & BG_COLOR), mask | F_BACKGROUND_IND); + return new AttributedStyle( + style & ~BG_COLOR | F_BACKGROUND_IND | (((long) color << BG_COLOR_EXP) & BG_COLOR), + mask | F_BACKGROUND_IND); } public AttributedStyle background(int r, int g, int b) { @@ -204,7 +211,9 @@ public AttributedStyle background(int r, int g, int b) { } public AttributedStyle backgroundRgb(int color) { - return new AttributedStyle(style & ~BG_COLOR | F_BACKGROUND_RGB | ((((long) color & 0xFFFFFF) << BG_COLOR_EXP) & BG_COLOR), mask | F_BACKGROUND_RGB); + return new AttributedStyle( + style & ~BG_COLOR | F_BACKGROUND_RGB | ((((long) color & 0xFFFFFF) << BG_COLOR_EXP) & BG_COLOR), + mask | F_BACKGROUND_RGB); } public AttributedStyle backgroundOff() { @@ -249,7 +258,6 @@ public boolean equals(Object o) { AttributedStyle that = (AttributedStyle) o; if (style != that.style) return false; return mask == that.mask; - } @Override @@ -266,10 +274,6 @@ public String toAnsi() { @Override public String toString() { - return "AttributedStyle{" + - "style=" + style + - ", mask=" + mask + - ", ansi=" + toAnsi() + - '}'; + return "AttributedStyle{" + "style=" + style + ", mask=" + mask + ", ansi=" + toAnsi() + '}'; } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ClosedException.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ClosedException.java index f2adb0878c718..a6f5bfb5b5463 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ClosedException.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ClosedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -14,8 +14,7 @@ public class ClosedException extends IOException { private static final long serialVersionUID = 3085420657077696L; - public ClosedException() { - } + public ClosedException() {} public ClosedException(String message) { super(message); diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ColorPalette.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ColorPalette.java index 6f55273a7b935..b4175fe13cec5 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ColorPalette.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ColorPalette.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2020, the original author or authors. + * Copyright (c) 2002-2020, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -21,7 +21,8 @@ */ public class ColorPalette { - public static final String XTERM_INITC = "\\E]4;%p1%d;rgb\\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\\E\\\\"; + public static final String XTERM_INITC = + "\\E]4;%p1%d;rgb\\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\\E\\\\"; public static final ColorPalette DEFAULT = new ColorPalette(); @@ -41,6 +42,7 @@ public ColorPalette(Terminal terminal) throws IOException { this(terminal, null); } + @SuppressWarnings("this-escape") public ColorPalette(Terminal terminal, String distance) throws IOException { this.terminal = terminal; this.distanceName = distance; @@ -245,10 +247,13 @@ private static int[] doLoad(Terminal terminal) throws IOException { if (rgb.size() != 3) { return null; } - double r = Integer.parseInt(rgb.get(0), 16) / ((1 << (4 * rgb.get(0).length())) - 1.0); - double g = Integer.parseInt(rgb.get(1), 16) / ((1 << (4 * rgb.get(1).length())) - 1.0); - double b = Integer.parseInt(rgb.get(2), 16) / ((1 << (4 * rgb.get(2).length())) - 1.0); - palette[idx] = (int)((Math.round(r * 255) << 16) + (Math.round(g * 255) << 8) + Math.round(b * 255)); + double r = Integer.parseInt(rgb.get(0), 16) + / ((1 << (4 * rgb.get(0).length())) - 1.0); + double g = Integer.parseInt(rgb.get(1), 16) + / ((1 << (4 * rgb.get(1).length())) - 1.0); + double b = Integer.parseInt(rgb.get(2), 16) + / ((1 << (4 * rgb.get(2).length())) - 1.0); + palette[idx] = (int) ((Math.round(r * 255) << 16) + (Math.round(g * 255) << 8) + Math.round(b * 255)); black &= palette[idx] == 0; } if (black) { @@ -256,7 +261,13 @@ private static int[] doLoad(Terminal terminal) throws IOException { } } int max = 256; - while (max > 0 && palette[--max] == 0); + while (max > 0 && palette[--max] == 0) + ; return Arrays.copyOfRange(palette, 0, max + 1); } + + @Override + public String toString() { + return "ColorPalette[" + "length=" + getLength() + ", " + "distance='" + getDist() + "\']"; + } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java index dc452e103645f..d5e7644d3cb44 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -21,6 +21,8 @@ public class Colors { + // @spotless:off + /** * Default 256 colors palette */ @@ -111,24 +113,26 @@ public class Colors { 0x2e2e2e, 0x5c5c5c, 0x737373, 0x8b8b8b, 0xa2a2a2, 0xb9b9b9, 0xd0d0d0, 0xe7e7e7, }; + // @spotless:on + /** D50 illuminant for CAM color spaces */ - public static final double[] D50 = new double[] { 96.422f, 100.0f, 82.521f }; + public static final double[] D50 = new double[] {96.422f, 100.0f, 82.521f}; /** D65 illuminant for CAM color spaces */ - public static final double[] D65 = new double[] { 95.047, 100.0, 108.883 }; + public static final double[] D65 = new double[] {95.047, 100.0, 108.883}; /** Average surrounding for CAM color spaces */ - public static final double[] averageSurrounding = new double[] { 1.0, 0.690, 1.0 }; + public static final double[] averageSurrounding = new double[] {1.0, 0.690, 1.0}; /** Dim surrounding for CAM color spaces */ - public static final double[] dimSurrounding = new double[] { 0.9, 0.590, 0.9 }; + public static final double[] dimSurrounding = new double[] {0.9, 0.590, 0.9}; /** Dark surrounding for CAM color spaces */ - public static final double[] darkSurrounding = new double[] { 0.8, 0.525, 0.8 }; + public static final double[] darkSurrounding = new double[] {0.8, 0.525, 0.8}; /** sRGB encoding environment */ - public static final double[] sRGB_encoding_environment = vc(D50, 64.0, 64.0/5, dimSurrounding); + public static final double[] sRGB_encoding_environment = vc(D50, 64.0, 64.0 / 5, dimSurrounding); /** sRGB typical environment */ - public static final double[] sRGB_typical_environment = vc(D50, 200.0, 200.0/5, averageSurrounding); + public static final double[] sRGB_typical_environment = vc(D50, 200.0, 200.0 / 5, averageSurrounding); /** Adobe RGB environment */ - public static final double[] AdobeRGB_environment = vc(D65, 160.0, 160.0/5, averageSurrounding); + public static final double[] AdobeRGB_environment = vc(D65, 160.0, 160.0 / 5, averageSurrounding); private static int[] COLORS_256 = DEFAULT_COLORS_256; @@ -149,8 +153,9 @@ public static Integer rgbColor(String name) { if (COLOR_NAMES == null) { Map colors = new LinkedHashMap<>(); try (InputStream is = InfoCmp.class.getResourceAsStream("colors.txt"); - BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { - br.lines().map(String::trim) + BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + br.lines() + .map(String::trim) .filter(s -> !s.startsWith("#")) .filter(s -> !s.isEmpty()) .forEachOrdered(s -> { @@ -206,7 +211,7 @@ static Distance getDistance(String dist) { if (dist == null) { dist = System.getProperty(PROP_COLOR_DISTANCE, "cie76"); } - return doGetDistance(dist); + return new NamedDistance(dist, doGetDistance(dist)); } private static Distance doGetDistance(String dist) { @@ -216,7 +221,7 @@ private static Distance doGetDistance(String dist) { double[] c1 = rgb(p1); double[] c2 = rgb(p2); double rmean = (c1[0] + c2[0]) / 2.0; - double[] w = { 2.0 + rmean, 4.0, 3.0 - rmean }; + double[] w = {2.0 + rmean, 4.0, 3.0 - rmean}; return scalar(c1, c2, w); }; } @@ -228,7 +233,7 @@ private static Distance doGetDistance(String dist) { } if (dist.matches("lab\\(([0-9]+(\\.[0-9]+)?),([0-9]+(\\.[0-9]+)?)\\)")) { double[] w = getWeights(dist); - return (p1, p2) -> scalar(rgb2cielab(p1), rgb2cielab(p2), new double[] { w[0], w[1], w[1] }); + return (p1, p2) -> scalar(rgb2cielab(p1), rgb2cielab(p2), new double[] {w[0], w[1], w[1]}); } if (dist.equals("cie94")) { return (p1, p2) -> cie94(rgb2cielab(p1), rgb2cielab(p2)); @@ -251,7 +256,7 @@ private static Distance doGetDistance(String dist) { double[] c1 = camlab(p1, sRGB_typical_environment); double[] c2 = camlab(p2, sRGB_typical_environment); double[] w = getWeights(dist); - return scalar(c1, c2, new double[] { w[0], w[1], w[1] }); + return scalar(c1, c2, new double[] {w[0], w[1], w[1]}); }; } if (dist.matches("camlch")) { @@ -273,20 +278,37 @@ private static Distance doGetDistance(String dist) { } private static double[] getWeights(String dist) { - String[] weights = dist.substring(dist.indexOf('(') + 1, dist.length() - 1).split(","); + String[] weights = + dist.substring(dist.indexOf('(') + 1, dist.length() - 1).split(","); return Stream.of(weights).mapToDouble(Double::parseDouble).toArray(); } private static double scalar(double[] c1, double[] c2, double[] w) { - return sqr((c1[0] - c2[0]) * w[0]) - + sqr((c1[1] - c2[1]) * w[1]) - + sqr((c1[2] - c2[2]) * w[2]); + return sqr((c1[0] - c2[0]) * w[0]) + sqr((c1[1] - c2[1]) * w[1]) + sqr((c1[2] - c2[2]) * w[2]); } private static double scalar(double[] c1, double[] c2) { - return sqr(c1[0] - c2[0]) - + sqr(c1[1] - c2[1]) - + sqr(c1[2] - c2[2]); + return sqr(c1[0] - c2[0]) + sqr(c1[1] - c2[1]) + sqr(c1[2] - c2[2]); + } + + private static class NamedDistance implements Distance { + private final String name; + private final Distance delegate; + + public NamedDistance(String name, Distance delegate) { + this.name = name; + this.delegate = delegate; + } + + @Override + public double compute(int c1, int c2) { + return delegate.compute(c1, c2); + } + + @Override + public String toString() { + return name; + } } private static final int L = 0; @@ -325,7 +347,7 @@ private static double cie00(double[] lab1, double[] lab2) { double c_star_average_ab = (c_star_1_ab + c_star_2_ab) / 2.0; double c_star_average_ab_pot_3 = c_star_average_ab * c_star_average_ab * c_star_average_ab; double c_star_average_ab_pot_7 = c_star_average_ab_pot_3 * c_star_average_ab_pot_3 * c_star_average_ab; - double G = 0.5 * (1.0 - Math.sqrt(c_star_average_ab_pot_7 / (c_star_average_ab_pot_7 + 6103515625.0))); //25^7 + double G = 0.5 * (1.0 - Math.sqrt(c_star_average_ab_pot_7 / (c_star_average_ab_pot_7 + 6103515625.0))); // 25^7 double a1_prime = (1.0 + G) * lab1[A]; double a2_prime = (1.0 + G) * lab2[A]; double C_prime_1 = Math.sqrt(a1_prime * a1_prime + lab1[B] * lab1[B]); @@ -365,16 +387,18 @@ private static double cie00(double[] lab1, double[] lab2) { + 0.24 * Math.cos(Math.toRadians(h_prime_average * 2.0)) + 0.32 * Math.cos(Math.toRadians(h_prime_average * 3.0 + 6.0)) - 0.20 * Math.cos(Math.toRadians(h_prime_average * 4.0 - 63.0)); - double S_L = 1.0 + ((0.015 * L_prime_average_minus_50_square) / Math.sqrt(20.0 + L_prime_average_minus_50_square)); + double S_L = + 1.0 + ((0.015 * L_prime_average_minus_50_square) / Math.sqrt(20.0 + L_prime_average_minus_50_square)); double S_C = 1.0 + 0.045 * C_prime_average; double S_H = 1.0 + 0.015 * T * C_prime_average; double h_prime_average_minus_275_div_25 = (h_prime_average - 275.0) / (25.0); - double h_prime_average_minus_275_div_25_square = h_prime_average_minus_275_div_25 * h_prime_average_minus_275_div_25; + double h_prime_average_minus_275_div_25_square = + h_prime_average_minus_275_div_25 * h_prime_average_minus_275_div_25; double delta_theta = 30.0 * Math.exp(-h_prime_average_minus_275_div_25_square); double C_prime_average_pot_3 = C_prime_average * C_prime_average * C_prime_average; double C_prime_average_pot_7 = C_prime_average_pot_3 * C_prime_average_pot_3 * C_prime_average; - double R_C = 2.0 * Math.sqrt(C_prime_average_pot_7 / (C_prime_average_pot_7 + 6103515625.0)); //25^7 - double R_T = - Math.sin(Math.toRadians(2.0 * delta_theta)) * R_C; + double R_C = 2.0 * Math.sqrt(C_prime_average_pot_7 / (C_prime_average_pot_7 + 6103515625.0)); // 25^7 + double R_T = -Math.sin(Math.toRadians(2.0 * delta_theta)) * R_C; double dLKlsl = delta_L_prime / (kl * S_L); double dCkcsc = delta_C_prime / (kc * S_C); double dHkhsh = delta_H_prime / (kh * S_H); @@ -392,11 +416,11 @@ private static double[] jmh2ucs(double[] lch) { double sM = ((1.0 / 0.0228) * Math.log(1.0 + 0.0228 * lch[1])); double a = sM * Math.cos(Math.toRadians(lch[2])); double b = sM * Math.sin(Math.toRadians(lch[2])); - return new double[] {sJ, a, b }; + return new double[] {sJ, a, b}; } static double camlch(double[] c1, double[] c2) { - return camlch(c1, c2, new double[] { 1.0, 1.0, 1.0 }); + return camlch(c1, c2, new double[] {1.0, 1.0, 1.0}); } static double camlch(double[] c1, double[] c2, double[] w) { @@ -414,18 +438,16 @@ static double camlch(double[] c1, double[] c2, double[] w) { private static double hueDifference(double hue1, double hue2, double c) { double difference = (hue2 - hue1) % c; double ch = c / 2; - if (difference > ch) - difference -= c; - if (difference < -ch) - difference += c; + if (difference > ch) difference -= c; + if (difference < -ch) difference += c; return difference; } private static double[] rgb(int color) { int r = (color >> 16) & 0xFF; - int g = (color >> 8) & 0xFF; - int b = (color >> 0) & 0xFF; - return new double[] { r / 255.0, g / 255.0, b / 255.0 }; + int g = (color >> 8) & 0xFF; + int b = (color >> 0) & 0xFF; + return new double[] {r / 255.0, g / 255.0, b / 255.0}; } static double[] rgb2xyz(int color) { @@ -454,13 +476,13 @@ static double[] camlab(int color, double[] vc) { static double[] lch2lab(double[] lch) { double toRad = Math.PI / 180; - return new double[] { lch[0], lch[1] * Math.cos(lch[2] * toRad), lch[1] * Math.sin(lch[2] * toRad) }; + return new double[] {lch[0], lch[1] * Math.cos(lch[2] * toRad), lch[1] * Math.sin(lch[2] * toRad)}; } private static double[] xyz2camlch(double[] xyz, double[] vc) { double[] XYZ = new double[] {xyz[0] * 100.0, xyz[1] * 100.0, xyz[2] * 100.0}; double[] cam = forwardTransform(XYZ, vc); - return new double[] { cam[J], cam[M], cam[h] }; + return new double[] {cam[J], cam[M], cam[h]}; } /** Lightness */ @@ -478,12 +500,12 @@ private static double[] xyz2camlch(double[] xyz, double[] vc) { /** Hue */ public static final int h = 6; - /** CIECAM02 appearance correlates */ private static double[] forwardTransform(double[] XYZ, double[] vc) { // calculate sharpened cone response double[] RGB = forwardPreAdaptationConeResponse(XYZ); - // calculate corresponding (sharpened) cone response considering various luminance level and surround conditions in D + // calculate corresponding (sharpened) cone response considering various luminance level and surround conditions + // in D double[] RGB_c = forwardPostAdaptationConeResponse(RGB, vc); // calculate HPE equal area cone fundamentals double[] RGBPrime = CAT02toHPE(RGB_c); @@ -501,37 +523,40 @@ private static double[] forwardTransform(double[] XYZ, double[] vc) { // calculate eccentricity double e = ((12500.0 / 13.0) * vc[VC_N_C] * vc[VC_N_CB]) * (Math.cos(Math.toRadians(h) + 2.0) + 3.8); // get t - double t = e * Math.sqrt(Math.pow(a, 2.0) + Math.pow(b, 2.0)) / (RGBPrime_a[0] + RGBPrime_a[1] + 1.05 * RGBPrime_a[2]); + double t = e + * Math.sqrt(Math.pow(a, 2.0) + Math.pow(b, 2.0)) + / (RGBPrime_a[0] + RGBPrime_a[1] + 1.05 * RGBPrime_a[2]); // calculate brightness double Q = (4.0 / vc[VC_C]) * Math.sqrt(J / 100.0) * (vc[VC_A_W] + 4.0) * Math.pow(vc[VC_F_L], 0.25); // calculate the correlates of chroma, colorfulness, and saturation - double C = Math.signum(t) * Math.pow(Math.abs(t), 0.9) * Math.sqrt(J / 100.0) * Math.pow(1.64- Math.pow(0.29, vc[VC_N]), 0.73); + double C = Math.signum(t) + * Math.pow(Math.abs(t), 0.9) + * Math.sqrt(J / 100.0) + * Math.pow(1.64 - Math.pow(0.29, vc[VC_N]), 0.73); double M = C * Math.pow(vc[VC_F_L], 0.25); double s = 100.0 * Math.sqrt(M / Q); // calculate hue composition double H = calculateH(h); - return new double[] { J, Q, C, M, s, H, h }; + return new double[] {J, Q, C, M, s, H, h}; } private static double calculateH(double h) { - if (h < 20.14) - h = h + 360; + if (h < 20.14) h = h + 360; double i; - if (h >= 20.14 && h < 90.0) { // index i = 1 + if (h >= 20.14 && h < 90.0) { // index i = 1 i = (h - 20.14) / 0.8; return 100.0 * i / (i + (90 - h) / 0.7); } else if (h < 164.25) { // index i = 2 i = (h - 90) / 0.7; return 100.0 + 100.0 * i / (i + (164.25 - h) / 1); - } else if (h < 237.53) { // index i = 3 + } else if (h < 237.53) { // index i = 3 i = (h - 164.25) / 1.0; return 200.0 + 100.0 * i / (i + (237.53 - h) / 1.2); - } else if (h <= 380.14) { // index i = 4 + } else if (h <= 380.14) { // index i = 4 i = (h - 237.53) / 1.2; double H = 300.0 + 100.0 * i / (i + (380.14 - h) / 0.8); // don't use 400 if we can use 0 - if (H <= 400.0 && H >= 399.999) - H = 0; + if (H <= 400.0 && H >= 399.999) H = 0; return H; } else { throw new IllegalArgumentException("h outside assumed range 0..360: " + h); @@ -540,8 +565,8 @@ private static double calculateH(double h) { private static double[] forwardResponseCompression(double[] RGB, double[] vc) { double[] result = new double[3]; - for(int channel = 0; channel < RGB.length; channel++) { - if(RGB[channel] >= 0) { + for (int channel = 0; channel < RGB.length; channel++) { + if (RGB[channel] >= 0) { double n = Math.pow(vc[VC_F_L] * RGB[channel] / 100.0, 0.42); result[channel] = 400.0 * n / (n + 27.13) + 0.1; } else { @@ -553,22 +578,22 @@ private static double[] forwardResponseCompression(double[] RGB, double[] vc) { } private static double[] forwardPostAdaptationConeResponse(double[] RGB, double[] vc) { - return new double[] { vc[VC_D_RGB_R] * RGB[0], vc[VC_D_RGB_G] * RGB[1], vc[VC_D_RGB_B] * RGB[2] }; + return new double[] {vc[VC_D_RGB_R] * RGB[0], vc[VC_D_RGB_G] * RGB[1], vc[VC_D_RGB_B] * RGB[2]}; } public static double[] CAT02toHPE(double[] RGB) { double[] RGBPrime = new double[3]; - RGBPrime[0] = 0.7409792 * RGB[0] + 0.2180250 * RGB[1] + 0.0410058 * RGB[2]; - RGBPrime[1] = 0.2853532 * RGB[0] + 0.6242014 * RGB[1] + 0.0904454 * RGB[2]; + RGBPrime[0] = 0.7409792 * RGB[0] + 0.2180250 * RGB[1] + 0.0410058 * RGB[2]; + RGBPrime[1] = 0.2853532 * RGB[0] + 0.6242014 * RGB[1] + 0.0904454 * RGB[2]; RGBPrime[2] = -0.0096280 * RGB[0] - 0.0056980 * RGB[1] + 1.0153260 * RGB[2]; return RGBPrime; } private static double[] forwardPreAdaptationConeResponse(double[] XYZ) { double[] RGB = new double[3]; - RGB[0] = 0.7328 * XYZ[0] + 0.4296 * XYZ[1] - 0.1624 * XYZ[2]; + RGB[0] = 0.7328 * XYZ[0] + 0.4296 * XYZ[1] - 0.1624 * XYZ[2]; RGB[1] = -0.7036 * XYZ[0] + 1.6975 * XYZ[1] + 0.0061 * XYZ[2]; - RGB[2] = 0.0030 * XYZ[0] + 0.0136 * XYZ[1] + 0.9834 * XYZ[2]; + RGB[2] = 0.0030 * XYZ[0] + 0.0136 * XYZ[1] + 0.9834 * XYZ[2]; return RGB; } @@ -581,8 +606,8 @@ private static double[] forwardPreAdaptationConeResponse(double[] XYZ) { static final int VC_Z_W = 2; static final int VC_L_A = 3; static final int VC_Y_B = 4; - static final int VC_F = 5; - static final int VC_C = 6; + static final int VC_F = 5; + static final int VC_C = 6; static final int VC_N_C = 7; static final int VC_Z = 8; @@ -607,33 +632,33 @@ static double[] vc(double[] xyz_w, double L_A, double Y_b, double[] surrounding) vc[VC_N_C] = surrounding[SUR_N_C]; double[] RGB_w = forwardPreAdaptationConeResponse(xyz_w); - double D = Math.max(0.0, Math.min(1.0, vc[VC_F] * (1.0 - (1.0 / 3.6) * Math.pow(Math.E, (-L_A - 42.0) / 92.0)))); + double D = + Math.max(0.0, Math.min(1.0, vc[VC_F] * (1.0 - (1.0 / 3.6) * Math.pow(Math.E, (-L_A - 42.0) / 92.0)))); double Yw = xyz_w[1]; double[] RGB_c = new double[] { - (D * Yw / RGB_w[0]) + (1.0 - D), - (D * Yw / RGB_w[1]) + (1.0 - D), - (D * Yw / RGB_w[2]) + (1.0 - D), + (D * Yw / RGB_w[0]) + (1.0 - D), (D * Yw / RGB_w[1]) + (1.0 - D), (D * Yw / RGB_w[2]) + (1.0 - D), }; // calculate increase in brightness and colorfulness caused by brighter viewing environments double L_Ax5 = 5.0 * L_A; double k = 1.0 / (L_Ax5 + 1.0); double kpow4 = Math.pow(k, 4.0); - vc[VC_F_L] = 0.2 * kpow4 * (L_Ax5) + 0.1 * Math.pow(1.0 - kpow4, 2.0) * Math.pow(L_Ax5, 1.0/3.0); + vc[VC_F_L] = 0.2 * kpow4 * (L_Ax5) + 0.1 * Math.pow(1.0 - kpow4, 2.0) * Math.pow(L_Ax5, 1.0 / 3.0); // calculate response compression on J and C caused by background lightness. vc[VC_N] = Y_b / Yw; vc[VC_Z] = 1.48 + Math.sqrt(vc[VC_N]); vc[VC_N_BB] = 0.725 * Math.pow(1.0 / vc[VC_N], 0.2); - vc[VC_N_CB] = vc[VC_N_BB]; // chromatic contrast factors (calculate increase in J, Q, and C caused by dark backgrounds) + vc[VC_N_CB] = vc[ + VC_N_BB]; // chromatic contrast factors (calculate increase in J, Q, and C caused by dark backgrounds) // calculate achromatic response to white double[] RGB_wc = new double[] {RGB_c[0] * RGB_w[0], RGB_c[1] * RGB_w[1], RGB_c[2] * RGB_w[2]}; double[] RGBPrime_w = CAT02toHPE(RGB_wc); double[] RGBPrime_aw = new double[3]; - for(int channel = 0; channel < RGBPrime_w.length; channel++) { - if(RGBPrime_w[channel] >= 0) { + for (int channel = 0; channel < RGBPrime_w.length; channel++) { + if (RGBPrime_w[channel] >= 0) { double n = Math.pow(vc[VC_F_L] * RGBPrime_w[channel] / 100.0, 0.42); RGBPrime_aw[channel] = 400.0 * n / (n + 27.13) + 0.1; } else { @@ -660,7 +685,7 @@ private static double[] rgb2xyz(double[] rgb) { double x = vr * 0.4124564 + vg * 0.3575761 + vb * 0.1804375; double y = vr * 0.2126729 + vg * 0.7151522 + vb * 0.0721750; double z = vr * 0.0193339 + vg * 0.1191920 + vb * 0.9503041; - return new double[] { x, y, z }; + return new double[] {x, y, z}; } private static double pivotRgb(double n) { @@ -674,11 +699,12 @@ private static double[] xyz2lab(double[] xyz) { double l = 116.0 * fy - 16.0; double a = 500.0 * (fx - fy); double b = 200.0 * (fy - fz); - return new double[] { l, a, b }; + return new double[] {l, a, b}; } private static final double epsilon = 216.0 / 24389.0; private static final double kappa = 24389.0 / 27.0; + private static double pivotXyz(double n) { return n > epsilon ? Math.cbrt(n) : (kappa * n + 16) / 116; } @@ -686,5 +712,4 @@ private static double pivotXyz(double n) { private static double sqr(double n) { return n * n; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java index bdfd4c37bc2d8..b9ada27070577 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -29,8 +29,7 @@ public final class Curses { private static final int IFTE_THEN = 2; private static final int IFTE_ELSE = 3; - private Curses() { - } + private Curses() {} /** * Print the given terminal capabilities @@ -95,9 +94,9 @@ private static void doTputs(Appendable out, String str, Object... params) throws case 'n': out.append('\n'); break; -// case 'l': -// rawPrint('\l'); -// break; + // case 'l': + // rawPrint('\l'); + // break; case 'r': if (exec) { out.append('\r'); @@ -138,7 +137,7 @@ private static void doTputs(Appendable out, String str, Object... params) throws case '^': ch = str.charAt(index++); if (exec) { - out.append((char)(ch - '@')); + out.append((char) (ch - '@')); } break; case '%': @@ -195,7 +194,8 @@ private static void doTputs(Appendable out, String str, Object... params) throws break; case '{': int start = index; - while (str.charAt(index++) != '}') ; + while (str.charAt(index++) != '}') + ; if (exec) { int v = Integer.parseInt(str.substring(start, index - 1)); stack.push(v); @@ -364,10 +364,18 @@ private static void doTputs(Appendable out, String str, Object... params) throws int cnv; while ("-+# ".indexOf(ch) >= 0) { switch (ch) { - case '-': left = true; break; - case '+': plus = true; break; - case '#': alternate = true; break; - case ' ': space = true; break; + case '-': + left = true; + break; + case '+': + plus = true; + break; + case '#': + alternate = true; + break; + case ' ': + space = true; + break; } ch = str.charAt(index++); } @@ -473,5 +481,4 @@ private static int toInteger(Object pop) { return Integer.parseInt(pop.toString()); } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/DiffHelper.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/DiffHelper.java index 3852f572746ce..93b065395de77 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/DiffHelper.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/DiffHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -25,7 +25,9 @@ public class DiffHelper { * which means: delete "Hello", add "Goodbye" and keep " world." */ public enum Operation { - DELETE, INSERT, EQUAL + DELETE, + INSERT, + EQUAL } /** @@ -86,16 +88,13 @@ public static List diff(AttributedString text1, AttributedString text2) { && text1.charAt(commonStart) == text2.charAt(commonStart) && text1.styleAt(commonStart).equals(text2.styleAt(commonStart))) { if (text1.isHidden(commonStart)) { - if (startHiddenRange < 0) - startHiddenRange = commonStart; - } else - startHiddenRange = -1; + if (startHiddenRange < 0) startHiddenRange = commonStart; + } else startHiddenRange = -1; commonStart++; } if (startHiddenRange >= 0 - && ((l1 > commonStart && text1.isHidden(commonStart)) - || (l2 > commonStart && text2.isHidden(commonStart)))) - commonStart = startHiddenRange; + && ((l1 > commonStart && text1.isHidden(commonStart)) + || (l2 > commonStart && text2.isHidden(commonStart)))) commonStart = startHiddenRange; startHiddenRange = -1; int commonEnd = 0; @@ -103,32 +102,24 @@ public static List diff(AttributedString text1, AttributedString text2) { && text1.charAt(l1 - commonEnd - 1) == text2.charAt(l2 - commonEnd - 1) && text1.styleAt(l1 - commonEnd - 1).equals(text2.styleAt(l2 - commonEnd - 1))) { if (text1.isHidden(l1 - commonEnd - 1)) { - if (startHiddenRange < 0) - startHiddenRange = commonEnd; - } else - startHiddenRange = -1; + if (startHiddenRange < 0) startHiddenRange = commonEnd; + } else startHiddenRange = -1; commonEnd++; } - if (startHiddenRange >= 0) - commonEnd = startHiddenRange; + if (startHiddenRange >= 0) commonEnd = startHiddenRange; LinkedList diffs = new LinkedList<>(); if (commonStart > 0) { - diffs.add(new Diff(DiffHelper.Operation.EQUAL, - text1.subSequence(0, commonStart))); + diffs.add(new Diff(DiffHelper.Operation.EQUAL, text1.subSequence(0, commonStart))); } if (l2 > commonStart + commonEnd) { - diffs.add(new Diff(DiffHelper.Operation.INSERT, - text2.subSequence(commonStart, l2 - commonEnd))); + diffs.add(new Diff(DiffHelper.Operation.INSERT, text2.subSequence(commonStart, l2 - commonEnd))); } if (l1 > commonStart + commonEnd) { - diffs.add(new Diff(DiffHelper.Operation.DELETE, - text1.subSequence(commonStart, l1 - commonEnd))); + diffs.add(new Diff(DiffHelper.Operation.DELETE, text1.subSequence(commonStart, l1 - commonEnd))); } if (commonEnd > 0) { - diffs.add(new Diff(DiffHelper.Operation.EQUAL, - text1.subSequence(l1 - commonEnd, l1))); + diffs.add(new Diff(DiffHelper.Operation.EQUAL, text1.subSequence(l1 - commonEnd, l1))); } return diffs; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java index aaeab9b71ffe9..557024a121ed2 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2020, the original author or authors. + * Copyright (c) 2002-2020, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -37,8 +37,8 @@ public class Display { protected final boolean fullScreen; protected List oldLines = Collections.emptyList(); protected int cursorPos; - private int columns; - private int columns1; // columns+1 + protected int columns; + protected int columns1; // columns+1 protected int rows; protected boolean reset; protected boolean delayLineWrap; @@ -49,15 +49,15 @@ public class Display { protected final boolean delayedWrapAtEol; protected final boolean cursorDownIsNewLine; + @SuppressWarnings("this-escape") public Display(Terminal terminal, boolean fullscreen) { this.terminal = terminal; this.fullScreen = fullscreen; this.canScroll = can(Capability.insert_line, Capability.parm_insert_line) - && can(Capability.delete_line, Capability.parm_delete_line); + && can(Capability.delete_line, Capability.parm_delete_line); this.wrapAtEol = terminal.getBooleanCapability(Capability.auto_right_margin); - this.delayedWrapAtEol = this.wrapAtEol - && terminal.getBooleanCapability(Capability.eat_newline_glitch); + this.delayedWrapAtEol = this.wrapAtEol && terminal.getBooleanCapability(Capability.eat_newline_glitch); this.cursorDownIsNewLine = "\n".equals(Curses.tputs(terminal.getStringCapability(Capability.cursor_down))); } @@ -69,18 +69,22 @@ public Display(Terminal terminal, boolean fullscreen) { public boolean delayLineWrap() { return delayLineWrap; } - public void setDelayLineWrap(boolean v) { delayLineWrap = v; } + + public void setDelayLineWrap(boolean v) { + delayLineWrap = v; + } public void resize(int rows, int columns) { if (rows == 0 || columns == 0) { - columns = Integer.MAX_VALUE - 1; + columns = 1; rows = 1; } if (this.rows != rows || this.columns != columns) { this.rows = rows; this.columns = columns; this.columns1 = columns + 1; - oldLines = AttributedString.join(AttributedString.EMPTY, oldLines).columnSplitLength(columns, true, delayLineWrap()); + oldLines = AttributedString.join(AttributedString.EMPTY, oldLines) + .columnSplitLength(columns, true, delayLineWrap()); } } @@ -128,7 +132,8 @@ public void update(List newLines, int targetCursorPos, boolean // If dumb display, get rid of ansi sequences now Integer cols = terminal.getNumericCapability(Capability.max_colors); if (cols == null || cols < 8) { - newLines = newLines.stream().map(s -> new AttributedString(s.toString())) + newLines = newLines.stream() + .map(s -> new AttributedString(s.toString())) .collect(Collectors.toList()); } @@ -138,12 +143,13 @@ public void update(List newLines, int targetCursorPos, boolean int nbFooters = 0; // Find common headers and footers int l = newLines.size(); - while (nbHeaders < l - && Objects.equals(newLines.get(nbHeaders), oldLines.get(nbHeaders))) { + while (nbHeaders < l && Objects.equals(newLines.get(nbHeaders), oldLines.get(nbHeaders))) { nbHeaders++; } while (nbFooters < l - nbHeaders - 1 - && Objects.equals(newLines.get(newLines.size() - nbFooters - 1), oldLines.get(oldLines.size() - nbFooters - 1))) { + && Objects.equals( + newLines.get(newLines.size() - nbFooters - 1), + oldLines.get(oldLines.size() - nbFooters - 1))) { nbFooters++; } List o1 = newLines.subList(nbHeaders, newLines.size() - nbFooters); @@ -190,18 +196,14 @@ public void update(List newLines, int targetCursorPos, boolean int numLines = Math.min(rows, Math.max(oldLines.size(), newLines.size())); boolean wrapNeeded = false; while (lineIndex < numLines) { - AttributedString oldLine = - lineIndex < oldLines.size() ? oldLines.get(lineIndex) - : AttributedString.NEWLINE; - AttributedString newLine = - lineIndex < newLines.size() ? newLines.get(lineIndex) - : AttributedString.NEWLINE; + AttributedString oldLine = lineIndex < oldLines.size() ? oldLines.get(lineIndex) : AttributedString.NEWLINE; + AttributedString newLine = lineIndex < newLines.size() ? newLines.get(lineIndex) : AttributedString.NEWLINE; currentPos = lineIndex * columns1; int curCol = currentPos; int oldLength = oldLine.length(); int newLength = newLine.length(); - boolean oldNL = oldLength > 0 && oldLine.charAt(oldLength-1)=='\n'; - boolean newNL = newLength > 0 && newLine.charAt(newLength-1)=='\n'; + boolean oldNL = oldLength > 0 && oldLine.charAt(oldLength - 1) == '\n'; + boolean newNL = newLength > 0 && newLine.charAt(newLength - 1) == '\n'; if (oldNL) { oldLength--; oldLine = oldLine.substring(0, oldLength); @@ -210,9 +212,7 @@ public void update(List newLines, int targetCursorPos, boolean newLength--; newLine = newLine.substring(0, newLength); } - if (wrapNeeded - && lineIndex == (cursorPos + 1) / columns1 - && lineIndex < newLines.size()) { + if (wrapNeeded && lineIndex == (cursorPos + 1) / columns1 && lineIndex < newLines.size()) { // move from right margin to next line's left margin cursorPos++; if (newLength == 0 || newLine.isHidden(0)) { @@ -250,8 +250,7 @@ public void update(List newLines, int targetCursorPos, boolean } break; case INSERT: - if (i <= diffs.size() - 2 - && diffs.get(i + 1).operation == DiffHelper.Operation.EQUAL) { + if (i <= diffs.size() - 2 && diffs.get(i + 1).operation == DiffHelper.Operation.EQUAL) { cursorPos = moveVisualCursorTo(currentPos); if (insertChars(width)) { rawPrint(diff.text); @@ -282,8 +281,7 @@ public void update(List newLines, int targetCursorPos, boolean if (currentPos - curCol >= columns) { continue; } - if (i <= diffs.size() - 2 - && diffs.get(i + 1).operation == DiffHelper.Operation.EQUAL) { + if (i <= diffs.size() - 2 && diffs.get(i + 1).operation == DiffHelper.Operation.EQUAL) { if (currentPos + diffs.get(i + 1).text.columnLength() < columns) { moveVisualCursorTo(currentPos); if (deleteChars(width)) { @@ -305,25 +303,23 @@ public void update(List newLines, int targetCursorPos, boolean } } lineIndex++; - boolean newWrap = ! newNL && lineIndex < newLines.size(); - if (targetCursorPos + 1 == lineIndex * columns1 - && (newWrap || ! delayLineWrap)) - targetCursorPos++; + boolean newWrap = !newNL && lineIndex < newLines.size(); + if (targetCursorPos + 1 == lineIndex * columns1 && (newWrap || !delayLineWrap)) targetCursorPos++; boolean atRight = (cursorPos - curCol) % columns1 == columns; wrapNeeded = false; if (this.delayedWrapAtEol) { - boolean oldWrap = ! oldNL && lineIndex < oldLines.size(); - if (newWrap != oldWrap && ! (oldWrap && cleared)) { - moveVisualCursorTo(lineIndex*columns1-1, newLines); - if (newWrap) - wrapNeeded = true; - else - terminal.puts(Capability.clr_eol); + boolean oldWrap = !oldNL && lineIndex < oldLines.size(); + if (newWrap != oldWrap && !(oldWrap && cleared)) { + moveVisualCursorTo(lineIndex * columns1 - 1, newLines); + if (newWrap) wrapNeeded = true; + else terminal.puts(Capability.clr_eol); } } else if (atRight) { if (this.wrapAtEol) { - terminal.writer().write(" \b"); - cursorPos++; + if (!fullScreen || (fullScreen && lineIndex < numLines)) { + terminal.writer().write(" \b"); + cursorPos++; + } } else { terminal.puts(Capability.carriage_return); // CR / not newline. cursorPos = curCol; @@ -358,8 +354,7 @@ protected boolean deleteChars(int nb) { } protected boolean can(Capability single, Capability multi) { - return terminal.getStringCapability(single) != null - || terminal.getStringCapability(multi) != null; + return terminal.getStringCapability(single) != null || terminal.getStringCapability(multi) != null; } protected boolean perform(Capability single, Capability multi, int nb) { @@ -405,7 +400,7 @@ private static int[] longestCommon(List l1, List l1, List newLines) { + protected void moveVisualCursorTo(int targetPos, List newLines) { if (cursorPos != targetPos) { boolean atRight = (targetPos % columns1) == columns; moveVisualCursorTo(targetPos - (atRight ? 1 : 0)); @@ -422,12 +416,11 @@ protected void moveVisualCursorTo(int targetPos, // There is no portable way to move to the right margin // except by writing a character in the right-most column. int row = targetPos / columns1; - AttributedString lastChar = row >= newLines.size() ? AttributedString.EMPTY - : newLines.get(row).columnSubSequence(columns-1, columns); - if (lastChar.length() == 0) - rawPrint((int) ' '); - else - rawPrint(lastChar); + AttributedString lastChar = row >= newLines.size() + ? AttributedString.EMPTY + : newLines.get(row).columnSubSequence(columns - 1, columns); + if (lastChar.length() == 0) rawPrint((int) ' '); + else rawPrint(lastChar); cursorPos++; } } @@ -499,5 +492,4 @@ void rawPrint(AttributedString str) { public int wcwidth(String str) { return str != null ? AttributedString.fromAnsi(str).columnLength() : 0; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ExecHelper.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ExecHelper.java index 9fce26fcf357d..5d3c235661b4c 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ExecHelper.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ExecHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -22,8 +22,7 @@ */ public final class ExecHelper { - private ExecHelper() { - } + private ExecHelper() {} public static String exec(boolean redirectInput, final String... cmd) throws IOException { Objects.requireNonNull(cmd); @@ -31,7 +30,7 @@ public static String exec(boolean redirectInput, final String... cmd) throws IOE Log.trace("Running: ", cmd); ProcessBuilder pb = new ProcessBuilder(cmd); if (OSUtils.IS_AIX) { - Map env = pb.environment(); + Map env = pb.environment(); env.put("PATH", "/opt/freeware/bin:" + env.get("PATH")); env.put("LANG", "C"); env.put("LC_ALL", "C"); @@ -90,4 +89,3 @@ private static void close(final Closeable... closeables) { } } } - diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/FastBufferedOutputStream.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/FastBufferedOutputStream.java new file mode 100644 index 0000000000000..62c29c17f7816 --- /dev/null +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/FastBufferedOutputStream.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2009-2023, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package jdk.internal.org.jline.utils; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * A simple buffering output stream with no synchronization. + */ +public class FastBufferedOutputStream extends FilterOutputStream { + + protected final byte[] buf = new byte[8192]; + protected int count; + + public FastBufferedOutputStream(OutputStream out) { + super(out); + } + + @Override + public void write(int b) throws IOException { + if (count >= buf.length) { + flushBuffer(); + } + buf[count++] = (byte) b; + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + if (len >= buf.length) { + flushBuffer(); + out.write(b, off, len); + return; + } + if (len > buf.length - count) { + flushBuffer(); + } + System.arraycopy(b, off, buf, count, len); + count += len; + } + + private void flushBuffer() throws IOException { + if (count > 0) { + out.write(buf, 0, count); + count = 0; + } + } + + @Override + public void flush() throws IOException { + flushBuffer(); + out.flush(); + } +} diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java index 23a7607148683..32cf3f56318ab 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2019, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -28,476 +28,474 @@ public final class InfoCmp { private static final Map CAPS = new HashMap<>(); - private InfoCmp() { - } + private InfoCmp() {} @SuppressWarnings("unused") public enum Capability { - - auto_left_margin, // auto_left_margin, bw, bw - auto_right_margin, // auto_right_margin, am, am - back_color_erase, // back_color_erase, bce, ut - can_change, // can_change, ccc, cc - ceol_standout_glitch, // ceol_standout_glitch, xhp, xs - col_addr_glitch, // col_addr_glitch, xhpa, YA - cpi_changes_res, // cpi_changes_res, cpix, YF - cr_cancels_micro_mode, // cr_cancels_micro_mode, crxm, YB - dest_tabs_magic_smso, // dest_tabs_magic_smso, xt, xt - eat_newline_glitch, // eat_newline_glitch, xenl, xn - erase_overstrike, // erase_overstrike, eo, eo - generic_type, // generic_type, gn, gn - hard_copy, // hard_copy, hc, hc - hard_cursor, // hard_cursor, chts, HC - has_meta_key, // has_meta_key, km, km - has_print_wheel, // has_print_wheel, daisy, YC - has_status_line, // has_status_line, hs, hs - hue_lightness_saturation, // hue_lightness_saturation, hls, hl - insert_null_glitch, // insert_null_glitch, in, in - lpi_changes_res, // lpi_changes_res, lpix, YG - memory_above, // memory_above, da, da - memory_below, // memory_below, db, db - move_insert_mode, // move_insert_mode, mir, mi - move_standout_mode, // move_standout_mode, msgr, ms - needs_xon_xoff, // needs_xon_xoff, nxon, nx - no_esc_ctlc, // no_esc_ctlc, xsb, xb - no_pad_char, // no_pad_char, npc, NP - non_dest_scroll_region, // non_dest_scroll_region, ndscr, ND - non_rev_rmcup, // non_rev_rmcup, nrrmc, NR - over_strike, // over_strike, os, os - prtr_silent, // prtr_silent, mc5i, 5i - row_addr_glitch, // row_addr_glitch, xvpa, YD - semi_auto_right_margin, // semi_auto_right_margin, sam, YE - status_line_esc_ok, // status_line_esc_ok, eslok, es - tilde_glitch, // tilde_glitch, hz, hz - transparent_underline, // transparent_underline, ul, ul - xon_xoff, // xon_xoff, xon, xo - columns, // columns, cols, co - init_tabs, // init_tabs, it, it - label_height, // label_height, lh, lh - label_width, // label_width, lw, lw - lines, // lines, lines, li - lines_of_memory, // lines_of_memory, lm, lm - magic_cookie_glitch, // magic_cookie_glitch, xmc, sg - max_attributes, // max_attributes, ma, ma - max_colors, // max_colors, colors, Co - max_pairs, // max_pairs, pairs, pa - maximum_windows, // maximum_windows, wnum, MW - no_color_video, // no_color_video, ncv, NC - num_labels, // num_labels, nlab, Nl - padding_baud_rate, // padding_baud_rate, pb, pb - virtual_terminal, // virtual_terminal, vt, vt - width_status_line, // width_status_line, wsl, ws - bit_image_entwining, // bit_image_entwining, bitwin, Yo - bit_image_type, // bit_image_type, bitype, Yp - buffer_capacity, // buffer_capacity, bufsz, Ya - buttons, // buttons, btns, BT - dot_horz_spacing, // dot_horz_spacing, spinh, Yc - dot_vert_spacing, // dot_vert_spacing, spinv, Yb - max_micro_address, // max_micro_address, maddr, Yd - max_micro_jump, // max_micro_jump, mjump, Ye - micro_col_size, // micro_col_size, mcs, Yf - micro_line_size, // micro_line_size, mls, Yg - number_of_pins, // number_of_pins, npins, Yh - output_res_char, // output_res_char, orc, Yi - output_res_horz_inch, // output_res_horz_inch, orhi, Yk - output_res_line, // output_res_line, orl, Yj - output_res_vert_inch, // output_res_vert_inch, orvi, Yl - print_rate, // print_rate, cps, Ym - wide_char_size, // wide_char_size, widcs, Yn - acs_chars, // acs_chars, acsc, ac - back_tab, // back_tab, cbt, bt - bell, // bell, bel, bl - carriage_return, // carriage_return, cr, cr - change_char_pitch, // change_char_pitch, cpi, ZA - change_line_pitch, // change_line_pitch, lpi, ZB - change_res_horz, // change_res_horz, chr, ZC - change_res_vert, // change_res_vert, cvr, ZD - change_scroll_region, // change_scroll_region, csr, cs - char_padding, // char_padding, rmp, rP - clear_all_tabs, // clear_all_tabs, tbc, ct - clear_margins, // clear_margins, mgc, MC - clear_screen, // clear_screen, clear, cl - clr_bol, // clr_bol, el1, cb - clr_eol, // clr_eol, el, ce - clr_eos, // clr_eos, ed, cd - column_address, // column_address, hpa, ch - command_character, // command_character, cmdch, CC - create_window, // create_window, cwin, CW - cursor_address, // cursor_address, cup, cm - cursor_down, // cursor_down, cud1, do - cursor_home, // cursor_home, home, ho - cursor_invisible, // cursor_invisible, civis, vi - cursor_left, // cursor_left, cub1, le - cursor_mem_address, // cursor_mem_address, mrcup, CM - cursor_normal, // cursor_normal, cnorm, ve - cursor_right, // cursor_right, cuf1, nd - cursor_to_ll, // cursor_to_ll, ll, ll - cursor_up, // cursor_up, cuu1, up - cursor_visible, // cursor_visible, cvvis, vs - define_char, // define_char, defc, ZE - delete_character, // delete_character, dch1, dc - delete_line, // delete_line, dl1, dl - dial_phone, // dial_phone, dial, DI - dis_status_line, // dis_status_line, dsl, ds - display_clock, // display_clock, dclk, DK - down_half_line, // down_half_line, hd, hd - ena_acs, // ena_acs, enacs, eA - enter_alt_charset_mode, // enter_alt_charset_mode, smacs, as - enter_am_mode, // enter_am_mode, smam, SA - enter_blink_mode, // enter_blink_mode, blink, mb - enter_bold_mode, // enter_bold_mode, bold, md - enter_ca_mode, // enter_ca_mode, smcup, ti - enter_delete_mode, // enter_delete_mode, smdc, dm - enter_dim_mode, // enter_dim_mode, dim, mh - enter_doublewide_mode, // enter_doublewide_mode, swidm, ZF - enter_draft_quality, // enter_draft_quality, sdrfq, ZG - enter_insert_mode, // enter_insert_mode, smir, im - enter_italics_mode, // enter_italics_mode, sitm, ZH - enter_leftward_mode, // enter_leftward_mode, slm, ZI - enter_micro_mode, // enter_micro_mode, smicm, ZJ - enter_near_letter_quality, // enter_near_letter_quality, snlq, ZK - enter_normal_quality, // enter_normal_quality, snrmq, ZL - enter_protected_mode, // enter_protected_mode, prot, mp - enter_reverse_mode, // enter_reverse_mode, rev, mr - enter_secure_mode, // enter_secure_mode, invis, mk - enter_shadow_mode, // enter_shadow_mode, sshm, ZM - enter_standout_mode, // enter_standout_mode, smso, so - enter_subscript_mode, // enter_subscript_mode, ssubm, ZN - enter_superscript_mode, // enter_superscript_mode, ssupm, ZO - enter_underline_mode, // enter_underline_mode, smul, us - enter_upward_mode, // enter_upward_mode, sum, ZP - enter_xon_mode, // enter_xon_mode, smxon, SX - erase_chars, // erase_chars, ech, ec - exit_alt_charset_mode, // exit_alt_charset_mode, rmacs, ae - exit_am_mode, // exit_am_mode, rmam, RA - exit_attribute_mode, // exit_attribute_mode, sgr0, me - exit_ca_mode, // exit_ca_mode, rmcup, te - exit_delete_mode, // exit_delete_mode, rmdc, ed - exit_doublewide_mode, // exit_doublewide_mode, rwidm, ZQ - exit_insert_mode, // exit_insert_mode, rmir, ei - exit_italics_mode, // exit_italics_mode, ritm, ZR - exit_leftward_mode, // exit_leftward_mode, rlm, ZS - exit_micro_mode, // exit_micro_mode, rmicm, ZT - exit_shadow_mode, // exit_shadow_mode, rshm, ZU - exit_standout_mode, // exit_standout_mode, rmso, se - exit_subscript_mode, // exit_subscript_mode, rsubm, ZV - exit_superscript_mode, // exit_superscript_mode, rsupm, ZW - exit_underline_mode, // exit_underline_mode, rmul, ue - exit_upward_mode, // exit_upward_mode, rum, ZX - exit_xon_mode, // exit_xon_mode, rmxon, RX - fixed_pause, // fixed_pause, pause, PA - flash_hook, // flash_hook, hook, fh - flash_screen, // flash_screen, flash, vb - form_feed, // form_feed, ff, ff - from_status_line, // from_status_line, fsl, fs - goto_window, // goto_window, wingo, WG - hangup, // hangup, hup, HU - init_1string, // init_1string, is1, i1 - init_2string, // init_2string, is2, is - init_3string, // init_3string, is3, i3 - init_file, // init_file, if, if - init_prog, // init_prog, iprog, iP - initialize_color, // initialize_color, initc, Ic - initialize_pair, // initialize_pair, initp, Ip - insert_character, // insert_character, ich1, ic - insert_line, // insert_line, il1, al - insert_padding, // insert_padding, ip, ip - key_a1, // key_a1, ka1, K1 - key_a3, // key_a3, ka3, K3 - key_b2, // key_b2, kb2, K2 - key_backspace, // key_backspace, kbs, kb - key_beg, // key_beg, kbeg, @1 - key_btab, // key_btab, kcbt, kB - key_c1, // key_c1, kc1, K4 - key_c3, // key_c3, kc3, K5 - key_cancel, // key_cancel, kcan, @2 - key_catab, // key_catab, ktbc, ka - key_clear, // key_clear, kclr, kC - key_close, // key_close, kclo, @3 - key_command, // key_command, kcmd, @4 - key_copy, // key_copy, kcpy, @5 - key_create, // key_create, kcrt, @6 - key_ctab, // key_ctab, kctab, kt - key_dc, // key_dc, kdch1, kD - key_dl, // key_dl, kdl1, kL - key_down, // key_down, kcud1, kd - key_eic, // key_eic, krmir, kM - key_end, // key_end, kend, @7 - key_enter, // key_enter, kent, @8 - key_eol, // key_eol, kel, kE - key_eos, // key_eos, ked, kS - key_exit, // key_exit, kext, @9 - key_f0, // key_f0, kf0, k0 - key_f1, // key_f1, kf1, k1 - key_f10, // key_f10, kf10, k; - key_f11, // key_f11, kf11, F1 - key_f12, // key_f12, kf12, F2 - key_f13, // key_f13, kf13, F3 - key_f14, // key_f14, kf14, F4 - key_f15, // key_f15, kf15, F5 - key_f16, // key_f16, kf16, F6 - key_f17, // key_f17, kf17, F7 - key_f18, // key_f18, kf18, F8 - key_f19, // key_f19, kf19, F9 - key_f2, // key_f2, kf2, k2 - key_f20, // key_f20, kf20, FA - key_f21, // key_f21, kf21, FB - key_f22, // key_f22, kf22, FC - key_f23, // key_f23, kf23, FD - key_f24, // key_f24, kf24, FE - key_f25, // key_f25, kf25, FF - key_f26, // key_f26, kf26, FG - key_f27, // key_f27, kf27, FH - key_f28, // key_f28, kf28, FI - key_f29, // key_f29, kf29, FJ - key_f3, // key_f3, kf3, k3 - key_f30, // key_f30, kf30, FK - key_f31, // key_f31, kf31, FL - key_f32, // key_f32, kf32, FM - key_f33, // key_f33, kf33, FN - key_f34, // key_f34, kf34, FO - key_f35, // key_f35, kf35, FP - key_f36, // key_f36, kf36, FQ - key_f37, // key_f37, kf37, FR - key_f38, // key_f38, kf38, FS - key_f39, // key_f39, kf39, FT - key_f4, // key_f4, kf4, k4 - key_f40, // key_f40, kf40, FU - key_f41, // key_f41, kf41, FV - key_f42, // key_f42, kf42, FW - key_f43, // key_f43, kf43, FX - key_f44, // key_f44, kf44, FY - key_f45, // key_f45, kf45, FZ - key_f46, // key_f46, kf46, Fa - key_f47, // key_f47, kf47, Fb - key_f48, // key_f48, kf48, Fc - key_f49, // key_f49, kf49, Fd - key_f5, // key_f5, kf5, k5 - key_f50, // key_f50, kf50, Fe - key_f51, // key_f51, kf51, Ff - key_f52, // key_f52, kf52, Fg - key_f53, // key_f53, kf53, Fh - key_f54, // key_f54, kf54, Fi - key_f55, // key_f55, kf55, Fj - key_f56, // key_f56, kf56, Fk - key_f57, // key_f57, kf57, Fl - key_f58, // key_f58, kf58, Fm - key_f59, // key_f59, kf59, Fn - key_f6, // key_f6, kf6, k6 - key_f60, // key_f60, kf60, Fo - key_f61, // key_f61, kf61, Fp - key_f62, // key_f62, kf62, Fq - key_f63, // key_f63, kf63, Fr - key_f7, // key_f7, kf7, k7 - key_f8, // key_f8, kf8, k8 - key_f9, // key_f9, kf9, k9 - key_find, // key_find, kfnd, @0 - key_help, // key_help, khlp, %1 - key_home, // key_home, khome, kh - key_ic, // key_ic, kich1, kI - key_il, // key_il, kil1, kA - key_left, // key_left, kcub1, kl - key_ll, // key_ll, kll, kH - key_mark, // key_mark, kmrk, %2 - key_message, // key_message, kmsg, %3 - key_move, // key_move, kmov, %4 - key_next, // key_next, knxt, %5 - key_npage, // key_npage, knp, kN - key_open, // key_open, kopn, %6 - key_options, // key_options, kopt, %7 - key_ppage, // key_ppage, kpp, kP - key_previous, // key_previous, kprv, %8 - key_print, // key_print, kprt, %9 - key_redo, // key_redo, krdo, %0 - key_reference, // key_reference, kref, &1 - key_refresh, // key_refresh, krfr, &2 - key_replace, // key_replace, krpl, &3 - key_restart, // key_restart, krst, &4 - key_resume, // key_resume, kres, &5 - key_right, // key_right, kcuf1, kr - key_save, // key_save, ksav, &6 - key_sbeg, // key_sbeg, kBEG, &9 - key_scancel, // key_scancel, kCAN, &0 - key_scommand, // key_scommand, kCMD, *1 - key_scopy, // key_scopy, kCPY, *2 - key_screate, // key_screate, kCRT, *3 - key_sdc, // key_sdc, kDC, *4 - key_sdl, // key_sdl, kDL, *5 - key_select, // key_select, kslt, *6 - key_send, // key_send, kEND, *7 - key_seol, // key_seol, kEOL, *8 - key_sexit, // key_sexit, kEXT, *9 - key_sf, // key_sf, kind, kF - key_sfind, // key_sfind, kFND, *0 - key_shelp, // key_shelp, kHLP, #1 - key_shome, // key_shome, kHOM, #2 - key_sic, // key_sic, kIC, #3 - key_sleft, // key_sleft, kLFT, #4 - key_smessage, // key_smessage, kMSG, %a - key_smove, // key_smove, kMOV, %b - key_snext, // key_snext, kNXT, %c - key_soptions, // key_soptions, kOPT, %d - key_sprevious, // key_sprevious, kPRV, %e - key_sprint, // key_sprint, kPRT, %f - key_sr, // key_sr, kri, kR - key_sredo, // key_sredo, kRDO, %g - key_sreplace, // key_sreplace, kRPL, %h - key_sright, // key_sright, kRIT, %i - key_srsume, // key_srsume, kRES, %j - key_ssave, // key_ssave, kSAV, !1 - key_ssuspend, // key_ssuspend, kSPD, !2 - key_stab, // key_stab, khts, kT - key_sundo, // key_sundo, kUND, !3 - key_suspend, // key_suspend, kspd, &7 - key_undo, // key_undo, kund, &8 - key_up, // key_up, kcuu1, ku - keypad_local, // keypad_local, rmkx, ke - keypad_xmit, // keypad_xmit, smkx, ks - lab_f0, // lab_f0, lf0, l0 - lab_f1, // lab_f1, lf1, l1 - lab_f10, // lab_f10, lf10, la - lab_f2, // lab_f2, lf2, l2 - lab_f3, // lab_f3, lf3, l3 - lab_f4, // lab_f4, lf4, l4 - lab_f5, // lab_f5, lf5, l5 - lab_f6, // lab_f6, lf6, l6 - lab_f7, // lab_f7, lf7, l7 - lab_f8, // lab_f8, lf8, l8 - lab_f9, // lab_f9, lf9, l9 - label_format, // label_format, fln, Lf - label_off, // label_off, rmln, LF - label_on, // label_on, smln, LO - meta_off, // meta_off, rmm, mo - meta_on, // meta_on, smm, mm - micro_column_address, // micro_column_address, mhpa, ZY - micro_down, // micro_down, mcud1, ZZ - micro_left, // micro_left, mcub1, Za - micro_right, // micro_right, mcuf1, Zb - micro_row_address, // micro_row_address, mvpa, Zc - micro_up, // micro_up, mcuu1, Zd - newline, // newline, nel, nw - order_of_pins, // order_of_pins, porder, Ze - orig_colors, // orig_colors, oc, oc - orig_pair, // orig_pair, op, op - pad_char, // pad_char, pad, pc - parm_dch, // parm_dch, dch, DC - parm_delete_line, // parm_delete_line, dl, DL - parm_down_cursor, // parm_down_cursor, cud, DO - parm_down_micro, // parm_down_micro, mcud, Zf - parm_ich, // parm_ich, ich, IC - parm_index, // parm_index, indn, SF - parm_insert_line, // parm_insert_line, il, AL - parm_left_cursor, // parm_left_cursor, cub, LE - parm_left_micro, // parm_left_micro, mcub, Zg - parm_right_cursor, // parm_right_cursor, cuf, RI - parm_right_micro, // parm_right_micro, mcuf, Zh - parm_rindex, // parm_rindex, rin, SR - parm_up_cursor, // parm_up_cursor, cuu, UP - parm_up_micro, // parm_up_micro, mcuu, Zi - pkey_key, // pkey_key, pfkey, pk - pkey_local, // pkey_local, pfloc, pl - pkey_xmit, // pkey_xmit, pfx, px - plab_norm, // plab_norm, pln, pn - print_screen, // print_screen, mc0, ps - prtr_non, // prtr_non, mc5p, pO - prtr_off, // prtr_off, mc4, pf - prtr_on, // prtr_on, mc5, po - pulse, // pulse, pulse, PU - quick_dial, // quick_dial, qdial, QD - remove_clock, // remove_clock, rmclk, RC - repeat_char, // repeat_char, rep, rp - req_for_input, // req_for_input, rfi, RF - reset_1string, // reset_1string, rs1, r1 - reset_2string, // reset_2string, rs2, r2 - reset_3string, // reset_3string, rs3, r3 - reset_file, // reset_file, rf, rf - restore_cursor, // restore_cursor, rc, rc - row_address, // row_address, vpa, cv - save_cursor, // save_cursor, sc, sc - scroll_forward, // scroll_forward, ind, sf - scroll_reverse, // scroll_reverse, ri, sr - select_char_set, // select_char_set, scs, Zj - set_attributes, // set_attributes, sgr, sa - set_background, // set_background, setb, Sb - set_bottom_margin, // set_bottom_margin, smgb, Zk - set_bottom_margin_parm, // set_bottom_margin_parm, smgbp, Zl - set_clock, // set_clock, sclk, SC - set_color_pair, // set_color_pair, scp, sp - set_foreground, // set_foreground, setf, Sf - set_left_margin, // set_left_margin, smgl, ML - set_left_margin_parm, // set_left_margin_parm, smglp, Zm - set_right_margin, // set_right_margin, smgr, MR - set_right_margin_parm, // set_right_margin_parm, smgrp, Zn - set_tab, // set_tab, hts, st - set_top_margin, // set_top_margin, smgt, Zo - set_top_margin_parm, // set_top_margin_parm, smgtp, Zp - set_window, // set_window, wind, wi - start_bit_image, // start_bit_image, sbim, Zq - start_char_set_def, // start_char_set_def, scsd, Zr - stop_bit_image, // stop_bit_image, rbim, Zs - stop_char_set_def, // stop_char_set_def, rcsd, Zt - subscript_characters, // subscript_characters, subcs, Zu - superscript_characters, // superscript_characters, supcs, Zv - tab, // tab, ht, ta - these_cause_cr, // these_cause_cr, docr, Zw - to_status_line, // to_status_line, tsl, ts - tone, // tone, tone, TO - underline_char, // underline_char, uc, uc - up_half_line, // up_half_line, hu, hu - user0, // user0, u0, u0 - user1, // user1, u1, u1 - user2, // user2, u2, u2 - user3, // user3, u3, u3 - user4, // user4, u4, u4 - user5, // user5, u5, u5 - user6, // user6, u6, u6 - user7, // user7, u7, u7 - user8, // user8, u8, u8 - user9, // user9, u9, u9 - wait_tone, // wait_tone, wait, WA - xoff_character, // xoff_character, xoffc, XF - xon_character, // xon_character, xonc, XN - zero_motion, // zero_motion, zerom, Zx - alt_scancode_esc, // alt_scancode_esc, scesa, S8 - bit_image_carriage_return, // bit_image_carriage_return, bicr, Yv - bit_image_newline, // bit_image_newline, binel, Zz - bit_image_repeat, // bit_image_repeat, birep, Xy - char_set_names, // char_set_names, csnm, Zy - code_set_init, // code_set_init, csin, ci - color_names, // color_names, colornm, Yw - define_bit_image_region, // define_bit_image_region, defbi, Yx - device_type, // device_type, devt, dv - display_pc_char, // display_pc_char, dispc, S1 - end_bit_image_region, // end_bit_image_region, endbi, Yy - enter_pc_charset_mode, // enter_pc_charset_mode, smpch, S2 - enter_scancode_mode, // enter_scancode_mode, smsc, S4 - exit_pc_charset_mode, // exit_pc_charset_mode, rmpch, S3 - exit_scancode_mode, // exit_scancode_mode, rmsc, S5 - get_mouse, // get_mouse, getm, Gm - key_mouse, // key_mouse, kmous, Km - mouse_info, // mouse_info, minfo, Mi - pc_term_options, // pc_term_options, pctrm, S6 - pkey_plab, // pkey_plab, pfxl, xl - req_mouse_pos, // req_mouse_pos, reqmp, RQ - scancode_escape, // scancode_escape, scesc, S7 - set0_des_seq, // set0_des_seq, s0ds, s0 - set1_des_seq, // set1_des_seq, s1ds, s1 - set2_des_seq, // set2_des_seq, s2ds, s2 - set3_des_seq, // set3_des_seq, s3ds, s3 - set_a_background, // set_a_background, setab, AB - set_a_foreground, // set_a_foreground, setaf, AF - set_color_band, // set_color_band, setcolor, Yz - set_lr_margin, // set_lr_margin, smglr, ML - set_page_length, // set_page_length, slines, YZ - set_tb_margin, // set_tb_margin, smgtb, MT - enter_horizontal_hl_mode, // enter_horizontal_hl_mode, ehhlm, Xh - enter_left_hl_mode, // enter_left_hl_mode, elhlm, Xl - enter_low_hl_mode, // enter_low_hl_mode, elohlm, Xo - enter_right_hl_mode, // enter_right_hl_mode, erhlm, Xr - enter_top_hl_mode, // enter_top_hl_mode, ethlm, Xt - enter_vertical_hl_mode, // enter_vertical_hl_mode, evhlm, Xv - set_a_attributes, // set_a_attributes, sgr1, sA - set_pglen_inch, // set_pglen_inch, slength, sL) + auto_left_margin, // auto_left_margin, bw, bw + auto_right_margin, // auto_right_margin, am, am + back_color_erase, // back_color_erase, bce, ut + can_change, // can_change, ccc, cc + ceol_standout_glitch, // ceol_standout_glitch, xhp, xs + col_addr_glitch, // col_addr_glitch, xhpa, YA + cpi_changes_res, // cpi_changes_res, cpix, YF + cr_cancels_micro_mode, // cr_cancels_micro_mode, crxm, YB + dest_tabs_magic_smso, // dest_tabs_magic_smso, xt, xt + eat_newline_glitch, // eat_newline_glitch, xenl, xn + erase_overstrike, // erase_overstrike, eo, eo + generic_type, // generic_type, gn, gn + hard_copy, // hard_copy, hc, hc + hard_cursor, // hard_cursor, chts, HC + has_meta_key, // has_meta_key, km, km + has_print_wheel, // has_print_wheel, daisy, YC + has_status_line, // has_status_line, hs, hs + hue_lightness_saturation, // hue_lightness_saturation, hls, hl + insert_null_glitch, // insert_null_glitch, in, in + lpi_changes_res, // lpi_changes_res, lpix, YG + memory_above, // memory_above, da, da + memory_below, // memory_below, db, db + move_insert_mode, // move_insert_mode, mir, mi + move_standout_mode, // move_standout_mode, msgr, ms + needs_xon_xoff, // needs_xon_xoff, nxon, nx + no_esc_ctlc, // no_esc_ctlc, xsb, xb + no_pad_char, // no_pad_char, npc, NP + non_dest_scroll_region, // non_dest_scroll_region, ndscr, ND + non_rev_rmcup, // non_rev_rmcup, nrrmc, NR + over_strike, // over_strike, os, os + prtr_silent, // prtr_silent, mc5i, 5i + row_addr_glitch, // row_addr_glitch, xvpa, YD + semi_auto_right_margin, // semi_auto_right_margin, sam, YE + status_line_esc_ok, // status_line_esc_ok, eslok, es + tilde_glitch, // tilde_glitch, hz, hz + transparent_underline, // transparent_underline, ul, ul + xon_xoff, // xon_xoff, xon, xo + columns, // columns, cols, co + init_tabs, // init_tabs, it, it + label_height, // label_height, lh, lh + label_width, // label_width, lw, lw + lines, // lines, lines, li + lines_of_memory, // lines_of_memory, lm, lm + magic_cookie_glitch, // magic_cookie_glitch, xmc, sg + max_attributes, // max_attributes, ma, ma + max_colors, // max_colors, colors, Co + max_pairs, // max_pairs, pairs, pa + maximum_windows, // maximum_windows, wnum, MW + no_color_video, // no_color_video, ncv, NC + num_labels, // num_labels, nlab, Nl + padding_baud_rate, // padding_baud_rate, pb, pb + virtual_terminal, // virtual_terminal, vt, vt + width_status_line, // width_status_line, wsl, ws + bit_image_entwining, // bit_image_entwining, bitwin, Yo + bit_image_type, // bit_image_type, bitype, Yp + buffer_capacity, // buffer_capacity, bufsz, Ya + buttons, // buttons, btns, BT + dot_horz_spacing, // dot_horz_spacing, spinh, Yc + dot_vert_spacing, // dot_vert_spacing, spinv, Yb + max_micro_address, // max_micro_address, maddr, Yd + max_micro_jump, // max_micro_jump, mjump, Ye + micro_col_size, // micro_col_size, mcs, Yf + micro_line_size, // micro_line_size, mls, Yg + number_of_pins, // number_of_pins, npins, Yh + output_res_char, // output_res_char, orc, Yi + output_res_horz_inch, // output_res_horz_inch, orhi, Yk + output_res_line, // output_res_line, orl, Yj + output_res_vert_inch, // output_res_vert_inch, orvi, Yl + print_rate, // print_rate, cps, Ym + wide_char_size, // wide_char_size, widcs, Yn + acs_chars, // acs_chars, acsc, ac + back_tab, // back_tab, cbt, bt + bell, // bell, bel, bl + carriage_return, // carriage_return, cr, cr + change_char_pitch, // change_char_pitch, cpi, ZA + change_line_pitch, // change_line_pitch, lpi, ZB + change_res_horz, // change_res_horz, chr, ZC + change_res_vert, // change_res_vert, cvr, ZD + change_scroll_region, // change_scroll_region, csr, cs + char_padding, // char_padding, rmp, rP + clear_all_tabs, // clear_all_tabs, tbc, ct + clear_margins, // clear_margins, mgc, MC + clear_screen, // clear_screen, clear, cl + clr_bol, // clr_bol, el1, cb + clr_eol, // clr_eol, el, ce + clr_eos, // clr_eos, ed, cd + column_address, // column_address, hpa, ch + command_character, // command_character, cmdch, CC + create_window, // create_window, cwin, CW + cursor_address, // cursor_address, cup, cm + cursor_down, // cursor_down, cud1, do + cursor_home, // cursor_home, home, ho + cursor_invisible, // cursor_invisible, civis, vi + cursor_left, // cursor_left, cub1, le + cursor_mem_address, // cursor_mem_address, mrcup, CM + cursor_normal, // cursor_normal, cnorm, ve + cursor_right, // cursor_right, cuf1, nd + cursor_to_ll, // cursor_to_ll, ll, ll + cursor_up, // cursor_up, cuu1, up + cursor_visible, // cursor_visible, cvvis, vs + define_char, // define_char, defc, ZE + delete_character, // delete_character, dch1, dc + delete_line, // delete_line, dl1, dl + dial_phone, // dial_phone, dial, DI + dis_status_line, // dis_status_line, dsl, ds + display_clock, // display_clock, dclk, DK + down_half_line, // down_half_line, hd, hd + ena_acs, // ena_acs, enacs, eA + enter_alt_charset_mode, // enter_alt_charset_mode, smacs, as + enter_am_mode, // enter_am_mode, smam, SA + enter_blink_mode, // enter_blink_mode, blink, mb + enter_bold_mode, // enter_bold_mode, bold, md + enter_ca_mode, // enter_ca_mode, smcup, ti + enter_delete_mode, // enter_delete_mode, smdc, dm + enter_dim_mode, // enter_dim_mode, dim, mh + enter_doublewide_mode, // enter_doublewide_mode, swidm, ZF + enter_draft_quality, // enter_draft_quality, sdrfq, ZG + enter_insert_mode, // enter_insert_mode, smir, im + enter_italics_mode, // enter_italics_mode, sitm, ZH + enter_leftward_mode, // enter_leftward_mode, slm, ZI + enter_micro_mode, // enter_micro_mode, smicm, ZJ + enter_near_letter_quality, // enter_near_letter_quality, snlq, ZK + enter_normal_quality, // enter_normal_quality, snrmq, ZL + enter_protected_mode, // enter_protected_mode, prot, mp + enter_reverse_mode, // enter_reverse_mode, rev, mr + enter_secure_mode, // enter_secure_mode, invis, mk + enter_shadow_mode, // enter_shadow_mode, sshm, ZM + enter_standout_mode, // enter_standout_mode, smso, so + enter_subscript_mode, // enter_subscript_mode, ssubm, ZN + enter_superscript_mode, // enter_superscript_mode, ssupm, ZO + enter_underline_mode, // enter_underline_mode, smul, us + enter_upward_mode, // enter_upward_mode, sum, ZP + enter_xon_mode, // enter_xon_mode, smxon, SX + erase_chars, // erase_chars, ech, ec + exit_alt_charset_mode, // exit_alt_charset_mode, rmacs, ae + exit_am_mode, // exit_am_mode, rmam, RA + exit_attribute_mode, // exit_attribute_mode, sgr0, me + exit_ca_mode, // exit_ca_mode, rmcup, te + exit_delete_mode, // exit_delete_mode, rmdc, ed + exit_doublewide_mode, // exit_doublewide_mode, rwidm, ZQ + exit_insert_mode, // exit_insert_mode, rmir, ei + exit_italics_mode, // exit_italics_mode, ritm, ZR + exit_leftward_mode, // exit_leftward_mode, rlm, ZS + exit_micro_mode, // exit_micro_mode, rmicm, ZT + exit_shadow_mode, // exit_shadow_mode, rshm, ZU + exit_standout_mode, // exit_standout_mode, rmso, se + exit_subscript_mode, // exit_subscript_mode, rsubm, ZV + exit_superscript_mode, // exit_superscript_mode, rsupm, ZW + exit_underline_mode, // exit_underline_mode, rmul, ue + exit_upward_mode, // exit_upward_mode, rum, ZX + exit_xon_mode, // exit_xon_mode, rmxon, RX + fixed_pause, // fixed_pause, pause, PA + flash_hook, // flash_hook, hook, fh + flash_screen, // flash_screen, flash, vb + form_feed, // form_feed, ff, ff + from_status_line, // from_status_line, fsl, fs + goto_window, // goto_window, wingo, WG + hangup, // hangup, hup, HU + init_1string, // init_1string, is1, i1 + init_2string, // init_2string, is2, is + init_3string, // init_3string, is3, i3 + init_file, // init_file, if, if + init_prog, // init_prog, iprog, iP + initialize_color, // initialize_color, initc, Ic + initialize_pair, // initialize_pair, initp, Ip + insert_character, // insert_character, ich1, ic + insert_line, // insert_line, il1, al + insert_padding, // insert_padding, ip, ip + key_a1, // key_a1, ka1, K1 + key_a3, // key_a3, ka3, K3 + key_b2, // key_b2, kb2, K2 + key_backspace, // key_backspace, kbs, kb + key_beg, // key_beg, kbeg, @1 + key_btab, // key_btab, kcbt, kB + key_c1, // key_c1, kc1, K4 + key_c3, // key_c3, kc3, K5 + key_cancel, // key_cancel, kcan, @2 + key_catab, // key_catab, ktbc, ka + key_clear, // key_clear, kclr, kC + key_close, // key_close, kclo, @3 + key_command, // key_command, kcmd, @4 + key_copy, // key_copy, kcpy, @5 + key_create, // key_create, kcrt, @6 + key_ctab, // key_ctab, kctab, kt + key_dc, // key_dc, kdch1, kD + key_dl, // key_dl, kdl1, kL + key_down, // key_down, kcud1, kd + key_eic, // key_eic, krmir, kM + key_end, // key_end, kend, @7 + key_enter, // key_enter, kent, @8 + key_eol, // key_eol, kel, kE + key_eos, // key_eos, ked, kS + key_exit, // key_exit, kext, @9 + key_f0, // key_f0, kf0, k0 + key_f1, // key_f1, kf1, k1 + key_f10, // key_f10, kf10, k; + key_f11, // key_f11, kf11, F1 + key_f12, // key_f12, kf12, F2 + key_f13, // key_f13, kf13, F3 + key_f14, // key_f14, kf14, F4 + key_f15, // key_f15, kf15, F5 + key_f16, // key_f16, kf16, F6 + key_f17, // key_f17, kf17, F7 + key_f18, // key_f18, kf18, F8 + key_f19, // key_f19, kf19, F9 + key_f2, // key_f2, kf2, k2 + key_f20, // key_f20, kf20, FA + key_f21, // key_f21, kf21, FB + key_f22, // key_f22, kf22, FC + key_f23, // key_f23, kf23, FD + key_f24, // key_f24, kf24, FE + key_f25, // key_f25, kf25, FF + key_f26, // key_f26, kf26, FG + key_f27, // key_f27, kf27, FH + key_f28, // key_f28, kf28, FI + key_f29, // key_f29, kf29, FJ + key_f3, // key_f3, kf3, k3 + key_f30, // key_f30, kf30, FK + key_f31, // key_f31, kf31, FL + key_f32, // key_f32, kf32, FM + key_f33, // key_f33, kf33, FN + key_f34, // key_f34, kf34, FO + key_f35, // key_f35, kf35, FP + key_f36, // key_f36, kf36, FQ + key_f37, // key_f37, kf37, FR + key_f38, // key_f38, kf38, FS + key_f39, // key_f39, kf39, FT + key_f4, // key_f4, kf4, k4 + key_f40, // key_f40, kf40, FU + key_f41, // key_f41, kf41, FV + key_f42, // key_f42, kf42, FW + key_f43, // key_f43, kf43, FX + key_f44, // key_f44, kf44, FY + key_f45, // key_f45, kf45, FZ + key_f46, // key_f46, kf46, Fa + key_f47, // key_f47, kf47, Fb + key_f48, // key_f48, kf48, Fc + key_f49, // key_f49, kf49, Fd + key_f5, // key_f5, kf5, k5 + key_f50, // key_f50, kf50, Fe + key_f51, // key_f51, kf51, Ff + key_f52, // key_f52, kf52, Fg + key_f53, // key_f53, kf53, Fh + key_f54, // key_f54, kf54, Fi + key_f55, // key_f55, kf55, Fj + key_f56, // key_f56, kf56, Fk + key_f57, // key_f57, kf57, Fl + key_f58, // key_f58, kf58, Fm + key_f59, // key_f59, kf59, Fn + key_f6, // key_f6, kf6, k6 + key_f60, // key_f60, kf60, Fo + key_f61, // key_f61, kf61, Fp + key_f62, // key_f62, kf62, Fq + key_f63, // key_f63, kf63, Fr + key_f7, // key_f7, kf7, k7 + key_f8, // key_f8, kf8, k8 + key_f9, // key_f9, kf9, k9 + key_find, // key_find, kfnd, @0 + key_help, // key_help, khlp, %1 + key_home, // key_home, khome, kh + key_ic, // key_ic, kich1, kI + key_il, // key_il, kil1, kA + key_left, // key_left, kcub1, kl + key_ll, // key_ll, kll, kH + key_mark, // key_mark, kmrk, %2 + key_message, // key_message, kmsg, %3 + key_move, // key_move, kmov, %4 + key_next, // key_next, knxt, %5 + key_npage, // key_npage, knp, kN + key_open, // key_open, kopn, %6 + key_options, // key_options, kopt, %7 + key_ppage, // key_ppage, kpp, kP + key_previous, // key_previous, kprv, %8 + key_print, // key_print, kprt, %9 + key_redo, // key_redo, krdo, %0 + key_reference, // key_reference, kref, &1 + key_refresh, // key_refresh, krfr, &2 + key_replace, // key_replace, krpl, &3 + key_restart, // key_restart, krst, &4 + key_resume, // key_resume, kres, &5 + key_right, // key_right, kcuf1, kr + key_save, // key_save, ksav, &6 + key_sbeg, // key_sbeg, kBEG, &9 + key_scancel, // key_scancel, kCAN, &0 + key_scommand, // key_scommand, kCMD, *1 + key_scopy, // key_scopy, kCPY, *2 + key_screate, // key_screate, kCRT, *3 + key_sdc, // key_sdc, kDC, *4 + key_sdl, // key_sdl, kDL, *5 + key_select, // key_select, kslt, *6 + key_send, // key_send, kEND, *7 + key_seol, // key_seol, kEOL, *8 + key_sexit, // key_sexit, kEXT, *9 + key_sf, // key_sf, kind, kF + key_sfind, // key_sfind, kFND, *0 + key_shelp, // key_shelp, kHLP, #1 + key_shome, // key_shome, kHOM, #2 + key_sic, // key_sic, kIC, #3 + key_sleft, // key_sleft, kLFT, #4 + key_smessage, // key_smessage, kMSG, %a + key_smove, // key_smove, kMOV, %b + key_snext, // key_snext, kNXT, %c + key_soptions, // key_soptions, kOPT, %d + key_sprevious, // key_sprevious, kPRV, %e + key_sprint, // key_sprint, kPRT, %f + key_sr, // key_sr, kri, kR + key_sredo, // key_sredo, kRDO, %g + key_sreplace, // key_sreplace, kRPL, %h + key_sright, // key_sright, kRIT, %i + key_srsume, // key_srsume, kRES, %j + key_ssave, // key_ssave, kSAV, !1 + key_ssuspend, // key_ssuspend, kSPD, !2 + key_stab, // key_stab, khts, kT + key_sundo, // key_sundo, kUND, !3 + key_suspend, // key_suspend, kspd, &7 + key_undo, // key_undo, kund, &8 + key_up, // key_up, kcuu1, ku + keypad_local, // keypad_local, rmkx, ke + keypad_xmit, // keypad_xmit, smkx, ks + lab_f0, // lab_f0, lf0, l0 + lab_f1, // lab_f1, lf1, l1 + lab_f10, // lab_f10, lf10, la + lab_f2, // lab_f2, lf2, l2 + lab_f3, // lab_f3, lf3, l3 + lab_f4, // lab_f4, lf4, l4 + lab_f5, // lab_f5, lf5, l5 + lab_f6, // lab_f6, lf6, l6 + lab_f7, // lab_f7, lf7, l7 + lab_f8, // lab_f8, lf8, l8 + lab_f9, // lab_f9, lf9, l9 + label_format, // label_format, fln, Lf + label_off, // label_off, rmln, LF + label_on, // label_on, smln, LO + meta_off, // meta_off, rmm, mo + meta_on, // meta_on, smm, mm + micro_column_address, // micro_column_address, mhpa, ZY + micro_down, // micro_down, mcud1, ZZ + micro_left, // micro_left, mcub1, Za + micro_right, // micro_right, mcuf1, Zb + micro_row_address, // micro_row_address, mvpa, Zc + micro_up, // micro_up, mcuu1, Zd + newline, // newline, nel, nw + order_of_pins, // order_of_pins, porder, Ze + orig_colors, // orig_colors, oc, oc + orig_pair, // orig_pair, op, op + pad_char, // pad_char, pad, pc + parm_dch, // parm_dch, dch, DC + parm_delete_line, // parm_delete_line, dl, DL + parm_down_cursor, // parm_down_cursor, cud, DO + parm_down_micro, // parm_down_micro, mcud, Zf + parm_ich, // parm_ich, ich, IC + parm_index, // parm_index, indn, SF + parm_insert_line, // parm_insert_line, il, AL + parm_left_cursor, // parm_left_cursor, cub, LE + parm_left_micro, // parm_left_micro, mcub, Zg + parm_right_cursor, // parm_right_cursor, cuf, RI + parm_right_micro, // parm_right_micro, mcuf, Zh + parm_rindex, // parm_rindex, rin, SR + parm_up_cursor, // parm_up_cursor, cuu, UP + parm_up_micro, // parm_up_micro, mcuu, Zi + pkey_key, // pkey_key, pfkey, pk + pkey_local, // pkey_local, pfloc, pl + pkey_xmit, // pkey_xmit, pfx, px + plab_norm, // plab_norm, pln, pn + print_screen, // print_screen, mc0, ps + prtr_non, // prtr_non, mc5p, pO + prtr_off, // prtr_off, mc4, pf + prtr_on, // prtr_on, mc5, po + pulse, // pulse, pulse, PU + quick_dial, // quick_dial, qdial, QD + remove_clock, // remove_clock, rmclk, RC + repeat_char, // repeat_char, rep, rp + req_for_input, // req_for_input, rfi, RF + reset_1string, // reset_1string, rs1, r1 + reset_2string, // reset_2string, rs2, r2 + reset_3string, // reset_3string, rs3, r3 + reset_file, // reset_file, rf, rf + restore_cursor, // restore_cursor, rc, rc + row_address, // row_address, vpa, cv + save_cursor, // save_cursor, sc, sc + scroll_forward, // scroll_forward, ind, sf + scroll_reverse, // scroll_reverse, ri, sr + select_char_set, // select_char_set, scs, Zj + set_attributes, // set_attributes, sgr, sa + set_background, // set_background, setb, Sb + set_bottom_margin, // set_bottom_margin, smgb, Zk + set_bottom_margin_parm, // set_bottom_margin_parm, smgbp, Zl + set_clock, // set_clock, sclk, SC + set_color_pair, // set_color_pair, scp, sp + set_foreground, // set_foreground, setf, Sf + set_left_margin, // set_left_margin, smgl, ML + set_left_margin_parm, // set_left_margin_parm, smglp, Zm + set_right_margin, // set_right_margin, smgr, MR + set_right_margin_parm, // set_right_margin_parm, smgrp, Zn + set_tab, // set_tab, hts, st + set_top_margin, // set_top_margin, smgt, Zo + set_top_margin_parm, // set_top_margin_parm, smgtp, Zp + set_window, // set_window, wind, wi + start_bit_image, // start_bit_image, sbim, Zq + start_char_set_def, // start_char_set_def, scsd, Zr + stop_bit_image, // stop_bit_image, rbim, Zs + stop_char_set_def, // stop_char_set_def, rcsd, Zt + subscript_characters, // subscript_characters, subcs, Zu + superscript_characters, // superscript_characters, supcs, Zv + tab, // tab, ht, ta + these_cause_cr, // these_cause_cr, docr, Zw + to_status_line, // to_status_line, tsl, ts + tone, // tone, tone, TO + underline_char, // underline_char, uc, uc + up_half_line, // up_half_line, hu, hu + user0, // user0, u0, u0 + user1, // user1, u1, u1 + user2, // user2, u2, u2 + user3, // user3, u3, u3 + user4, // user4, u4, u4 + user5, // user5, u5, u5 + user6, // user6, u6, u6 + user7, // user7, u7, u7 + user8, // user8, u8, u8 + user9, // user9, u9, u9 + wait_tone, // wait_tone, wait, WA + xoff_character, // xoff_character, xoffc, XF + xon_character, // xon_character, xonc, XN + zero_motion, // zero_motion, zerom, Zx + alt_scancode_esc, // alt_scancode_esc, scesa, S8 + bit_image_carriage_return, // bit_image_carriage_return, bicr, Yv + bit_image_newline, // bit_image_newline, binel, Zz + bit_image_repeat, // bit_image_repeat, birep, Xy + char_set_names, // char_set_names, csnm, Zy + code_set_init, // code_set_init, csin, ci + color_names, // color_names, colornm, Yw + define_bit_image_region, // define_bit_image_region, defbi, Yx + device_type, // device_type, devt, dv + display_pc_char, // display_pc_char, dispc, S1 + end_bit_image_region, // end_bit_image_region, endbi, Yy + enter_pc_charset_mode, // enter_pc_charset_mode, smpch, S2 + enter_scancode_mode, // enter_scancode_mode, smsc, S4 + exit_pc_charset_mode, // exit_pc_charset_mode, rmpch, S3 + exit_scancode_mode, // exit_scancode_mode, rmsc, S5 + get_mouse, // get_mouse, getm, Gm + key_mouse, // key_mouse, kmous, Km + mouse_info, // mouse_info, minfo, Mi + pc_term_options, // pc_term_options, pctrm, S6 + pkey_plab, // pkey_plab, pfxl, xl + req_mouse_pos, // req_mouse_pos, reqmp, RQ + scancode_escape, // scancode_escape, scesc, S7 + set0_des_seq, // set0_des_seq, s0ds, s0 + set1_des_seq, // set1_des_seq, s1ds, s1 + set2_des_seq, // set2_des_seq, s2ds, s2 + set3_des_seq, // set3_des_seq, s3ds, s3 + set_a_background, // set_a_background, setab, AB + set_a_foreground, // set_a_foreground, setaf, AF + set_color_band, // set_color_band, setcolor, Yz + set_lr_margin, // set_lr_margin, smglr, ML + set_page_length, // set_page_length, slines, YZ + set_tb_margin, // set_tb_margin, smgtb, MT + enter_horizontal_hl_mode, // enter_horizontal_hl_mode, ehhlm, Xh + enter_left_hl_mode, // enter_left_hl_mode, elhlm, Xl + enter_low_hl_mode, // enter_low_hl_mode, elohlm, Xo + enter_right_hl_mode, // enter_right_hl_mode, erhlm, Xr + enter_top_hl_mode, // enter_top_hl_mode, ethlm, Xt + enter_vertical_hl_mode, // enter_vertical_hl_mode, evhlm, Xv + set_a_attributes, // set_a_attributes, sgr1, sA + set_pglen_inch, // set_pglen_inch, slength, sL) ; public String[] getNames() { @@ -515,8 +513,9 @@ public static Capability byName(String name) { public static Map getCapabilitiesByName() { Map capabilities = new LinkedHashMap<>(); try (InputStream is = InfoCmp.class.getResourceAsStream("capabilities.txt"); - BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { - br.lines().map(String::trim) + BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + br.lines() + .map(String::trim) .filter(s -> !s.startsWith("#")) .filter(s -> !s.isEmpty()) .forEach(s -> { @@ -539,9 +538,7 @@ public static void setDefaultInfoCmp(String terminal, Supplier caps) { CAPS.putIfAbsent(terminal, caps); } - public static String getInfoCmp( - String terminal - ) throws IOException, InterruptedException { + public static String getInfoCmp(String terminal) throws IOException, InterruptedException { String caps = getLoadedInfoCmp(terminal); if (caps == null) { Process p = new ProcessBuilder(OSUtils.INFOCMP_COMMAND, terminal).start(); @@ -563,8 +560,7 @@ public static void parseInfoCmp( String capabilities, Set bools, Map ints, - Map strings - ) { + Map strings) { Map capsByName = getCapabilitiesByName(); String[] lines = capabilities.split("\n"); for (int i = 1; i < lines.length; i++) { @@ -609,7 +605,7 @@ public static void parseInfoCmp( static String loadDefaultInfoCmp(String name) { try (InputStream is = InfoCmp.class.getResourceAsStream(name + ".caps"); - BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { return br.lines().collect(Collectors.joining("\n", "", "\n")); } catch (IOException e) { throw new IOError(e); @@ -617,11 +613,23 @@ static String loadDefaultInfoCmp(String name) { } static { - for (String s : Arrays.asList("dumb", "dumb-color", "ansi", "xterm", "xterm-256color", - "windows", "windows-256color", "windows-conemu", "windows-vtp", - "screen", "screen-256color", "rxvt-unicode", "rxvt-unicode-256color", "rxvt-basic", "rxvt")) { + for (String s : Arrays.asList( + "dumb", + "dumb-color", + "ansi", + "xterm", + "xterm-256color", + "windows", + "windows-256color", + "windows-conemu", + "windows-vtp", + "screen", + "screen-256color", + "rxvt-unicode", + "rxvt-unicode-256color", + "rxvt-basic", + "rxvt")) { setDefaultInfoCmp(s, () -> loadDefaultInfoCmp(s)); } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InputStreamReader.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InputStreamReader.java index fda314fb9b4aa..449d6aa69c85c 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InputStreamReader.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InputStreamReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -22,7 +22,6 @@ import java.nio.charset.MalformedInputException; import java.nio.charset.UnmappableCharacterException; - /** * * NOTE for JLine: the default InputStreamReader that comes from the JRE @@ -66,9 +65,10 @@ public class InputStreamReader extends Reader { public InputStreamReader(InputStream in) { super(in); this.in = in; - decoder = Charset.defaultCharset().newDecoder().onMalformedInput( - CodingErrorAction.REPLACE).onUnmappableCharacter( - CodingErrorAction.REPLACE); + decoder = Charset.defaultCharset() + .newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); bytes.limit(0); } @@ -87,20 +87,19 @@ public InputStreamReader(InputStream in) { * @throws UnsupportedEncodingException * if the encoding specified by {@code enc} cannot be found. */ - public InputStreamReader(InputStream in, final String enc) - throws UnsupportedEncodingException { + public InputStreamReader(InputStream in, final String enc) throws UnsupportedEncodingException { super(in); if (enc == null) { throw new NullPointerException(); } this.in = in; try { - decoder = Charset.forName(enc).newDecoder().onMalformedInput( - CodingErrorAction.REPLACE).onUnmappableCharacter( - CodingErrorAction.REPLACE); + decoder = Charset.forName(enc) + .newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); } catch (IllegalArgumentException e) { - throw (UnsupportedEncodingException) - new UnsupportedEncodingException(enc).initCause(e); + throw (UnsupportedEncodingException) new UnsupportedEncodingException(enc).initCause(e); } bytes.limit(0); } @@ -134,9 +133,9 @@ public InputStreamReader(InputStream in, CharsetDecoder dec) { public InputStreamReader(InputStream in, Charset charset) { super(in); this.in = in; - decoder = charset.newDecoder().onMalformedInput( - CodingErrorAction.REPLACE).onUnmappableCharacter( - CodingErrorAction.REPLACE); + decoder = charset.newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); bytes.limit(0); } @@ -257,8 +256,7 @@ public int read(char[] buf, int offset, int length) throws IOException { // fill the buffer if needed if (needInput) { try { - if ((in.available() == 0) - && (out.position() > offset)) { + if ((in.available() == 0) && (out.position() > offset)) { // we could return the result without blocking read break; } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Levenshtein.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Levenshtein.java index cc95593ed8071..015515f7d074e 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Levenshtein.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Levenshtein.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -49,9 +49,8 @@ public static int distance(CharSequence lhs, CharSequence rhs) { return distance(lhs, rhs, 1, 1, 1, 1); } - public static int distance(CharSequence source, CharSequence target, - int deleteCost, int insertCost, - int replaceCost, int swapCost) { + public static int distance( + CharSequence source, CharSequence target, int deleteCost, int insertCost, int replaceCost, int swapCost) { /* * Required to facilitate the premise to the algorithm that two swaps of the * same character are never required for optimality. @@ -115,5 +114,4 @@ public static int distance(CharSequence source, CharSequence target, } return table[source.length() - 1][target.length() - 1]; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java index 91f18755e255f..254173d5bd971 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2020, the original author or authors. + * Copyright (c) 2002-2020, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -73,8 +73,7 @@ static void render(final PrintStream out, final Object message) { } } out.print("]"); - } - else { + } else { out.print(message); } } @@ -84,11 +83,10 @@ static void render(final PrintStream out, final Object message) { // ByteArrayOutputStream baos = new ByteArrayOutputStream(); // PrintStream ps = new PrintStream(baos); // for (int i = 0; i < messages.length; i++) { -// // Special handling for the last message if its a throwable, render its stack on the next line +// // Special handling for the last message if it's a throwable, render its stack on the next line // if (i + 1 == messages.length && messages[i] instanceof Throwable) { // cause = (Throwable) messages[i]; -// } -// else { +// } else { // render(ps, messages[i]); // } // } @@ -122,5 +120,4 @@ static void render(final PrintStream out, final Object message) { // static boolean isEnabled(Level level) { // return logger.isLoggable(level); // } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlocking.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlocking.java index f8cb53489f991..4130634ca4bfb 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlocking.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlocking.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -84,8 +84,7 @@ private NonBlockingReaderInputStream(NonBlockingReader reader, Charset charset) @Override public int available() { - return (int) (reader.available() * this.encoder.averageBytesPerChar()) - + bytes.remaining(); + return (int) (reader.available() * this.encoder.averageBytesPerChar()) + bytes.remaining(); } @Override @@ -124,7 +123,6 @@ public int read(long timeout, boolean isPeek) throws IOException { return READ_EXPIRED; } } - } private static class NonBlockingInputStreamReader extends NonBlockingReader { @@ -135,10 +133,12 @@ private static class NonBlockingInputStreamReader extends NonBlockingReader { private final CharBuffer chars; public NonBlockingInputStreamReader(NonBlockingInputStream inputStream, Charset encoding) { - this(inputStream, - (encoding != null ? encoding : Charset.defaultCharset()).newDecoder() - .onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE)); + this( + inputStream, + (encoding != null ? encoding : Charset.defaultCharset()) + .newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE)); } public NonBlockingInputStreamReader(NonBlockingInputStream input, CharsetDecoder decoder) { @@ -201,8 +201,8 @@ public int readBuffered(char[] b, int off, int len, long timeout) throws IOExcep bytes.position(0); bytes.limit(0); } - int nb = input.readBuffered(bytes.array(), bytes.limit(), - bytes.capacity() - bytes.limit(), t.timeout()); + int nb = input.readBuffered( + bytes.array(), bytes.limit(), bytes.capacity() - bytes.limit(), t.timeout()); if (nb < 0) { return nb; } @@ -227,5 +227,4 @@ public void close() throws IOException { input.close(); } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStream.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStream.java index a4283eb700625..10ab68cfd28e0 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStream.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -74,7 +74,7 @@ public int read(byte b[], int off, int len) throws IOException { if (c == EOF) { return EOF; } - b[off] = (byte)c; + b[off] = (byte) c; return 1; } @@ -115,9 +115,7 @@ public int readBuffered(byte[] b, int off, int len, long timeout) throws IOExcep * thread is currently blocked waiting for I/O it may not actually * shut down until the I/O is received. */ - public void shutdown() { - } + public void shutdown() {} public abstract int read(long timeout, boolean isPeek) throws IOException; - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStreamImpl.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStreamImpl.java index 680047fbe60e0..24cf13a0ee5ad 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStreamImpl.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStreamImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -26,17 +26,15 @@ * the thread that handles blocking I/O. * */ -public class NonBlockingInputStreamImpl - extends NonBlockingInputStream -{ - private InputStream in; // The actual input stream - private int b = READ_EXPIRED; // Recently read byte - - private String name; - private boolean threadIsReading = false; - private IOException exception = null; - private long threadDelay = 60 * 1000; - private Thread thread; +public class NonBlockingInputStreamImpl extends NonBlockingInputStream { + private InputStream in; // The actual input stream + private int b = READ_EXPIRED; // Recently read byte + + private String name; + private boolean threadIsReading = false; + private IOException exception = null; + private long threadDelay = 60 * 1000; + private Thread thread; /** * Creates a NonBlockingReader out of a normal blocking @@ -97,8 +95,7 @@ public synchronized int read(long timeout, boolean isPeek) throws IOException { if (exception != null) { assert b == READ_EXPIRED; IOException toBeThrown = exception; - if (!isPeek) - exception = null; + if (!isPeek) exception = null; throw toBeThrown; } @@ -109,11 +106,9 @@ public synchronized int read(long timeout, boolean isPeek) throws IOException { */ if (b >= -1) { assert exception == null; - } - else if (!isPeek && timeout <= 0L && !threadIsReading) { + } else if (!isPeek && timeout <= 0L && !threadIsReading) { b = in.read(); - } - else { + } else { /* * If the thread isn't reading already, then ask it to do so. */ @@ -128,14 +123,13 @@ else if (!isPeek && timeout <= 0L && !threadIsReading) { * now we play the waiting game. */ Timeout t = new Timeout(timeout); - while (!t.elapsed()) { + while (!t.elapsed()) { try { if (Thread.interrupted()) { throw new InterruptedException(); } wait(t.timeout()); - } - catch (InterruptedException e) { + } catch (InterruptedException e) { exception = (IOException) new InterruptedIOException().initCause(e); } @@ -143,8 +137,7 @@ else if (!isPeek && timeout <= 0L && !threadIsReading) { assert b == READ_EXPIRED; IOException toBeThrown = exception; - if (!isPeek) - exception = null; + if (!isPeek) exception = null; throw toBeThrown; } @@ -168,7 +161,7 @@ else if (!isPeek && timeout <= 0L && !threadIsReading) { return ret; } - private void run () { + private void run() { Log.debug("NonBlockingInputStream start"); boolean needToRead; @@ -236,5 +229,4 @@ private void run () { } } } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java index 5a3613443508e..aad6e3d0f5af0 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2017, the original author or authors. + * Copyright (c) 2002-2017, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -57,11 +57,7 @@ private int wait(ByteBuffer buffer, long timeout) throws IOException { throw new InterruptedIOException(); } } - return buffer.hasRemaining() - ? 0 - : closed - ? EOF - : READ_EXPIRED; + return buffer.hasRemaining() ? 0 : closed ? EOF : READ_EXPIRED; } private static boolean rewind(ByteBuffer buffer, ByteBuffer other) { @@ -167,7 +163,7 @@ private class NbpOutputStream extends OutputStream { @Override public void write(int b) throws IOException { - NonBlockingPumpInputStream.this.write(new byte[] { (byte) b }, 0, 1); + NonBlockingPumpInputStream.this.write(new byte[] {(byte) b}, 0, 1); } @Override @@ -184,7 +180,5 @@ public void flush() throws IOException { public void close() throws IOException { NonBlockingPumpInputStream.this.close(); } - } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java index f36423f03b82f..63db6e5f5bf7f 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2017, the original author or authors. + * Copyright (c) 2002-2017, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -121,7 +121,7 @@ public int readBuffered(char[] b, int off, int len, long timeout) throws IOExcep try { if (timeout > 0) { if (!notEmpty.await(timeout, TimeUnit.MILLISECONDS)) { - throw new IOException( "Timeout reading" ); + throw new IOException("Timeout reading"); } } else { notEmpty.await(); @@ -207,14 +207,11 @@ public void write(char[] cbuf, int off, int len) throws IOException { } @Override - public void flush() throws IOException { - } + public void flush() throws IOException {} @Override public void close() throws IOException { NonBlockingPumpReader.this.close(); } - } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReader.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReader.java index e2f664f29993b..460f548b5069f 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReader.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -23,8 +23,7 @@ public abstract class NonBlockingReader extends Reader { * thread is currently blocked waiting for I/O it will not actually * shut down until the I/O is received. */ - public void shutdown() { - } + public void shutdown() {} @Override public int read() throws IOException { @@ -109,5 +108,4 @@ public int available() { * @throws IOException if anything wrong happens */ protected abstract int read(long timeout, boolean isPeek) throws IOException; - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReaderImpl.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReaderImpl.java index d384cc9a0dc01..5aaabd1d3c0d0 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReaderImpl.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReaderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -28,19 +28,17 @@ * @since 2.7 * @author Scott C. Gray <scottgray1@gmail.com> */ -public class NonBlockingReaderImpl - extends NonBlockingReader -{ +public class NonBlockingReaderImpl extends NonBlockingReader { public static final int READ_EXPIRED = -2; - private Reader in; // The actual input stream - private int ch = READ_EXPIRED; // Recently read character + private Reader in; // The actual input stream + private int ch = READ_EXPIRED; // Recently read character - private String name; - private boolean threadIsReading = false; - private IOException exception = null; - private long threadDelay = 60 * 1000; - private Thread thread; + private String name; + private boolean threadIsReading = false; + private IOException exception = null; + private long threadDelay = 60 * 1000; + private Thread thread; /** * Creates a NonBlockingReader out of a normal blocking @@ -135,8 +133,7 @@ protected synchronized int read(long timeout, boolean isPeek) throws IOException if (exception != null) { assert ch == READ_EXPIRED; IOException toBeThrown = exception; - if (!isPeek) - exception = null; + if (!isPeek) exception = null; throw toBeThrown; } @@ -147,11 +144,9 @@ protected synchronized int read(long timeout, boolean isPeek) throws IOException */ if (ch >= -1) { assert exception == null; - } - else if (!isPeek && timeout <= 0L && !threadIsReading) { + } else if (!isPeek && timeout <= 0L && !threadIsReading) { ch = in.read(); - } - else { + } else { /* * If the thread isn't reading already, then ask it to do so. */ @@ -166,14 +161,13 @@ else if (!isPeek && timeout <= 0L && !threadIsReading) { * now we play the waiting game. */ Timeout t = new Timeout(timeout); - while (!t.elapsed()) { + while (!t.elapsed()) { try { if (Thread.interrupted()) { throw new InterruptedException(); } wait(t.timeout()); - } - catch (InterruptedException e) { + } catch (InterruptedException e) { exception = (IOException) new InterruptedIOException().initCause(e); } @@ -181,8 +175,7 @@ else if (!isPeek && timeout <= 0L && !threadIsReading) { assert ch == READ_EXPIRED; IOException toBeThrown = exception; - if (!isPeek) - exception = null; + if (!isPeek) exception = null; throw toBeThrown; } @@ -206,7 +199,7 @@ else if (!isPeek && timeout <= 0L && !threadIsReading) { return ret; } - private void run () { + private void run() { Log.debug("NonBlockingReader start"); boolean needToRead; @@ -245,12 +238,12 @@ private void run () { IOException failure = null; try { charRead = in.read(); -// if (charRead < 0) { -// continue; -// } + // if (charRead < 0) { + // continue; + // } } catch (IOException e) { failure = e; -// charRead = -1; + // charRead = -1; } /* diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/OSUtils.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/OSUtils.java index 97487731852ef..344d081c12486 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/OSUtils.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/OSUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -9,16 +9,23 @@ package jdk.internal.org.jline.utils; import java.io.File; -import java.nio.file.Files; -import java.nio.file.Paths; public class OSUtils { - public static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().contains("win"); + public static final boolean IS_LINUX = + System.getProperty("os.name").toLowerCase().contains("linux"); - public static final boolean IS_CYGWIN = IS_WINDOWS - && System.getenv("PWD") != null - && System.getenv("PWD").startsWith("/"); + public static final boolean IS_WINDOWS = + System.getProperty("os.name").toLowerCase().contains("win"); + + public static final boolean IS_OSX = + System.getProperty("os.name").toLowerCase().contains("mac"); + + public static final boolean IS_AIX = + System.getProperty("os.name").toLowerCase().contains("aix"); + + public static final boolean IS_CYGWIN = + IS_WINDOWS && System.getenv("PWD") != null && System.getenv("PWD").startsWith("/"); @Deprecated public static final boolean IS_MINGW = IS_WINDOWS @@ -28,7 +35,7 @@ public class OSUtils { public static final boolean IS_MSYSTEM = IS_WINDOWS && System.getenv("MSYSTEM") != null && (System.getenv("MSYSTEM").startsWith("MINGW") - || System.getenv("MSYSTEM").equals("MSYS")); + || System.getenv("MSYSTEM").equals("MSYS")); public static final boolean IS_WSL = System.getenv("WSL_DISTRO_NAME") != null; @@ -36,11 +43,7 @@ public class OSUtils { public static final boolean IS_WSL2 = IS_WSL && !IS_WSL1; - public static final boolean IS_CONEMU = IS_WINDOWS - && System.getenv("ConEmuPID") != null; - - public static final boolean IS_OSX = System.getProperty("os.name").toLowerCase().contains("mac"); - public static final boolean IS_AIX = System.getProperty("os.name").equals("AIX"); + public static final boolean IS_CONEMU = IS_WINDOWS && System.getenv("ConEmuPID") != null; public static String TTY_COMMAND; public static String STTY_COMMAND; @@ -48,64 +51,57 @@ public class OSUtils { public static String INFOCMP_COMMAND; public static String TEST_COMMAND; + private static boolean isExecutable(File f) { + return f.canExecute() && !f.isDirectory(); + } + static { - String tty; - String stty; - String sttyfopt; - String infocmp; - String test; - if (OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) { - tty = null; - stty = null; - sttyfopt = null; - infocmp = null; - test = null; - String path = System.getenv("PATH"); - if (path != null) { - String[] paths = path.split(";"); - for (String p : paths) { - if (tty == null && new File(p, "tty.exe").exists()) { - tty = new File(p, "tty.exe").getAbsolutePath(); - } - if (stty == null && new File(p, "stty.exe").exists()) { - stty = new File(p, "stty.exe").getAbsolutePath(); - } - if (infocmp == null && new File(p, "infocmp.exe").exists()) { - infocmp = new File(p, "infocmp.exe").getAbsolutePath(); - } - if (test == null && new File(p, "test.exe").exists()) { - test = new File(p, "test.exe").getAbsolutePath(); - } + boolean cygwinOrMsys = OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM; + String suffix = cygwinOrMsys ? ".exe" : ""; + String tty = null; + String stty = null; + String sttyfopt = null; + String infocmp = null; + String test = null; + String path = System.getenv("PATH"); + if (path != null) { + String[] paths = path.split(File.pathSeparator); + for (String p : paths) { + File ttyFile = new File(p, "tty" + suffix); + if (tty == null && isExecutable(ttyFile)) { + tty = ttyFile.getAbsolutePath(); + } + File sttyFile = new File(p, "stty" + suffix); + if (stty == null && isExecutable(sttyFile)) { + stty = sttyFile.getAbsolutePath(); + } + File infocmpFile = new File(p, "infocmp" + suffix); + if (infocmp == null && isExecutable(infocmpFile)) { + infocmp = infocmpFile.getAbsolutePath(); + } + File testFile = new File(p, "test" + suffix); + if (test == null && isExecutable(testFile)) { + test = testFile.getAbsolutePath(); } } - if (tty == null) { - tty = "tty.exe"; - } - if (stty == null) { - stty = "stty.exe"; - } - if (infocmp == null) { - infocmp = "infocmp.exe"; - } - if (test == null) { - test = "test.exe"; - } - } else { - tty = "tty"; - stty = IS_OSX ? "/bin/stty" : "stty"; - sttyfopt = IS_OSX ? "-f" : "-F"; - infocmp = "infocmp"; - test = isTestCommandValid("/usr/bin/test") ? "/usr/bin/test" - : "/bin/test"; } + if (tty == null) { + tty = "tty" + suffix; + } + if (stty == null) { + stty = "stty" + suffix; + } + if (infocmp == null) { + infocmp = "infocmp" + suffix; + } + if (test == null) { + test = "test" + suffix; + } + sttyfopt = IS_OSX ? "-f" : "-F"; TTY_COMMAND = tty; STTY_COMMAND = stty; STTY_F_OPTION = sttyfopt; INFOCMP_COMMAND = infocmp; TEST_COMMAND = test; } - - private static boolean isTestCommandValid(String command) { - return Files.isExecutable(Paths.get(command)); - } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java index a6894e7672ca3..ce68fa5f84e9a 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2017, the original author or authors. + * Copyright (c) 2002-2017, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -378,7 +378,6 @@ public void flush() throws IOException { public void close() throws IOException { reader.close(); } - } private static class InputStream extends java.io.InputStream { @@ -464,7 +463,5 @@ public int read(byte[] b, int off, int len) throws IOException { public void close() throws IOException { reader.close(); } - } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ShutdownHooks.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ShutdownHooks.java index 8089de321cce6..d5456df03d36d 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ShutdownHooks.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ShutdownHooks.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -18,8 +18,7 @@ * @author Jason Dillon * @since 2.7 */ -public final class ShutdownHooks -{ +public final class ShutdownHooks { private static final List tasks = new ArrayList<>(); private static Thread hook; @@ -29,8 +28,7 @@ public static synchronized T add(final T task) { // Install the hook thread if needed if (hook == null) { - hook = addHook(new Thread("JLine Shutdown Hook") - { + hook = addHook(new Thread("JLine Shutdown Hook") { @Override public void run() { runTasks(); @@ -53,8 +51,7 @@ private static synchronized void runTasks() { Log.debug("Running task: ", task); try { task.run(); - } - catch (Throwable e) { + } catch (Throwable e) { Log.warn("Task failed", e); } } @@ -91,8 +88,7 @@ private static void removeHook(final Thread thread) { try { Runtime.getRuntime().removeShutdownHook(thread); - } - catch (IllegalStateException e) { + } catch (IllegalStateException e) { // The VM is shutting down, not a big deal; ignore } } @@ -100,8 +96,7 @@ private static void removeHook(final Thread thread) { /** * Essentially a {@link Runnable} which allows running to throw an exception. */ - public interface Task - { + public interface Task { void run() throws Exception; } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java index b94f1fa318e66..9b25a80473573 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2020, the original author or authors. + * Copyright (c) 2002-2020, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -21,8 +21,7 @@ */ public final class Signals { - private Signals() { - } + private Signals() {} /** * @@ -41,8 +40,8 @@ public static Object register(String name, final Runnable handler, ClassLoader l try { Class signalHandlerClass = Class.forName("sun.misc.SignalHandler"); // Implement signal handler - Object signalHandler = Proxy.newProxyInstance(loader, - new Class[]{signalHandlerClass}, (proxy, method, args) -> { + Object signalHandler = + Proxy.newProxyInstance(loader, new Class[] {signalHandlerClass}, (proxy, method, args) -> { // only method we are proxying is handle() if (method.getDeclaringClass() == Object.class) { if ("toString".equals(method.getName())) { @@ -101,8 +100,7 @@ private static Object doRegister(String name, Object handler) throws Exception { return null; } Class signalHandlerClass = Class.forName("sun.misc.SignalHandler"); - return signalClass.getMethod("handle", signalClass, signalHandlerClass) - .invoke(null, signal, handler); + return signalClass.getMethod("handle", signalClass, signalHandlerClass).invoke(null, signal, handler); } @SuppressWarnings("") @@ -120,5 +118,4 @@ private static String toString(Object handler) { } return handler != null ? handler.toString() : "null"; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Status.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Status.java index e8d9e49e8b3a1..7348b1ea1c5f3 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Status.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Status.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2019, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -8,187 +8,266 @@ */ package jdk.internal.org.jline.utils; -import java.util.Objects; -import java.util.Collections; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import jdk.internal.org.jline.terminal.Size; import jdk.internal.org.jline.terminal.Terminal; import jdk.internal.org.jline.terminal.impl.AbstractTerminal; import jdk.internal.org.jline.utils.InfoCmp.Capability; -import jdk.internal.org.jline.terminal.Size; public class Status { - protected final AbstractTerminal terminal; + protected final Terminal terminal; protected final boolean supported; - protected List oldLines = Collections.emptyList(); - protected List linesToRestore = Collections.emptyList(); - protected int rows; - protected int columns; - protected boolean force; protected boolean suspended = false; protected AttributedString borderString; protected int border = 0; + protected Display display; + protected List lines = Collections.emptyList(); + protected int scrollRegion; public static Status getStatus(Terminal terminal) { return getStatus(terminal, true); } - public static Status getStatus(Terminal terminal, boolean create) { - return terminal instanceof AbstractTerminal - ? ((AbstractTerminal) terminal).getStatus(create) - : null; + public static Optional getExistingStatus(Terminal terminal) { + return Optional.ofNullable(getStatus(terminal, false)); } + public static Status getStatus(Terminal terminal, boolean create) { + return terminal instanceof AbstractTerminal ? ((AbstractTerminal) terminal).getStatus(create) : null; + } - public Status(AbstractTerminal terminal) { + @SuppressWarnings("this-escape") + public Status(Terminal terminal) { this.terminal = Objects.requireNonNull(terminal, "terminal can not be null"); this.supported = terminal.getStringCapability(Capability.change_scroll_region) != null - && terminal.getStringCapability(Capability.save_cursor) != null - && terminal.getStringCapability(Capability.restore_cursor) != null - && terminal.getStringCapability(Capability.cursor_address) != null; + && terminal.getStringCapability(Capability.save_cursor) != null + && terminal.getStringCapability(Capability.restore_cursor) != null + && terminal.getStringCapability(Capability.cursor_address) != null; if (supported) { - char borderChar = '\u2700'; - AttributedStringBuilder bb = new AttributedStringBuilder(); - for (int i = 0; i < 200; i++) { - bb.append(borderChar); - } - borderString = bb.toAttributedString(); + display = new MovingCursorDisplay(terminal); resize(); + display.reset(); + scrollRegion = display.rows - 1; } } + public void close() { + terminal.puts(Capability.save_cursor); + terminal.puts(Capability.change_scroll_region, 0, display.rows - 1); + terminal.puts(Capability.restore_cursor); + terminal.flush(); + } + public void setBorder(boolean border) { this.border = border ? 1 : 0; } public void resize() { - Size size = terminal.getSize(); - this.rows = size.getRows(); - this.columns = size.getColumns(); - this.force = true; + resize(terminal.getSize()); } - public void reset() { - this.force = true; + public void resize(Size size) { + display.resize(size.getRows(), size.getColumns()); } - public void hardReset() { - if (suspended) { - return; + public void reset() { + if (supported) { + display.reset(); + scrollRegion = display.rows; + terminal.puts(Capability.change_scroll_region, 0, scrollRegion); } - List lines = new ArrayList<>(oldLines); - int b = border; - update(null); - border = b; - update(lines); } public void redraw() { if (suspended) { return; } - update(oldLines); + update(lines); } - public void clear() { - privateClear(oldLines.size()); + public void hide() { + update(Collections.emptyList()); } - private void clearAll() { - int b = border; - border = 0; - privateClear(oldLines.size() + b); + public void update(List lines) { + update(lines, true); } - private void privateClear(int statusSize) { - List as = new ArrayList<>(); - for (int i = 0; i < statusSize; i++) { - as.add(new AttributedString("")); - } - if (!as.isEmpty()) { - update(as); - } - } + private final AttributedString ellipsis = + new AttributedStringBuilder().append("\u2026", AttributedStyle.INVERSE).toAttributedString(); - public void update(List lines) { + /** + * Returns true if the cursor may be misplaced and should + * be updated. + */ + public void update(List lines, boolean flush) { if (!supported) { return; } - if (lines == null) { - lines = Collections.emptyList(); - } + this.lines = new ArrayList<>(lines); if (suspended) { - linesToRestore = new ArrayList<>(lines); return; } - if (lines.isEmpty()) { - clearAll(); + + lines = new ArrayList<>(lines); + // add border + int rows = display.rows; + int columns = display.columns; + if (border == 1 && !lines.isEmpty() && rows > 1) { + lines.add(0, getBorderString(columns)); } - if (oldLines.equals(lines) && !force) { - return; + // trim or complete lines to the full width + for (int i = 0; i < lines.size(); i++) { + AttributedString str = lines.get(i); + if (str.columnLength() > columns) { + str = new AttributedStringBuilder(columns) + .append(lines.get(i).columnSubSequence(0, columns - ellipsis.columnLength())) + .append(ellipsis) + .toAttributedString(); + } else if (str.columnLength() < columns) { + str = new AttributedStringBuilder(columns) + .append(str) + .append(' ', columns - str.columnLength()) + .toAttributedString(); + } + lines.set(i, str); } - int statusSize = lines.size() + (lines.size() == 0 ? 0 : border); - int nb = statusSize - oldLines.size() - (oldLines.size() == 0 ? 0 : border); - if (nb > 0) { - for (int i = 0; i < nb; i++) { + + List oldLines = this.display.oldLines; + + int newScrollRegion = display.rows - 1 - lines.size(); + // Update the scroll region if needed. + // Note that settings the scroll region usually moves the cursor, so we need to get ready for that. + if (newScrollRegion < scrollRegion) { + // We need to scroll up to grow the status bar + terminal.puts(Capability.save_cursor); + for (int i = newScrollRegion; i < scrollRegion; i++) { terminal.puts(Capability.cursor_down); } - for (int i = 0; i < nb; i++) { + terminal.puts(Capability.change_scroll_region, 0, newScrollRegion); + terminal.puts(Capability.restore_cursor); + for (int i = newScrollRegion; i < scrollRegion; i++) { terminal.puts(Capability.cursor_up); } + scrollRegion = newScrollRegion; + } else if (newScrollRegion > scrollRegion) { + terminal.puts(Capability.save_cursor); + terminal.puts(Capability.change_scroll_region, 0, newScrollRegion); + terminal.puts(Capability.restore_cursor); + scrollRegion = newScrollRegion; } - terminal.puts(Capability.save_cursor); - terminal.puts(Capability.cursor_address, rows - statusSize, 0); - if (!terminal.puts(Capability.clr_eos)) { - for (int i = rows - statusSize; i < rows; i++) { - terminal.puts(Capability.cursor_address, i, 0); + + // if the display has more lines, we need to add empty ones to make sure they will be erased + List toDraw = new ArrayList<>(lines); + int nbToDraw = toDraw.size(); + int nbOldLines = oldLines.size(); + if (nbOldLines > nbToDraw) { + terminal.puts(Capability.save_cursor); + terminal.puts(Capability.cursor_address, display.rows - nbOldLines, 0); + for (int i = 0; i < nbOldLines - nbToDraw; i++) { terminal.puts(Capability.clr_eol); + if (i < nbOldLines - nbToDraw - 1) { + terminal.puts(Capability.cursor_down); + } + oldLines.remove(0); } + terminal.puts(Capability.restore_cursor); } - if (border == 1 && lines.size() > 0) { - terminal.puts(Capability.cursor_address, rows - statusSize, 0); - borderString.columnSubSequence(0, columns).print(terminal); - } - for (int i = 0; i < lines.size(); i++) { - terminal.puts(Capability.cursor_address, rows - lines.size() + i, 0); - if (lines.get(i).length() > columns) { - AttributedStringBuilder asb = new AttributedStringBuilder(); - asb.append(lines.get(i).substring(0, columns - 3)).append("...", new AttributedStyle(AttributedStyle.INVERSE)); - asb.toAttributedString().columnSubSequence(0, columns).print(terminal); - } else { - lines.get(i).columnSubSequence(0, columns).print(terminal); + // update display + display.update(lines, -1, flush); + } + + private AttributedString getBorderString(int columns) { + if (borderString == null || borderString.length() != columns) { + char borderChar = '\u2700'; + AttributedStringBuilder bb = new AttributedStringBuilder(); + for (int i = 0; i < columns; i++) { + bb.append(borderChar); } + borderString = bb.toAttributedString(); } - terminal.puts(Capability.change_scroll_region, 0, rows - 1 - statusSize); - terminal.puts(Capability.restore_cursor); - terminal.flush(); - oldLines = new ArrayList<>(lines); - force = false; + return borderString; } + /** + * The {@code suspend} method is used when a full-screen. + * If the status was not already suspended, the lines + * used by the status are cleared during this call. + */ public void suspend() { - if (suspended) { - return; + if (!suspended) { + suspended = true; } - linesToRestore = new ArrayList<>(oldLines); - int b = border; - update(null); - border = b; - suspended = true; } + /** + * The {@code restore()} call is the opposite of {@code suspend()} and + * will make the status bar be updated again. + * If the status was suspended, the lines + * used by the status will be drawn during this call. + */ public void restore() { - if (!suspended) { - return; + if (suspended) { + suspended = false; + update(this.lines); } - suspended = false; - update(linesToRestore); - linesToRestore = Collections.emptyList(); } public int size() { - return oldLines.size() + border; + return size(this.lines); + } + + private int size(List lines) { + int l = lines.size(); + return l > 0 ? l + border : 0; + } + + @Override + public String toString() { + return "Status[" + "supported=" + supported + ']'; } + static class MovingCursorDisplay extends Display { + protected int firstLine; + + public MovingCursorDisplay(Terminal terminal) { + super(terminal, false); + } + + @Override + public void update(List newLines, int targetCursorPos, boolean flush) { + cursorPos = -1; + firstLine = rows - newLines.size(); + super.update(newLines, targetCursorPos, flush); + if (cursorPos != -1) { + terminal.puts(Capability.restore_cursor); + } + } + + @Override + protected void moveVisualCursorTo(int targetPos, List newLines) { + initCursor(); + super.moveVisualCursorTo(targetPos, newLines); + } + + @Override + protected int moveVisualCursorTo(int i1) { + initCursor(); + return super.moveVisualCursorTo(i1); + } + + void initCursor() { + if (cursorPos == -1) { + terminal.puts(Capability.save_cursor); + terminal.puts(Capability.cursor_address, firstLine, 0); + cursorPos = 0; + } + } + } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java index be1659957b445..29d87cbc3c85f 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -55,12 +55,12 @@ private static Integer colorRgb(String name) { return null; } } else { - // load indexed color - Integer color = color(name); - if (color != null && color != -1) { - color = Colors.DEFAULT_COLORS_256[color]; - } - return color; + // load indexed color + Integer color = color(name); + if (color != null && color != -1) { + color = Colors.DEFAULT_COLORS_256[color]; + } + return color; } } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Timeout.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Timeout.java index edea89cf2d9c3..1e3091e19a117 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Timeout.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Timeout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2018, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -44,5 +44,4 @@ public boolean elapsed() { public long timeout() { return timeout > 0 ? Math.max(1, end - cur) : timeout; } - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WCWidth.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WCWidth.java index db3a8d3d99084..7b0caf91c25c4 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WCWidth.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WCWidth.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2016, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -10,8 +10,7 @@ public final class WCWidth { - private WCWidth() { - } + private WCWidth() {} /* The following two functions define the column width of an ISO 10646 * character as follows: @@ -44,88 +43,86 @@ private WCWidth() { * This implementation assumes that wchar_t characters are encoded * in ISO 10646. */ - public static int wcwidth(int ucs) - { + public static int wcwidth(int ucs) { /* test for 8-bit control characters */ - if (ucs == 0) - return 0; - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) - return -1; + if (ucs == 0) return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1; /* binary search in table of non-spacing characters */ - if (bisearch(ucs, combining, combining.length - 1)) - return 0; + if (bisearch(ucs, combining, combining.length - 1)) return 0; /* if we arrive here, ucs is not a combining or C0/C1 control character */ - return 1 + - ((ucs >= 0x1100 && - (ucs <= 0x115f || /* Hangul Jamo init. consonants */ - ucs == 0x2329 || ucs == 0x232a || - (ucs >= 0x2e80 && ucs <= 0xa4cf && - ucs != 0x303f) || /* CJK ... Yi */ - (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ - (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ - (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ - (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ - (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ - (ucs >= 0xffe0 && ucs <= 0xffe6) || - (ucs >= 0x1f000 && ucs <= 0x1feee) || - (ucs >= 0x20000 && ucs <= 0x2fffd) || - (ucs >= 0x30000 && ucs <= 0x3fffd))) ? 1 : 0); + return 1 + + ((ucs >= 0x1100 + && (ucs <= 0x115f + || /* Hangul Jamo init. consonants */ ucs == 0x2329 + || ucs == 0x232a + || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) + || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) + || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) + || /* CJK Compatibility Ideographs */ (ucs >= 0xfe10 && ucs <= 0xfe19) + || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) + || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) + || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) + || (ucs >= 0x1f000 && ucs <= 0x1feee) + || (ucs >= 0x20000 && ucs <= 0x2fffd) + || (ucs >= 0x30000 && ucs <= 0x3fffd))) + ? 1 + : 0); } /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ static Interval[] combining = { - new Interval( 0x0300, 0x036F ), new Interval( 0x0483, 0x0486 ), new Interval( 0x0488, 0x0489 ), - new Interval( 0x0591, 0x05BD ), new Interval( 0x05BF, 0x05BF ), new Interval( 0x05C1, 0x05C2 ), - new Interval( 0x05C4, 0x05C5 ), new Interval( 0x05C7, 0x05C7 ), new Interval( 0x0600, 0x0603 ), - new Interval( 0x0610, 0x0615 ), new Interval( 0x064B, 0x065E ), new Interval( 0x0670, 0x0670 ), - new Interval( 0x06D6, 0x06E4 ), new Interval( 0x06E7, 0x06E8 ), new Interval( 0x06EA, 0x06ED ), - new Interval( 0x070F, 0x070F ), new Interval( 0x0711, 0x0711 ), new Interval( 0x0730, 0x074A ), - new Interval( 0x07A6, 0x07B0 ), new Interval( 0x07EB, 0x07F3 ), new Interval( 0x0901, 0x0902 ), - new Interval( 0x093C, 0x093C ), new Interval( 0x0941, 0x0948 ), new Interval( 0x094D, 0x094D ), - new Interval( 0x0951, 0x0954 ), new Interval( 0x0962, 0x0963 ), new Interval( 0x0981, 0x0981 ), - new Interval( 0x09BC, 0x09BC ), new Interval( 0x09C1, 0x09C4 ), new Interval( 0x09CD, 0x09CD ), - new Interval( 0x09E2, 0x09E3 ), new Interval( 0x0A01, 0x0A02 ), new Interval( 0x0A3C, 0x0A3C ), - new Interval( 0x0A41, 0x0A42 ), new Interval( 0x0A47, 0x0A48 ), new Interval( 0x0A4B, 0x0A4D ), - new Interval( 0x0A70, 0x0A71 ), new Interval( 0x0A81, 0x0A82 ), new Interval( 0x0ABC, 0x0ABC ), - new Interval( 0x0AC1, 0x0AC5 ), new Interval( 0x0AC7, 0x0AC8 ), new Interval( 0x0ACD, 0x0ACD ), - new Interval( 0x0AE2, 0x0AE3 ), new Interval( 0x0B01, 0x0B01 ), new Interval( 0x0B3C, 0x0B3C ), - new Interval( 0x0B3F, 0x0B3F ), new Interval( 0x0B41, 0x0B43 ), new Interval( 0x0B4D, 0x0B4D ), - new Interval( 0x0B56, 0x0B56 ), new Interval( 0x0B82, 0x0B82 ), new Interval( 0x0BC0, 0x0BC0 ), - new Interval( 0x0BCD, 0x0BCD ), new Interval( 0x0C3E, 0x0C40 ), new Interval( 0x0C46, 0x0C48 ), - new Interval( 0x0C4A, 0x0C4D ), new Interval( 0x0C55, 0x0C56 ), new Interval( 0x0CBC, 0x0CBC ), - new Interval( 0x0CBF, 0x0CBF ), new Interval( 0x0CC6, 0x0CC6 ), new Interval( 0x0CCC, 0x0CCD ), - new Interval( 0x0CE2, 0x0CE3 ), new Interval( 0x0D41, 0x0D43 ), new Interval( 0x0D4D, 0x0D4D ), - new Interval( 0x0DCA, 0x0DCA ), new Interval( 0x0DD2, 0x0DD4 ), new Interval( 0x0DD6, 0x0DD6 ), - new Interval( 0x0E31, 0x0E31 ), new Interval( 0x0E34, 0x0E3A ), new Interval( 0x0E47, 0x0E4E ), - new Interval( 0x0EB1, 0x0EB1 ), new Interval( 0x0EB4, 0x0EB9 ), new Interval( 0x0EBB, 0x0EBC ), - new Interval( 0x0EC8, 0x0ECD ), new Interval( 0x0F18, 0x0F19 ), new Interval( 0x0F35, 0x0F35 ), - new Interval( 0x0F37, 0x0F37 ), new Interval( 0x0F39, 0x0F39 ), new Interval( 0x0F71, 0x0F7E ), - new Interval( 0x0F80, 0x0F84 ), new Interval( 0x0F86, 0x0F87 ), new Interval( 0x0F90, 0x0F97 ), - new Interval( 0x0F99, 0x0FBC ), new Interval( 0x0FC6, 0x0FC6 ), new Interval( 0x102D, 0x1030 ), - new Interval( 0x1032, 0x1032 ), new Interval( 0x1036, 0x1037 ), new Interval( 0x1039, 0x1039 ), - new Interval( 0x1058, 0x1059 ), new Interval( 0x1160, 0x11FF ), new Interval( 0x135F, 0x135F ), - new Interval( 0x1712, 0x1714 ), new Interval( 0x1732, 0x1734 ), new Interval( 0x1752, 0x1753 ), - new Interval( 0x1772, 0x1773 ), new Interval( 0x17B4, 0x17B5 ), new Interval( 0x17B7, 0x17BD ), - new Interval( 0x17C6, 0x17C6 ), new Interval( 0x17C9, 0x17D3 ), new Interval( 0x17DD, 0x17DD ), - new Interval( 0x180B, 0x180D ), new Interval( 0x18A9, 0x18A9 ), new Interval( 0x1920, 0x1922 ), - new Interval( 0x1927, 0x1928 ), new Interval( 0x1932, 0x1932 ), new Interval( 0x1939, 0x193B ), - new Interval( 0x1A17, 0x1A18 ), new Interval( 0x1B00, 0x1B03 ), new Interval( 0x1B34, 0x1B34 ), - new Interval( 0x1B36, 0x1B3A ), new Interval( 0x1B3C, 0x1B3C ), new Interval( 0x1B42, 0x1B42 ), - new Interval( 0x1B6B, 0x1B73 ), new Interval( 0x1DC0, 0x1DCA ), new Interval( 0x1DFE, 0x1DFF ), - new Interval( 0x200B, 0x200F ), new Interval( 0x202A, 0x202E ), new Interval( 0x2060, 0x2063 ), - new Interval( 0x206A, 0x206F ), new Interval( 0x20D0, 0x20EF ), new Interval( 0x302A, 0x302F ), - new Interval( 0x3099, 0x309A ), new Interval( 0xA806, 0xA806 ), new Interval( 0xA80B, 0xA80B ), - new Interval( 0xA825, 0xA826 ), new Interval( 0xFB1E, 0xFB1E ), new Interval( 0xFE00, 0xFE0F ), - new Interval( 0xFE20, 0xFE23 ), new Interval( 0xFEFF, 0xFEFF ), new Interval( 0xFFF9, 0xFFFB ), - new Interval( 0x10A01, 0x10A03 ), new Interval( 0x10A05, 0x10A06 ), new Interval( 0x10A0C, 0x10A0F ), - new Interval( 0x10A38, 0x10A3A ), new Interval( 0x10A3F, 0x10A3F ), new Interval( 0x1D167, 0x1D169 ), - new Interval( 0x1D173, 0x1D182 ), new Interval( 0x1D185, 0x1D18B ), new Interval( 0x1D1AA, 0x1D1AD ), - new Interval( 0x1D242, 0x1D244 ), new Interval( 0x1F3FB, 0x1F3FF ), new Interval( 0xE0001, 0xE0001 ), - new Interval( 0xE0020, 0xE007F ), new Interval( 0xE0100, 0xE01EF ) + new Interval(0x0300, 0x036F), new Interval(0x0483, 0x0486), new Interval(0x0488, 0x0489), + new Interval(0x0591, 0x05BD), new Interval(0x05BF, 0x05BF), new Interval(0x05C1, 0x05C2), + new Interval(0x05C4, 0x05C5), new Interval(0x05C7, 0x05C7), new Interval(0x0600, 0x0603), + new Interval(0x0610, 0x0615), new Interval(0x064B, 0x065E), new Interval(0x0670, 0x0670), + new Interval(0x06D6, 0x06E4), new Interval(0x06E7, 0x06E8), new Interval(0x06EA, 0x06ED), + new Interval(0x070F, 0x070F), new Interval(0x0711, 0x0711), new Interval(0x0730, 0x074A), + new Interval(0x07A6, 0x07B0), new Interval(0x07EB, 0x07F3), new Interval(0x0901, 0x0902), + new Interval(0x093C, 0x093C), new Interval(0x0941, 0x0948), new Interval(0x094D, 0x094D), + new Interval(0x0951, 0x0954), new Interval(0x0962, 0x0963), new Interval(0x0981, 0x0981), + new Interval(0x09BC, 0x09BC), new Interval(0x09C1, 0x09C4), new Interval(0x09CD, 0x09CD), + new Interval(0x09E2, 0x09E3), new Interval(0x0A01, 0x0A02), new Interval(0x0A3C, 0x0A3C), + new Interval(0x0A41, 0x0A42), new Interval(0x0A47, 0x0A48), new Interval(0x0A4B, 0x0A4D), + new Interval(0x0A70, 0x0A71), new Interval(0x0A81, 0x0A82), new Interval(0x0ABC, 0x0ABC), + new Interval(0x0AC1, 0x0AC5), new Interval(0x0AC7, 0x0AC8), new Interval(0x0ACD, 0x0ACD), + new Interval(0x0AE2, 0x0AE3), new Interval(0x0B01, 0x0B01), new Interval(0x0B3C, 0x0B3C), + new Interval(0x0B3F, 0x0B3F), new Interval(0x0B41, 0x0B43), new Interval(0x0B4D, 0x0B4D), + new Interval(0x0B56, 0x0B56), new Interval(0x0B82, 0x0B82), new Interval(0x0BC0, 0x0BC0), + new Interval(0x0BCD, 0x0BCD), new Interval(0x0C3E, 0x0C40), new Interval(0x0C46, 0x0C48), + new Interval(0x0C4A, 0x0C4D), new Interval(0x0C55, 0x0C56), new Interval(0x0CBC, 0x0CBC), + new Interval(0x0CBF, 0x0CBF), new Interval(0x0CC6, 0x0CC6), new Interval(0x0CCC, 0x0CCD), + new Interval(0x0CE2, 0x0CE3), new Interval(0x0D41, 0x0D43), new Interval(0x0D4D, 0x0D4D), + new Interval(0x0DCA, 0x0DCA), new Interval(0x0DD2, 0x0DD4), new Interval(0x0DD6, 0x0DD6), + new Interval(0x0E31, 0x0E31), new Interval(0x0E34, 0x0E3A), new Interval(0x0E47, 0x0E4E), + new Interval(0x0EB1, 0x0EB1), new Interval(0x0EB4, 0x0EB9), new Interval(0x0EBB, 0x0EBC), + new Interval(0x0EC8, 0x0ECD), new Interval(0x0F18, 0x0F19), new Interval(0x0F35, 0x0F35), + new Interval(0x0F37, 0x0F37), new Interval(0x0F39, 0x0F39), new Interval(0x0F71, 0x0F7E), + new Interval(0x0F80, 0x0F84), new Interval(0x0F86, 0x0F87), new Interval(0x0F90, 0x0F97), + new Interval(0x0F99, 0x0FBC), new Interval(0x0FC6, 0x0FC6), new Interval(0x102D, 0x1030), + new Interval(0x1032, 0x1032), new Interval(0x1036, 0x1037), new Interval(0x1039, 0x1039), + new Interval(0x1058, 0x1059), new Interval(0x1160, 0x11FF), new Interval(0x135F, 0x135F), + new Interval(0x1712, 0x1714), new Interval(0x1732, 0x1734), new Interval(0x1752, 0x1753), + new Interval(0x1772, 0x1773), new Interval(0x17B4, 0x17B5), new Interval(0x17B7, 0x17BD), + new Interval(0x17C6, 0x17C6), new Interval(0x17C9, 0x17D3), new Interval(0x17DD, 0x17DD), + new Interval(0x180B, 0x180D), new Interval(0x18A9, 0x18A9), new Interval(0x1920, 0x1922), + new Interval(0x1927, 0x1928), new Interval(0x1932, 0x1932), new Interval(0x1939, 0x193B), + new Interval(0x1A17, 0x1A18), new Interval(0x1B00, 0x1B03), new Interval(0x1B34, 0x1B34), + new Interval(0x1B36, 0x1B3A), new Interval(0x1B3C, 0x1B3C), new Interval(0x1B42, 0x1B42), + new Interval(0x1B6B, 0x1B73), new Interval(0x1DC0, 0x1DCA), new Interval(0x1DFE, 0x1DFF), + new Interval(0x200B, 0x200F), new Interval(0x202A, 0x202E), new Interval(0x2060, 0x2063), + new Interval(0x206A, 0x206F), new Interval(0x20D0, 0x20EF), new Interval(0x302A, 0x302F), + new Interval(0x3099, 0x309A), new Interval(0xA806, 0xA806), new Interval(0xA80B, 0xA80B), + new Interval(0xA825, 0xA826), new Interval(0xFB1E, 0xFB1E), new Interval(0xFE00, 0xFE0F), + new Interval(0xFE20, 0xFE23), new Interval(0xFEFF, 0xFEFF), new Interval(0xFFF9, 0xFFFB), + new Interval(0x10A01, 0x10A03), new Interval(0x10A05, 0x10A06), new Interval(0x10A0C, 0x10A0F), + new Interval(0x10A38, 0x10A3A), new Interval(0x10A3F, 0x10A3F), new Interval(0x1D167, 0x1D169), + new Interval(0x1D173, 0x1D182), new Interval(0x1D185, 0x1D18B), new Interval(0x1D1AA, 0x1D1AD), + new Interval(0x1D242, 0x1D244), new Interval(0x1F3FB, 0x1F3FF), new Interval(0xE0001, 0xE0001), + new Interval(0xE0020, 0xE007F), new Interval(0xE0100, 0xE01EF) }; private static class Interval { @@ -143,20 +140,14 @@ private static boolean bisearch(int ucs, Interval[] table, int max) { int min = 0; int mid; - if (ucs < table[0].first || ucs > table[max].last) - return false; + if (ucs < table[0].first || ucs > table[max].last) return false; while (max >= min) { mid = (min + max) / 2; - if (ucs > table[mid].last) - min = mid + 1; - else if (ucs < table[mid].first) - max = mid - 1; - else - return true; + if (ucs > table[mid].last) min = mid + 1; + else if (ucs < table[mid].first) max = mid - 1; + else return true; } return false; } - - } diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WriterOutputStream.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WriterOutputStream.java index 3ad63a8f38133..b1285f9636ea5 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WriterOutputStream.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WriterOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2017, the original author or authors. + * Copyright (c) 2002-2017, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -35,9 +35,11 @@ public class WriterOutputStream extends OutputStream { private final CharBuffer decoderOut = CharBuffer.allocate(128); public WriterOutputStream(Writer out, Charset charset) { - this(out, charset.newDecoder() - .onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE)); + this( + out, + charset.newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE)); } public WriterOutputStream(Writer out, CharsetDecoder decoder) { @@ -47,7 +49,7 @@ public WriterOutputStream(Writer out, CharsetDecoder decoder) { @Override public void write(int b) throws IOException { - write(new byte[] { (byte)b }, 0, 1); + write(new byte[] {(byte) b}, 0, 1); } @Override diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/windows-conemu.caps b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/windows-conemu.caps index 719bcf8620296..f5caa4402bcc6 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/windows-conemu.caps +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/windows-conemu.caps @@ -5,6 +5,9 @@ windows-conemu|conemu windows terminal, cr=^M, cub=\E[%p1%dD, cub1=\E[D, cud=\E[%p1%dB, cud1=\E[B, cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, + civis=\E[?25l, cvvis=\E[?25h, + smcup=\E[?1049h, rmcup=\E[?1049l, + rc=\E8, sc=\E7, il=\E[%p1%dL, il1=\E[L, dl=\E[%p1%dM, dl1=\E[M, ech=\E[%p1%dX, diff --git a/src/jdk.internal.le/share/legal/jline.md b/src/jdk.internal.le/share/legal/jline.md index 6840c69cfc02b..5b813c0af2faf 100644 --- a/src/jdk.internal.le/share/legal/jline.md +++ b/src/jdk.internal.le/share/legal/jline.md @@ -1,9 +1,9 @@ -## JLine v3.22.0 +## JLine v3.26.1 ### JLine License
       
      -Copyright (c) 2002-2018, the original author or authors.
      +Copyright (c) 2002-2023, the original author or authors.
       All rights reserved.
       
       https://opensource.org/licenses/BSD-3-Clause
      diff --git a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaTerminalProvider.java b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaTerminalProvider.java
      index b820ce2187efe..4137643c7ac47 100644
      --- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaTerminalProvider.java
      +++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaTerminalProvider.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2002-2020, the original author or authors.
      + * Copyright (c) 2002-2020, the original author(s).
        *
        * This software is distributable under the BSD license. See the terms of the
        * BSD license in the documentation provided with this software.
      @@ -8,99 +8,146 @@
        */
       package jdk.internal.org.jline.terminal.impl.jna;
       
      +import java.io.IOException;
      +import java.io.InputStream;
      +import java.io.OutputStream;
      +import java.nio.charset.Charset;
      +import java.util.function.Function;
      +
       import jdk.internal.org.jline.terminal.Attributes;
       import jdk.internal.org.jline.terminal.Size;
       import jdk.internal.org.jline.terminal.Terminal;
      -import jdk.internal.org.jline.terminal.impl.PosixPtyTerminal;
      -import jdk.internal.org.jline.terminal.impl.PosixSysTerminal;
      +import jdk.internal.org.jline.terminal.TerminalBuilder;
      +//import jdk.internal.org.jline.terminal.impl.PosixPtyTerminal;
      +//import jdk.internal.org.jline.terminal.impl.PosixSysTerminal;
       import jdk.internal.org.jline.terminal.impl.jna.win.JnaWinSysTerminal;
      -import jdk.internal.org.jline.terminal.spi.TerminalProvider;
       import jdk.internal.org.jline.terminal.spi.Pty;
      +import jdk.internal.org.jline.terminal.spi.SystemStream;
      +import jdk.internal.org.jline.terminal.spi.TerminalProvider;
       import jdk.internal.org.jline.utils.OSUtils;
       
      -import java.io.IOException;
      -import java.io.InputStream;
      -import java.io.OutputStream;
      -import java.nio.charset.Charset;
      -import java.util.function.Function;
      +public class JnaTerminalProvider implements TerminalProvider {
      +
      +    public JnaTerminalProvider() {
      +        checkSystemStream(SystemStream.Output);
      +    }
       
      -public class JnaTerminalProvider implements TerminalProvider
      -{
           @Override
           public String name() {
      -        return "jna";
      +        return TerminalBuilder.PROP_PROVIDER_JNA;
           }
       
      -//    public Pty current(TerminalProvider.Stream console) throws IOException {
      -//        return JnaNativePty.current(console);
      +//    public Pty current(SystemStream systemStream) throws IOException {
      +//        return JnaNativePty.current(this, systemStream);
       //    }
       //
       //    public Pty open(Attributes attributes, Size size) throws IOException {
      -//        return JnaNativePty.open(attributes, size);
      +//        return JnaNativePty.open(this, attributes, size);
       //    }
       
           @Override
      -    public Terminal sysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
      -                                boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused,
      -                                Stream consoleStream, Function inputStreamWrapper) throws IOException {
      +    public Terminal sysTerminal(
      +            String name,
      +            String type,
      +            boolean ansiPassThrough,
      +            Charset encoding,
      +            boolean nativeSignals,
      +            Terminal.SignalHandler signalHandler,
      +            boolean paused,
      +            SystemStream systemStream,
      +            Function inputStreamWrapper)
      +            throws IOException {
               if (OSUtils.IS_WINDOWS) {
      -            return winSysTerminal(name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, consoleStream, inputStreamWrapper );
      +            return winSysTerminal(name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, systemStream, inputStreamWrapper );
               } else {
                   return null;
               }
           }
       
      -    public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
      -                                   boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused,
      -                                   Stream console, Function inputStreamWrapper) throws IOException {
      -        return JnaWinSysTerminal.createTerminal(name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, console, inputStreamWrapper);
      +    public Terminal winSysTerminal(
      +            String name,
      +            String type,
      +            boolean ansiPassThrough,
      +            Charset encoding,
      +            boolean nativeSignals,
      +            Terminal.SignalHandler signalHandler,
      +            boolean paused,
      +            SystemStream systemStream,
      +            Function inputStreamWrapper)
      +            throws IOException {
      +        return JnaWinSysTerminal.createTerminal(
      +                this, systemStream, name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, inputStreamWrapper);
           }
       
      -//    public Terminal posixSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
      -//                                     boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused,
      -//                                     Stream consoleStream) throws IOException {
      -//        Pty pty = current(consoleStream);
      +//    public Terminal posixSysTerminal(
      +//            String name,
      +//            String type,
      +//            boolean ansiPassThrough,
      +//            Charset encoding,
      +//            boolean nativeSignals,
      +//            Terminal.SignalHandler signalHandler,
      +//            boolean paused,
      +//            SystemStream systemStream)
      +//            throws IOException {
      +//        Pty pty = current(systemStream);
       //        return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler);
       //    }
       
           @Override
      -    public Terminal newTerminal(String name, String type, InputStream in, OutputStream out,
      -                                Charset encoding, Terminal.SignalHandler signalHandler, boolean paused,
      -                                Attributes attributes, Size size) throws IOException
      -    {
      +    public Terminal newTerminal(
      +            String name,
      +            String type,
      +            InputStream in,
      +            OutputStream out,
      +            Charset encoding,
      +            Terminal.SignalHandler signalHandler,
      +            boolean paused,
      +            Attributes attributes,
      +            Size size)
      +            throws IOException {
       //        Pty pty = open(attributes, size);
       //        return new PosixPtyTerminal(name, type, pty, in, out, encoding, signalHandler, paused);
               return null;
           }
       
           @Override
      -    public boolean isSystemStream(Stream stream) {
      +    public boolean isSystemStream(SystemStream stream) {
               try {
      -            if (OSUtils.IS_WINDOWS) {
      -                return isWindowsSystemStream(stream);
      -            } else {
      -//                return isPosixSystemStream(stream);
      -                return false;
      -            }
      +            return checkSystemStream(stream);
               } catch (Throwable t) {
                   return false;
               }
           }
       
      -    public boolean isWindowsSystemStream(Stream stream) {
      -        return JnaWinSysTerminal.isWindowsSystemStream(stream);
      +    private boolean checkSystemStream(SystemStream stream) {
      +        if (OSUtils.IS_WINDOWS) {
      +            return JnaWinSysTerminal.isWindowsSystemStream(stream);
      +        } else {
      +//            return JnaNativePty.isPosixSystemStream(stream);
      +            return false;
      +        }
           }
       
      -//    public boolean isPosixSystemStream(Stream stream) {
      -//        return JnaNativePty.isPosixSystemStream(stream);
      -//    }
      -
           @Override
      -    public String systemStreamName(Stream stream) {
      +    public String systemStreamName(SystemStream stream) {
       //        if (OSUtils.IS_WINDOWS) {
                   return null;
       //        } else {
       //            return JnaNativePty.posixSystemStreamName(stream);
       //        }
           }
      +
      +//    @Override
      +//    public int systemStreamWidth(SystemStream stream) {
      +//        try (Pty pty = current(stream)) {
      +//            return pty.getSize().getColumns();
      +//        } catch (Throwable t) {
      +//            return -1;
      +//        }
      +//    }
      +
      +    @Override
      +    public String toString() {
      +        return "TerminalProvider[" + name() + "]";
      +    }
       }
      diff --git a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinConsoleWriter.java b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinConsoleWriter.java
      index a4469003c1656..8dad17d7cc746 100644
      --- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinConsoleWriter.java
      +++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinConsoleWriter.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2002-2017, the original author or authors.
      + * Copyright (c) 2002-2017, the original author(s).
        *
        * This software is distributable under the BSD license. See the terms of the
        * BSD license in the documentation provided with this software.
      @@ -8,12 +8,13 @@
        */
       package jdk.internal.org.jline.terminal.impl.jna.win;
       
      +import java.io.IOException;
      +
      +import jdk.internal.org.jline.terminal.impl.AbstractWindowsConsoleWriter;
      +
       //import com.sun.jna.LastErrorException;
       //import com.sun.jna.Pointer;
       //import com.sun.jna.ptr.IntByReference;
      -import jdk.internal.org.jline.terminal.impl.AbstractWindowsConsoleWriter;
      -
      -import java.io.IOException;
       
       class JnaWinConsoleWriter extends AbstractWindowsConsoleWriter {
       
      @@ -32,5 +33,4 @@ protected void writeConsole(char[] text, int len) throws IOException {
                   throw new IOException("Failed to write to console", e);
               }
           }
      -
       }
      diff --git a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java
      index 5ffc5d714edca..8afa327120e81 100644
      --- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java
      +++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2002-2020, the original author or authors.
      + * Copyright (c) 2002-2020, the original author(s).
        *
        * This software is distributable under the BSD license. See the terms of the
        * BSD license in the documentation provided with this software.
      @@ -16,25 +16,43 @@
       import java.util.function.Function;
       import java.util.function.IntConsumer;
       
      -//import com.sun.jna.LastErrorException;
      -//import com.sun.jna.Pointer;
      -//import com.sun.jna.ptr.IntByReference;
       import jdk.internal.org.jline.terminal.Cursor;
       import jdk.internal.org.jline.terminal.Size;
       import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal;
      +import jdk.internal.org.jline.terminal.spi.SystemStream;
       import jdk.internal.org.jline.terminal.spi.TerminalProvider;
      +import jdk.internal.org.jline.utils.InfoCmp.Capability;
       import jdk.internal.org.jline.utils.InfoCmp;
       import jdk.internal.org.jline.utils.OSUtils;
       
      -public class JnaWinSysTerminal extends AbstractWindowsTerminal {
      +//import com.sun.jna.LastErrorException;
      +//import com.sun.jna.Pointer;
      +//import com.sun.jna.ptr.IntByReference;
      +
      +public class JnaWinSysTerminal extends AbstractWindowsTerminal {
       
           private static final Pointer consoleIn = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_INPUT_HANDLE);
           private static final Pointer consoleOut = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE);
           private static final Pointer consoleErr = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_ERROR_HANDLE);
       
      -    public static JnaWinSysTerminal createTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, boolean nativeSignals, SignalHandler signalHandler, boolean paused, TerminalProvider.Stream consoleStream, Function inputStreamWrapper) throws IOException {
      +    public static JnaWinSysTerminal createTerminal(
      +            TerminalProvider provider,
      +            SystemStream systemStream,
      +            String name,
      +            String type,
      +            boolean ansiPassThrough,
      +            Charset encoding,
      +            boolean nativeSignals,
      +            SignalHandler signalHandler,
      +            boolean paused,
      +            Function inputStreamWrapper)
      +            throws IOException {
      +        // Get input console mode
      +        IntByReference inMode = new IntByReference();
      +        Kernel32.INSTANCE.GetConsoleMode(JnaWinSysTerminal.consoleIn, inMode);
      +        // Get output console and mode
               Pointer console;
      -        switch (consoleStream) {
      +        switch (systemStream) {
                   case Output:
                       console = JnaWinSysTerminal.consoleOut;
                       break;
      @@ -42,38 +60,42 @@ public static JnaWinSysTerminal createTerminal(String name, String type, boolean
                       console = JnaWinSysTerminal.consoleErr;
                       break;
                   default:
      -                throw new IllegalArgumentException("Unsupport stream for console: " + consoleStream);
      +                throw new IllegalArgumentException("Unsupported stream for console: " + systemStream);
               }
      +        IntByReference outMode = new IntByReference();
      +        Kernel32.INSTANCE.GetConsoleMode(console, outMode);
      +        // Create writer
               Writer writer;
               if (ansiPassThrough) {
      -            if (type == null) {
      -                type = OSUtils.IS_CONEMU ? TYPE_WINDOWS_CONEMU : TYPE_WINDOWS;
      -            }
      +            type = type != null ? type : OSUtils.IS_CONEMU ? TYPE_WINDOWS_CONEMU : TYPE_WINDOWS;
                   writer = new JnaWinConsoleWriter(console);
               } else {
      -            IntByReference mode = new IntByReference();
      -            Kernel32.INSTANCE.GetConsoleMode(console, mode);
      -            try {
      -                Kernel32.INSTANCE.SetConsoleMode(console, mode.getValue() | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING);
      -                if (type == null) {
      -                    type = TYPE_WINDOWS_VTP;
      -                }
      +            if (enableVtp(console, outMode.getValue())) {
      +                type = type != null ? type : TYPE_WINDOWS_VTP;
                       writer = new JnaWinConsoleWriter(console);
      -            } catch (LastErrorException e) {
      -                if (OSUtils.IS_CONEMU) {
      -                    if (type == null) {
      -                        type = TYPE_WINDOWS_CONEMU;
      -                    }
      -                    writer = new JnaWinConsoleWriter(console);
      -                } else {
      -                    if (type == null) {
      -                        type = TYPE_WINDOWS;
      -                    }
      -                    writer = new WindowsAnsiWriter(new BufferedWriter(new JnaWinConsoleWriter(console)), console);
      -                }
      +            } else if (OSUtils.IS_CONEMU) {
      +                type = type != null ? type : TYPE_WINDOWS_CONEMU;
      +                writer = new JnaWinConsoleWriter(console);
      +            } else {
      +                type = type != null ? type : TYPE_WINDOWS;
      +                writer = new WindowsAnsiWriter(new BufferedWriter(new JnaWinConsoleWriter(console)), console);
                   }
               }
      -        JnaWinSysTerminal terminal = new JnaWinSysTerminal(writer, name, type, encoding, nativeSignals, signalHandler, inputStreamWrapper);
      +        // Create terminal
      +        JnaWinSysTerminal terminal = new JnaWinSysTerminal(
      +                provider,
      +                systemStream,
      +                writer,
      +                name,
      +                type,
      +                encoding,
      +                nativeSignals,
      +                signalHandler,
      +                JnaWinSysTerminal.consoleIn,
      +                inMode.getValue(),
      +                console,
      +                outMode.getValue(),
      +                inputStreamWrapper);
               // Start input pump thread
               if (!paused) {
                   terminal.resume();
      @@ -81,15 +103,32 @@ public static JnaWinSysTerminal createTerminal(String name, String type, boolean
               return terminal;
           }
       
      -    public static boolean isWindowsSystemStream(TerminalProvider.Stream stream) {
      +    private static boolean enableVtp(Pointer console, int outMode) {
      +        try {
      +            Kernel32.INSTANCE.SetConsoleMode(
      +                    console, outMode | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING);
      +            return true;
      +        } catch (LastErrorException e) {
      +            return false;
      +        }
      +    }
      +
      +    public static boolean isWindowsSystemStream(SystemStream stream) {
               try {
                   IntByReference mode = new IntByReference();
                   Pointer console;
                   switch (stream) {
      -                case Input: console = consoleIn; break;
      -                case Output: console = consoleOut; break;
      -                case Error: console = consoleErr; break;
      -                default: return false;
      +                case Input:
      +                    console = consoleIn;
      +                    break;
      +                case Output:
      +                    console = consoleOut;
      +                    break;
      +                case Error:
      +                    console = consoleErr;
      +                    break;
      +                default:
      +                    return false;
                   }
                   Kernel32.INSTANCE.GetConsoleMode(console, mode);
                   return true;
      @@ -98,27 +137,53 @@ public static boolean isWindowsSystemStream(TerminalProvider.Stream stream) {
               }
           }
       
      -    JnaWinSysTerminal(Writer writer, String name, String type, Charset encoding, boolean nativeSignals, SignalHandler signalHandler,
      -            Function inputStreamWrapper) throws IOException {
      -        super(writer, name, type, encoding, nativeSignals, signalHandler, inputStreamWrapper);
      -        strings.put(InfoCmp.Capability.key_mouse, "\\E[M");
      +    JnaWinSysTerminal(
      +            TerminalProvider provider,
      +            SystemStream systemStream,
      +            Writer writer,
      +            String name,
      +            String type,
      +            Charset encoding,
      +            boolean nativeSignals,
      +            SignalHandler signalHandler,
      +            Pointer inConsole,
      +            int inConsoleMode,
      +            Pointer outConsole,
      +            int outConsoleMode,
      +            Function inputStreamWrapper)
      +            throws IOException {
      +        super(
      +                provider,
      +                systemStream,
      +                writer,
      +                name,
      +                type,
      +                encoding,
      +                nativeSignals,
      +                signalHandler,
      +                inConsole,
      +                inConsoleMode,
      +                outConsole,
      +                outConsoleMode,
      +                inputStreamWrapper);
      +        this.strings.put(Capability.key_mouse, "\\E[M");
           }
       
           @Override
      -    protected int getConsoleMode() {
      +    protected int getConsoleMode(Pointer console) {
               IntByReference mode = new IntByReference();
      -        Kernel32.INSTANCE.GetConsoleMode(consoleIn, mode);
      +        Kernel32.INSTANCE.GetConsoleMode(console, mode);
               return mode.getValue();
           }
       
           @Override
      -    protected void setConsoleMode(int mode) {
      -        Kernel32.INSTANCE.SetConsoleMode(consoleIn, mode);
      +    protected void setConsoleMode(Pointer console, int mode) {
      +        Kernel32.INSTANCE.SetConsoleMode(console, mode);
           }
       
           public Size getSize() {
               Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
      -        Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info);
      +        Kernel32.INSTANCE.GetConsoleScreenBufferInfo(outConsole, info);
               return new Size(info.windowWidth(), info.windowHeight());
           }
       
      @@ -154,10 +219,11 @@ protected boolean processConsoleInput() throws IOException {
           }
       
           private void processKeyEvent(Kernel32.KEY_EVENT_RECORD keyEvent) throws IOException {
      -        processKeyEvent(keyEvent.bKeyDown, keyEvent.wVirtualKeyCode, keyEvent.uChar.UnicodeChar, keyEvent.dwControlKeyState);
      +        processKeyEvent(
      +                keyEvent.bKeyDown, keyEvent.wVirtualKeyCode, keyEvent.uChar.UnicodeChar, keyEvent.dwControlKeyState);
           }
       
      -    private char[] focus = new char[] { '\033', '[', ' ' };
      +    private char[] focus = new char[] {'\033', '[', ' '};
       
           private void processFocusEvent(boolean hasFocus) throws IOException {
               if (focusTracking) {
      @@ -166,7 +232,7 @@ private void processFocusEvent(boolean hasFocus) throws IOException {
               }
           }
       
      -    private char[] mouse = new char[] { '\033', '[', 'M', ' ', ' ', ' ' };
      +    private char[] mouse = new char[] {'\033', '[', 'M', ' ', ' ', ' '};
       
           private void processMouseEvent(Kernel32.MOUSE_EVENT_RECORD mouseEvent) throws IOException {
               int dwEventFlags = mouseEvent.dwEventFlags;
      @@ -177,7 +243,7 @@ private void processMouseEvent(Kernel32.MOUSE_EVENT_RECORD mouseEvent) throws IO
                   return;
               }
               int cb = 0;
      -        dwEventFlags &= ~ Kernel32.DOUBLE_CLICK; // Treat double-clicks as normal
      +        dwEventFlags &= ~Kernel32.DOUBLE_CLICK; // Treat double-clicks as normal
               if (dwEventFlags == Kernel32.MOUSE_WHEELED) {
                   cb |= 64;
                   if ((dwButtonState >> 16) < 0) {
      @@ -223,5 +289,4 @@ public Cursor getCursorPosition(IntConsumer discarded) {
               Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info);
               return new Cursor(info.dwCursorPosition.X, info.dwCursorPosition.Y);
           }
      -
       }
      diff --git a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/Kernel32.java b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/Kernel32.java
      index 6f350501ad44c..d3e20364a8a1a 100644
      --- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/Kernel32.java
      +++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/Kernel32.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2002-2018, the original author or authors.
      + * Copyright (c) 2002-2018, the original author(s).
        *
        * This software is distributable under the BSD license. See the terms of the
        * BSD license in the documentation provided with this software.
      @@ -30,46 +30,46 @@ interface Kernel32 {//extends StdCallLibrary {
       
       //    Pointer INVALID_HANDLE_VALUE = Pointer.createConstant(-1L);
       
      -    int STD_INPUT_HANDLE =  -10;
      +    int STD_INPUT_HANDLE = -10;
           int STD_OUTPUT_HANDLE = -11;
      -    int STD_ERROR_HANDLE =  -12;
      -
      -    int ENABLE_PROCESSED_INPUT =    0x0001;
      -    int ENABLE_LINE_INPUT =         0x0002;
      -    int ENABLE_ECHO_INPUT =         0x0004;
      -    int ENABLE_WINDOW_INPUT =       0x0008;
      -    int ENABLE_MOUSE_INPUT =        0x0010;
      -    int ENABLE_INSERT_MODE =        0x0020;
      -    int ENABLE_QUICK_EDIT_MODE =    0x0040;
      -    int ENABLE_EXTENDED_FLAGS =     0x0080;
      -
      -    int RIGHT_ALT_PRESSED =     0x0001;
      -    int LEFT_ALT_PRESSED =      0x0002;
      -    int RIGHT_CTRL_PRESSED =    0x0004;
      -    int LEFT_CTRL_PRESSED =     0x0008;
      -    int SHIFT_PRESSED =         0x0010;
      -
      -    int FOREGROUND_BLUE =       0x0001;
      -    int FOREGROUND_GREEN =      0x0002;
      -    int FOREGROUND_RED =        0x0004;
      -    int FOREGROUND_INTENSITY =  0x0008;
      -    int BACKGROUND_BLUE =       0x0010;
      -    int BACKGROUND_GREEN =      0x0020;
      -    int BACKGROUND_RED =        0x0040;
      -    int BACKGROUND_INTENSITY =  0x0080;
      +    int STD_ERROR_HANDLE = -12;
      +
      +    int ENABLE_PROCESSED_INPUT = 0x0001;
      +    int ENABLE_LINE_INPUT = 0x0002;
      +    int ENABLE_ECHO_INPUT = 0x0004;
      +    int ENABLE_WINDOW_INPUT = 0x0008;
      +    int ENABLE_MOUSE_INPUT = 0x0010;
      +    int ENABLE_INSERT_MODE = 0x0020;
      +    int ENABLE_QUICK_EDIT_MODE = 0x0040;
      +    int ENABLE_EXTENDED_FLAGS = 0x0080;
      +
      +    int RIGHT_ALT_PRESSED = 0x0001;
      +    int LEFT_ALT_PRESSED = 0x0002;
      +    int RIGHT_CTRL_PRESSED = 0x0004;
      +    int LEFT_CTRL_PRESSED = 0x0008;
      +    int SHIFT_PRESSED = 0x0010;
      +
      +    int FOREGROUND_BLUE = 0x0001;
      +    int FOREGROUND_GREEN = 0x0002;
      +    int FOREGROUND_RED = 0x0004;
      +    int FOREGROUND_INTENSITY = 0x0008;
      +    int BACKGROUND_BLUE = 0x0010;
      +    int BACKGROUND_GREEN = 0x0020;
      +    int BACKGROUND_RED = 0x0040;
      +    int BACKGROUND_INTENSITY = 0x0080;
       
           // Button state
           int FROM_LEFT_1ST_BUTTON_PRESSED = 0x0001;
      -    int RIGHTMOST_BUTTON_PRESSED     = 0x0002;
      +    int RIGHTMOST_BUTTON_PRESSED = 0x0002;
           int FROM_LEFT_2ND_BUTTON_PRESSED = 0x0004;
           int FROM_LEFT_3RD_BUTTON_PRESSED = 0x0008;
           int FROM_LEFT_4TH_BUTTON_PRESSED = 0x0010;
       
           // Event flags
      -    int MOUSE_MOVED                  = 0x0001;
      -    int DOUBLE_CLICK                 = 0x0002;
      -    int MOUSE_WHEELED                = 0x0004;
      -    int MOUSE_HWHEELED               = 0x0008;
      +    int MOUSE_MOVED = 0x0001;
      +    int DOUBLE_CLICK = 0x0002;
      +    int MOUSE_WHEELED = 0x0004;
      +    int MOUSE_HWHEELED = 0x0008;
       
           // DWORD WINAPI WaitForSingleObject(
           //  _In_ HANDLE hHandle,
      @@ -94,18 +94,18 @@ interface Kernel32 {//extends StdCallLibrary {
       //    // UINT WINAPI GetConsoleCP(void)
       //    int GetConsoleCP();
       //
      -    // UINT WINAPI GetConsoleOutputCP(void)
      -    int GetConsoleOutputCP();
      -
           // BOOL WINAPI FillConsoleOutputCharacter(
           // _In_ HANDLE hConsoleOutput,
           // _In_ TCHAR cCharacter,
           // _In_ DWORD nLength,
           // _In_ COORD dwWriteCoord,
           // _Out_ LPDWORD lpNumberOfCharsWritten);
      -    void FillConsoleOutputCharacter(Pointer in_hConsoleOutput,
      -                                    char in_cCharacter, int in_nLength, COORD in_dwWriteCoord,
      -                                    IntByReference out_lpNumberOfCharsWritten)
      +    void FillConsoleOutputCharacter(
      +            Pointer in_hConsoleOutput,
      +            char in_cCharacter,
      +            int in_nLength,
      +            COORD in_dwWriteCoord,
      +            IntByReference out_lpNumberOfCharsWritten)
                   throws LastErrorException;
       
           // BOOL WINAPI FillConsoleOutputAttribute(
      @@ -114,56 +114,53 @@ void FillConsoleOutputCharacter(Pointer in_hConsoleOutput,
           // _In_ DWORD nLength,
           // _In_ COORD dwWriteCoord,
           // _Out_ LPDWORD lpNumberOfAttrsWritten);
      -    void FillConsoleOutputAttribute(Pointer in_hConsoleOutput,
      -                                    short in_wAttribute, int in_nLength, COORD in_dwWriteCoord,
      -                                    IntByReference out_lpNumberOfAttrsWritten)
      +    void FillConsoleOutputAttribute(
      +            Pointer in_hConsoleOutput,
      +            short in_wAttribute,
      +            int in_nLength,
      +            COORD in_dwWriteCoord,
      +            IntByReference out_lpNumberOfAttrsWritten)
                   throws LastErrorException;
      -//
      -////    // BOOL WINAPI GetConsoleCursorInfo(
      -////    // _In_ HANDLE hConsoleOutput,
      -////    // _Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo);
      -////    void GetConsoleCursorInfo(Pointer in_hConsoleOutput,
      -////                              CONSOLE_CURSOR_INFO.ByReference out_lpConsoleCursorInfo)
      -////            throws LastErrorException;
      -//
      +
      +//    // BOOL WINAPI GetConsoleCursorInfo(
      +//    // _In_ HANDLE hConsoleOutput,
      +//    // _Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo);
      +//    void GetConsoleCursorInfo(Pointer in_hConsoleOutput, CONSOLE_CURSOR_INFO.ByReference out_lpConsoleCursorInfo)
      +//            throws LastErrorException;
      +
           // BOOL WINAPI GetConsoleMode(
           //   _In_   HANDLE hConsoleHandle,
           //   _Out_  LPDWORD lpMode);
      -    void GetConsoleMode(
      -            Pointer in_hConsoleOutput,
      -            IntByReference out_lpMode)
      -            throws LastErrorException;
      +    void GetConsoleMode(Pointer in_hConsoleOutput, IntByReference out_lpMode) throws LastErrorException;
       
           // BOOL WINAPI GetConsoleScreenBufferInfo(
           // _In_   HANDLE hConsoleOutput,
           // _Out_  PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
      -    void GetConsoleScreenBufferInfo(
      -            Pointer in_hConsoleOutput,
      -            CONSOLE_SCREEN_BUFFER_INFO out_lpConsoleScreenBufferInfo)
      +    void GetConsoleScreenBufferInfo(Pointer in_hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFO out_lpConsoleScreenBufferInfo)
                   throws LastErrorException;
       //
       //    // BOOL WINAPI GetNumberOfConsoleInputEvents(
       //    // _In_ HANDLE hConsoleInput,
       //    // _Out_ LPDWORD lpcNumberOfEvents);
      -//    void GetNumberOfConsoleInputEvents(Pointer in_hConsoleOutput,
      -//                                       IntByReference out_lpcNumberOfEvents) throws LastErrorException;
      +//    void GetNumberOfConsoleInputEvents(Pointer in_hConsoleOutput, IntByReference out_lpcNumberOfEvents)
      +//            throws LastErrorException;
       //
           // BOOL WINAPI ReadConsoleInput(
           // _In_ HANDLE hConsoleInput,
           // _Out_ PINPUT_RECORD lpBuffer,
           // _In_ DWORD nLength,
           // _Out_ LPDWORD lpNumberOfEventsRead);
      -    void ReadConsoleInput(Pointer in_hConsoleOutput,
      -                          INPUT_RECORD[] out_lpBuffer, int in_nLength,
      -                          IntByReference out_lpNumberOfEventsRead) throws LastErrorException;
      +    void ReadConsoleInput(
      +            Pointer in_hConsoleOutput,
      +            INPUT_RECORD[] out_lpBuffer,
      +            int in_nLength,
      +            IntByReference out_lpNumberOfEventsRead)
      +            throws LastErrorException;
       
       //    // BOOL WINAPI SetConsoleCtrlHandler(
       //    // _In_opt_  PHANDLER_ROUTINE HandlerRoutine,
       //    // _In_      BOOL Add);
      -//    void SetConsoleCtrlHandler(
      -//            Pointer in_opt_HandlerRoutine,
      -//            boolean in_Add)
      -//            throws LastErrorException;
      +//    void SetConsoleCtrlHandler(Pointer in_opt_HandlerRoutine, boolean in_Add) throws LastErrorException;
       //
       //    // BOOL WINAPI ReadConsoleOutput(
       //    // _In_     HANDLE hConsoleOutput,
      @@ -171,12 +168,21 @@ void ReadConsoleInput(Pointer in_hConsoleOutput,
       //    // _In_     COORD dwBufferSize,
       //    // _In_     COORD dwBufferCoord,
       //    // _Inout_  PSMALL_RECT lpReadRegion);
      -////    void ReadConsoleOutput(Pointer in_hConsoleOutput, CHAR_INFO[] out_lpBuffer,
      -////                           COORD in_dwBufferSize, COORD in_dwBufferCoord,
      -////                           SMALL_RECT inout_lpReadRegion) throws LastErrorException;
      -////    void ReadConsoleOutputA(Pointer in_hConsoleOutput, CHAR_INFO[] out_lpBuffer,
      -////                            COORD in_dwBufferSize, COORD in_dwBufferCoord,
      -////                            SMALL_RECT inout_lpReadRegion) throws LastErrorException;
      +//    void ReadConsoleOutput(
      +//            Pointer in_hConsoleOutput,
      +//            CHAR_INFO[] out_lpBuffer,
      +//            COORD in_dwBufferSize,
      +//            COORD in_dwBufferCoord,
      +//            SMALL_RECT inout_lpReadRegion)
      +//            throws LastErrorException;
      +//
      +//    void ReadConsoleOutputA(
      +//            Pointer in_hConsoleOutput,
      +//            CHAR_INFO[] out_lpBuffer,
      +//            COORD in_dwBufferSize,
      +//            COORD in_dwBufferCoord,
      +//            SMALL_RECT inout_lpReadRegion)
      +//            throws LastErrorException;
       //
       //    // BOOL WINAPI ReadConsoleOutputCharacter(
       //    // _In_   HANDLE hConsoleOutput,
      @@ -184,20 +190,26 @@ void ReadConsoleInput(Pointer in_hConsoleOutput,
       //    // _In_   DWORD nLength,
       //    // _In_   COORD dwReadCoord,
       //    // _Out_  LPDWORD lpNumberOfCharsRead);
      -//    void ReadConsoleOutputCharacter(Pointer in_hConsoleOutput,
      -//                                    char[] ouy_lpCharacter, int in_nLength, COORD in_dwReadCoord,
      -//                                    IntByReference out_lpNumberOfCharsRead)
      +//    void ReadConsoleOutputCharacter(
      +//            Pointer in_hConsoleOutput,
      +//            char[] ouy_lpCharacter,
      +//            int in_nLength,
      +//            COORD in_dwReadCoord,
      +//            IntByReference out_lpNumberOfCharsRead)
       //            throws LastErrorException;
      -//    void ReadConsoleOutputCharacterA(Pointer in_hConsoleOutput,
      -//                                     byte[] ouy_lpCharacter, int in_nLength, COORD in_dwReadCoord,
      -//                                     IntByReference out_lpNumberOfCharsRead)
      +//
      +//    void ReadConsoleOutputCharacterA(
      +//            Pointer in_hConsoleOutput,
      +//            byte[] ouy_lpCharacter,
      +//            int in_nLength,
      +//            COORD in_dwReadCoord,
      +//            IntByReference out_lpNumberOfCharsRead)
       //            throws LastErrorException;
       //
       //    // BOOL WINAPI SetConsoleCursorInfo(
       //    // _In_ HANDLE hConsoleOutput,
       //    // _In_ const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo);
      -//    void SetConsoleCursorInfo(Pointer in_hConsoleOutput,
      -//                              CONSOLE_CURSOR_INFO in_lpConsoleCursorInfo)
      +//    void SetConsoleCursorInfo(Pointer in_hConsoleOutput, CONSOLE_CURSOR_INFO in_lpConsoleCursorInfo)
       //            throws LastErrorException;
       //
       //    // BOOL WINAPI SetConsoleCP(
      @@ -207,47 +219,39 @@ void ReadConsoleInput(Pointer in_hConsoleOutput,
       //    // BOOL WINAPI SetConsoleOutputCP(
       //    // _In_ UINT wCodePageID);
       //    void SetConsoleOutputCP(int in_wCodePageID) throws LastErrorException;
      -//
      +
           // BOOL WINAPI SetConsoleCursorPosition(
           // _In_ HANDLE hConsoleOutput,
           // _In_ COORD dwCursorPosition);
      -    void SetConsoleCursorPosition(Pointer in_hConsoleOutput,
      -                                  COORD in_dwCursorPosition) throws LastErrorException;
      +    void SetConsoleCursorPosition(Pointer in_hConsoleOutput, COORD in_dwCursorPosition) throws LastErrorException;
       
           // BOOL WINAPI SetConsoleMode(
           //   _In_  HANDLE hConsoleHandle,
           //   _In_  DWORD dwMode);
      -    void SetConsoleMode(
      -            Pointer in_hConsoleOutput,
      -            int in_dwMode) throws LastErrorException;
      +    void SetConsoleMode(Pointer in_hConsoleOutput, int in_dwMode) throws LastErrorException;
       
       //    // BOOL WINAPI SetConsoleScreenBufferSize(
       //    // __in HANDLE hConsoleOutput,
       //    // __in COORD dwSize
       //    // );
      -//    void SetConsoleScreenBufferSize(Pointer in_hConsoleOutput,
      -//                                    COORD in_dwSize) throws LastErrorException;
      -//
      +//    void SetConsoleScreenBufferSize(Pointer in_hConsoleOutput, COORD in_dwSize) throws LastErrorException;
      +
           // BOOL WINAPI SetConsoleTextAttribute(
           // _In_ HANDLE hConsoleOutput,
           // _In_ WORD   wAttributes
           // );
      -    void SetConsoleTextAttribute(Pointer in_hConsoleOutput,
      -                                 short in_wAttributes)
      -            throws LastErrorException;
      +    void SetConsoleTextAttribute(Pointer in_hConsoleOutput, short in_wAttributes) throws LastErrorException;
       
           // BOOL WINAPI SetConsoleTitle(
           // _In_ LPCTSTR lpConsoleTitle
           // );
      -    void SetConsoleTitle(String in_lpConsoleTitle)
      -            throws LastErrorException;
      +    void SetConsoleTitle(String in_lpConsoleTitle) throws LastErrorException;
       
       //    // BOOL WINAPI SetConsoleWindowInfo(
       //    // _In_ HANDLE hConsoleOutput,
       //    // _In_ BOOL bAbsolute,
       //    // _In_ const SMALL_RECT *lpConsoleWindow);
      -//    void SetConsoleWindowInfo(Pointer in_hConsoleOutput,
      -//                              boolean in_bAbsolute, SMALL_RECT in_lpConsoleWindow)
      +//    void SetConsoleWindowInfo(Pointer in_hConsoleOutput, boolean in_bAbsolute, SMALL_RECT in_lpConsoleWindow)
       //            throws LastErrorException;
       
           // BOOL WINAPI WriteConsole(
      @@ -257,8 +261,13 @@ void SetConsoleTitle(String in_lpConsoleTitle)
           //  _Out_            LPDWORD lpNumberOfCharsWritten,
           //  _Reserved_       LPVOID  lpReserved
           // );
      -    void WriteConsoleW(Pointer in_hConsoleOutput, char[] in_lpBuffer, int in_nNumberOfCharsToWrite,
      -                          IntByReference out_lpNumberOfCharsWritten, Pointer reserved_lpReserved) throws LastErrorException;
      +    void WriteConsoleW(
      +            Pointer in_hConsoleOutput,
      +            char[] in_lpBuffer,
      +            int in_nNumberOfCharsToWrite,
      +            IntByReference out_lpNumberOfCharsWritten,
      +            Pointer reserved_lpReserved)
      +            throws LastErrorException;
       
       //    // BOOL WINAPI WriteConsoleOutput(
       //    // _In_ HANDLE hConsoleOutput,
      @@ -266,12 +275,21 @@ void WriteConsoleW(Pointer in_hConsoleOutput, char[] in_lpBuffer, int in_nNumber
       //    // _In_ COORD dwBufferSize,
       //    // _In_ COORD dwBufferCoord,
       //    // _Inout_ PSMALL_RECT lpWriteRegion);
      -////    void WriteConsoleOutput(Pointer in_hConsoleOutput, CHAR_INFO[] in_lpBuffer,
      -////                            COORD in_dwBufferSize, COORD in_dwBufferCoord,
      -////                            SMALL_RECT inout_lpWriteRegion) throws LastErrorException;
      -////    void WriteConsoleOutputA(Pointer in_hConsoleOutput, CHAR_INFO[] in_lpBuffer,
      -////                             COORD in_dwBufferSize, COORD in_dwBufferCoord,
      -////                             SMALL_RECT inout_lpWriteRegion) throws LastErrorException;
      +//    void WriteConsoleOutput(
      +//            Pointer in_hConsoleOutput,
      +//            CHAR_INFO[] in_lpBuffer,
      +//            COORD in_dwBufferSize,
      +//            COORD in_dwBufferCoord,
      +//            SMALL_RECT inout_lpWriteRegion)
      +//            throws LastErrorException;
      +//
      +//    void WriteConsoleOutputA(
      +//            Pointer in_hConsoleOutput,
      +//            CHAR_INFO[] in_lpBuffer,
      +//            COORD in_dwBufferSize,
      +//            COORD in_dwBufferCoord,
      +//            SMALL_RECT inout_lpWriteRegion)
      +//            throws LastErrorException;
       //
       //    // BOOL WINAPI WriteConsoleOutputCharacter(
       //    // _In_ HANDLE hConsoleOutput,
      @@ -279,26 +297,34 @@ void WriteConsoleW(Pointer in_hConsoleOutput, char[] in_lpBuffer, int in_nNumber
       //    // _In_ DWORD nLength,
       //    // _In_ COORD dwWriteCoord,
       //    // _Out_ LPDWORD lpNumberOfCharsWritten);
      -//    void WriteConsoleOutputCharacter(Pointer in_hConsoleOutput,
      -//                                     char[] in_lpCharacter, int in_nLength, COORD in_dwWriteCoord,
      -//                                     IntByReference out_lpNumberOfCharsWritten)
      -//            throws LastErrorException;
      -//    void WriteConsoleOutputCharacterA(Pointer in_hConsoleOutput,
      -//                                      byte[] in_lpCharacter, int in_nLength, COORD in_dwWriteCoord,
      -//                                      IntByReference out_lpNumberOfCharsWritten)
      +//    void WriteConsoleOutputCharacter(
      +//            Pointer in_hConsoleOutput,
      +//            char[] in_lpCharacter,
      +//            int in_nLength,
      +//            COORD in_dwWriteCoord,
      +//            IntByReference out_lpNumberOfCharsWritten)
       //            throws LastErrorException;
       //
      +//    void WriteConsoleOutputCharacterA(
      +//            Pointer in_hConsoleOutput,
      +//            byte[] in_lpCharacter,
      +//            int in_nLength,
      +//            COORD in_dwWriteCoord,
      +//            IntByReference out_lpNumberOfCharsWritten)
      +//            throws LastErrorException;
      +
           // BOOL WINAPI ScrollConsoleScreenBuffer(
           //     _In_           HANDLE     hConsoleOutput,
           //     _In_     const SMALL_RECT *lpScrollRectangle,
           //     _In_opt_ const SMALL_RECT *lpClipRectangle,
           //     _In_           COORD      dwDestinationOrigin,
           //     _In_     const CHAR_INFO  *lpFill);
      -    void ScrollConsoleScreenBuffer(Pointer in_hConsoleOutput,
      -                                   SMALL_RECT in_lpScrollRectangle,
      -                                   SMALL_RECT in_lpClipRectangle,
      -                                   COORD in_dwDestinationOrigin,
      -                                   CHAR_INFO in_lpFill)
      +    void ScrollConsoleScreenBuffer(
      +            Pointer in_hConsoleOutput,
      +            SMALL_RECT in_lpScrollRectangle,
      +            SMALL_RECT in_lpClipRectangle,
      +            COORD in_dwDestinationOrigin,
      +            CHAR_INFO in_lpFill)
                   throws LastErrorException;
       
           // typedef struct _CHAR_INFO {
      @@ -309,8 +335,7 @@ void ScrollConsoleScreenBuffer(Pointer in_hConsoleOutput,
           //   WORD  Attributes;
           // } CHAR_INFO, *PCHAR_INFO;
           class CHAR_INFO {//extends Structure {
      -        public CHAR_INFO() {
      -        }
      +        public CHAR_INFO() {}
       
               public CHAR_INFO(char c, short attr) {
                   uChar = new UnionChar(c);
      @@ -329,7 +354,7 @@ public CHAR_INFO(char c, short attr) {
       //            return (CHAR_INFO[]) new CHAR_INFO().toArray(size);
       //        }
       //
      -//        private static String[] fieldOrder = { "uChar", "Attributes" };
      +//        private static String[] fieldOrder = {"uChar", "Attributes"};
       //
       //        @Override
       //        protected java.util.List getFieldOrder() {
      @@ -345,11 +370,9 @@ class CONSOLE_CURSOR_INFO {//extends Structure {
               public int dwSize;
               public boolean bVisible;
       
      -//        public static class ByReference extends CONSOLE_CURSOR_INFO implements
      -//                Structure.ByReference {
      -//        }
      +//        public static class ByReference extends CONSOLE_CURSOR_INFO implements Structure.ByReference {}
       //
      -//        private static String[] fieldOrder = { "dwSize", "bVisible" };
      +//        private static String[] fieldOrder = {"dwSize", "bVisible"};
       //
       //        @Override
       //        protected java.util.List getFieldOrder() {
      @@ -365,13 +388,15 @@ class CONSOLE_CURSOR_INFO {//extends Structure {
           //   COORD      dwMaximumWindowSize;
           // } CONSOLE_SCREEN_BUFFER_INFO;
           class CONSOLE_SCREEN_BUFFER_INFO {//extends Structure {
      -        public COORD      dwSize;
      -        public COORD      dwCursorPosition;
      -        public short      wAttributes;
      +        public COORD dwSize;
      +        public COORD dwCursorPosition;
      +        public short wAttributes;
               public SMALL_RECT srWindow;
      -        public COORD      dwMaximumWindowSize;
      +        public COORD dwMaximumWindowSize;
       
      -//        private static String[] fieldOrder = { "dwSize", "dwCursorPosition", "wAttributes", "srWindow", "dwMaximumWindowSize" };
      +//        private static String[] fieldOrder = {
      +//            "dwSize", "dwCursorPosition", "wAttributes", "srWindow", "dwMaximumWindowSize"
      +//        };
       //
       //        @Override
       //        protected java.util.List getFieldOrder() {
      @@ -392,8 +417,7 @@ public int windowHeight() {
           //    SHORT Y;
           //  } COORD, *PCOORD;
           class COORD {//extends Structure implements Structure.ByValue {
      -        public COORD() {
      -        }
      +        public COORD() {}
       
               public COORD(short X, short Y) {
                   this.X = X;
      @@ -403,7 +427,7 @@ public COORD(short X, short Y) {
               public short X;
               public short Y;
       
      -//        private static String[] fieldOrder = { "X", "Y" };
      +//        private static String[] fieldOrder = {"X", "Y"};
       //
       //        @Override
       //        protected java.util.List getFieldOrder() {
      @@ -489,7 +513,9 @@ class KEY_EVENT_RECORD {//extends Structure {
               public UnionChar uChar;
               public int dwControlKeyState;
       
      -//        private static String[] fieldOrder = {"bKeyDown", "wRepeatCount", "wVirtualKeyCode", "wVirtualScanCode", "uChar", "dwControlKeyState"};
      +//        private static String[] fieldOrder = {
      +//            "bKeyDown", "wRepeatCount", "wVirtualKeyCode", "wVirtualScanCode", "uChar", "dwControlKeyState"
      +//        };
       //
       //        @Override
       //        protected java.util.List getFieldOrder() {
      @@ -509,7 +535,7 @@ class MOUSE_EVENT_RECORD {//extends Structure {
               public int dwControlKeyState;
               public int dwEventFlags;
       
      -//        private static String[] fieldOrder = { "dwMousePosition", "dwButtonState", "dwControlKeyState", "dwEventFlags"};
      +//        private static String[] fieldOrder = {"dwMousePosition", "dwButtonState", "dwControlKeyState", "dwEventFlags"};
       //
       //        @Override
       //        protected java.util.List getFieldOrder() {
      @@ -548,7 +574,7 @@ class MENU_EVENT_RECORD {//extends Structure {
       
           // typedef struct _FOCUS_EVENT_RECORD {
           //  BOOL bSetFocus;
      -    //} FOCUS_EVENT_RECORD;
      +    // } FOCUS_EVENT_RECORD;
           class FOCUS_EVENT_RECORD {//extends Structure {
               public boolean bSetFocus;
       
      @@ -567,8 +593,7 @@ class FOCUS_EVENT_RECORD {//extends Structure {
           //    SHORT Bottom;
           //  } SMALL_RECT;
           class SMALL_RECT {//extends Structure {
      -        public SMALL_RECT() {
      -        }
      +        public SMALL_RECT() {}
       
               public SMALL_RECT(SMALL_RECT org) {
                   this(org.Top, org.Left, org.Bottom, org.Right);
      @@ -586,7 +611,7 @@ public SMALL_RECT(short Top, short Left, short Bottom, short Right) {
               public short Right;
               public short Bottom;
       
      -//        private static String[] fieldOrder = { "Left", "Top", "Right", "Bottom" };
      +//        private static String[] fieldOrder = {"Left", "Top", "Right", "Bottom"};
       //
       //        @Override
       //        protected java.util.List getFieldOrder() {
      @@ -594,18 +619,16 @@ public SMALL_RECT(short Top, short Left, short Bottom, short Right) {
       //        }
       
               public short width() {
      -            return (short)(this.Right - this.Left);
      +            return (short) (this.Right - this.Left);
               }
       
               public short height() {
      -            return (short)(this.Bottom - this.Top);
      +            return (short) (this.Bottom - this.Top);
               }
      -
           }
       
           class UnionChar {//extends Union {
      -        public UnionChar() {
      -        }
      +        public UnionChar() {}
       
               public UnionChar(char c) {
       //            setType(char.class);
      diff --git a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/Kernel32Impl.java b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/Kernel32Impl.java
      index 793e1e86fc483..2a1120bc5a10a 100644
      --- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/Kernel32Impl.java
      +++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/Kernel32Impl.java
      @@ -45,9 +45,6 @@ public class Kernel32Impl implements Kernel32 {
           @Override
           public native Pointer GetStdHandle(int nStdHandle);
       
      -    @Override
      -    public native int GetConsoleOutputCP();
      -
           @Override
           public native void FillConsoleOutputCharacter(Pointer in_hConsoleOutput, char in_cCharacter, int in_nLength, COORD in_dwWriteCoord, IntByReference out_lpNumberOfCharsWritten) throws LastErrorException;
       
      diff --git a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/WindowsAnsiWriter.java b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/WindowsAnsiWriter.java
      index 7e1b82db8053b..6d3f217cbcd5a 100644
      --- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/WindowsAnsiWriter.java
      +++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/WindowsAnsiWriter.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2002-2016, the original author or authors.
      + * Copyright (c) 2002-2016, the original author(s).
        *
        * This software is distributable under the BSD license. See the terms of the
        * BSD license in the documentation provided with this software.
      @@ -11,11 +11,12 @@
       import java.io.IOException;
       import java.io.Writer;
       
      -//import com.sun.jna.Pointer;
      -//import com.sun.jna.ptr.IntByReference;
       import jdk.internal.org.jline.utils.AnsiWriter;
       import jdk.internal.org.jline.utils.Colors;
       
      +//import com.sun.jna.Pointer;
      +//import com.sun.jna.ptr.IntByReference;
      +
       import static jdk.internal.org.jline.terminal.impl.jna.win.Kernel32.BACKGROUND_BLUE;
       import static jdk.internal.org.jline.terminal.impl.jna.win.Kernel32.BACKGROUND_GREEN;
       import static jdk.internal.org.jline.terminal.impl.jna.win.Kernel32.BACKGROUND_INTENSITY;
      @@ -25,7 +26,6 @@
       import static jdk.internal.org.jline.terminal.impl.jna.win.Kernel32.FOREGROUND_INTENSITY;
       import static jdk.internal.org.jline.terminal.impl.jna.win.Kernel32.FOREGROUND_RED;
       
      -
       /**
        * A Windows ANSI escape processor, uses JNA to access native platform
        * API's to change the console attributes.
      @@ -36,41 +36,41 @@
        */
       public final class WindowsAnsiWriter extends AnsiWriter {
       
      -    private static final short FOREGROUND_BLACK   = 0;
      -    private static final short FOREGROUND_YELLOW  = (short) (FOREGROUND_RED|FOREGROUND_GREEN);
      -    private static final short FOREGROUND_MAGENTA = (short) (FOREGROUND_BLUE|FOREGROUND_RED);
      -    private static final short FOREGROUND_CYAN    = (short) (FOREGROUND_BLUE|FOREGROUND_GREEN);
      -    private static final short FOREGROUND_WHITE   = (short) (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
      +    private static final short FOREGROUND_BLACK = 0;
      +    private static final short FOREGROUND_YELLOW = (short) (FOREGROUND_RED | FOREGROUND_GREEN);
      +    private static final short FOREGROUND_MAGENTA = (short) (FOREGROUND_BLUE | FOREGROUND_RED);
      +    private static final short FOREGROUND_CYAN = (short) (FOREGROUND_BLUE | FOREGROUND_GREEN);
      +    private static final short FOREGROUND_WHITE = (short) (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
       
      -    private static final short BACKGROUND_BLACK   = 0;
      -    private static final short BACKGROUND_YELLOW  = (short) (BACKGROUND_RED|BACKGROUND_GREEN);
      -    private static final short BACKGROUND_MAGENTA = (short) (BACKGROUND_BLUE|BACKGROUND_RED);
      -    private static final short BACKGROUND_CYAN    = (short) (BACKGROUND_BLUE|BACKGROUND_GREEN);
      -    private static final short BACKGROUND_WHITE   = (short) (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE);
      +    private static final short BACKGROUND_BLACK = 0;
      +    private static final short BACKGROUND_YELLOW = (short) (BACKGROUND_RED | BACKGROUND_GREEN);
      +    private static final short BACKGROUND_MAGENTA = (short) (BACKGROUND_BLUE | BACKGROUND_RED);
      +    private static final short BACKGROUND_CYAN = (short) (BACKGROUND_BLUE | BACKGROUND_GREEN);
      +    private static final short BACKGROUND_WHITE = (short) (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
       
           private static final short ANSI_FOREGROUND_COLOR_MAP[] = {
      -            FOREGROUND_BLACK,
      -            FOREGROUND_RED,
      -            FOREGROUND_GREEN,
      -            FOREGROUND_YELLOW,
      -            FOREGROUND_BLUE,
      -            FOREGROUND_MAGENTA,
      -            FOREGROUND_CYAN,
      -            FOREGROUND_WHITE,
      +        FOREGROUND_BLACK,
      +        FOREGROUND_RED,
      +        FOREGROUND_GREEN,
      +        FOREGROUND_YELLOW,
      +        FOREGROUND_BLUE,
      +        FOREGROUND_MAGENTA,
      +        FOREGROUND_CYAN,
      +        FOREGROUND_WHITE,
           };
       
           private static final short ANSI_BACKGROUND_COLOR_MAP[] = {
      -            BACKGROUND_BLACK,
      -            BACKGROUND_RED,
      -            BACKGROUND_GREEN,
      -            BACKGROUND_YELLOW,
      -            BACKGROUND_BLUE,
      -            BACKGROUND_MAGENTA,
      -            BACKGROUND_CYAN,
      -            BACKGROUND_WHITE,
      +        BACKGROUND_BLACK,
      +        BACKGROUND_RED,
      +        BACKGROUND_GREEN,
      +        BACKGROUND_YELLOW,
      +        BACKGROUND_BLUE,
      +        BACKGROUND_MAGENTA,
      +        BACKGROUND_CYAN,
      +        BACKGROUND_WHITE,
           };
       
      -    private final static int MAX_ESCAPE_SEQUENCE_LENGTH = 100;
      +    private static final int MAX_ESCAPE_SEQUENCE_LENGTH = 100;
       
           private final Pointer console;
       
      @@ -134,7 +134,7 @@ private void applyCursorPosition() throws IOException {
           protected void processEraseScreen(int eraseOption) throws IOException {
               getConsoleInfo();
               IntByReference written = new IntByReference();
      -        switch(eraseOption) {
      +        switch (eraseOption) {
                   case ERASE_SCREEN:
                       Kernel32.COORD topLeft = new Kernel32.COORD();
                       topLeft.X = 0;
      @@ -147,36 +147,44 @@ protected void processEraseScreen(int eraseOption) throws IOException {
                       Kernel32.COORD topLeft2 = new Kernel32.COORD();
                       topLeft2.X = 0;
                       topLeft2.Y = info.srWindow.Top;
      -                int lengthToCursor = (info.dwCursorPosition.Y - info.srWindow.Top) * info.dwSize.X + info.dwCursorPosition.X;
      +                int lengthToCursor =
      +                        (info.dwCursorPosition.Y - info.srWindow.Top) * info.dwSize.X + info.dwCursorPosition.X;
                       Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', lengthToCursor, topLeft2, written);
      -                Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, lengthToCursor, topLeft2, written);
      +                Kernel32.INSTANCE.FillConsoleOutputAttribute(
      +                        console, info.wAttributes, lengthToCursor, topLeft2, written);
                       break;
                   case ERASE_SCREEN_TO_END:
      -                int lengthToEnd = (info.srWindow.Bottom - info.dwCursorPosition.Y) * info.dwSize.X +
      -                        (info.dwSize.X - info.dwCursorPosition.X);
      +                int lengthToEnd = (info.srWindow.Bottom - info.dwCursorPosition.Y) * info.dwSize.X
      +                        + (info.dwSize.X - info.dwCursorPosition.X);
                       Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', lengthToEnd, info.dwCursorPosition, written);
      -                Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, lengthToEnd, info.dwCursorPosition, written);
      +                Kernel32.INSTANCE.FillConsoleOutputAttribute(
      +                        console, info.wAttributes, lengthToEnd, info.dwCursorPosition, written);
               }
           }
       
           protected void processEraseLine(int eraseOption) throws IOException {
               getConsoleInfo();
               IntByReference written = new IntByReference();
      -        switch(eraseOption) {
      +        switch (eraseOption) {
                   case ERASE_LINE:
                       Kernel32.COORD leftColCurrRow = new Kernel32.COORD((short) 0, info.dwCursorPosition.Y);
                       Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', info.dwSize.X, leftColCurrRow, written);
      -                Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, info.dwSize.X, leftColCurrRow, written);
      +                Kernel32.INSTANCE.FillConsoleOutputAttribute(
      +                        console, info.wAttributes, info.dwSize.X, leftColCurrRow, written);
                       break;
                   case ERASE_LINE_TO_BEGINING:
                       Kernel32.COORD leftColCurrRow2 = new Kernel32.COORD((short) 0, info.dwCursorPosition.Y);
      -                Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', info.dwCursorPosition.X, leftColCurrRow2, written);
      -                Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, info.dwCursorPosition.X, leftColCurrRow2, written);
      +                Kernel32.INSTANCE.FillConsoleOutputCharacter(
      +                        console, ' ', info.dwCursorPosition.X, leftColCurrRow2, written);
      +                Kernel32.INSTANCE.FillConsoleOutputAttribute(
      +                        console, info.wAttributes, info.dwCursorPosition.X, leftColCurrRow2, written);
                       break;
                   case ERASE_LINE_TO_END:
                       int lengthToLastCol = info.dwSize.X - info.dwCursorPosition.X;
      -                Kernel32.INSTANCE.FillConsoleOutputCharacter(console, ' ', lengthToLastCol, info.dwCursorPosition, written);
      -                Kernel32.INSTANCE.FillConsoleOutputAttribute(console, info.wAttributes, lengthToLastCol, info.dwCursorPosition, written);
      +                Kernel32.INSTANCE.FillConsoleOutputCharacter(
      +                        console, ' ', lengthToLastCol, info.dwCursorPosition, written);
      +                Kernel32.INSTANCE.FillConsoleOutputAttribute(
      +                        console, info.wAttributes, lengthToLastCol, info.dwCursorPosition, written);
               }
           }
       
      @@ -218,7 +226,7 @@ protected void processCursorDown(int count) throws IOException {
                   scroll.Top = 0;
                   Kernel32.COORD org = new Kernel32.COORD();
                   org.X = 0;
      -            org.Y = (short)(- nb);
      +            org.Y = (short) (-nb);
                   Kernel32.CHAR_INFO info = new Kernel32.CHAR_INFO(' ', originalColors);
                   Kernel32.INSTANCE.ScrollConsoleScreenBuffer(console, scroll, scroll, org, info);
               }
      @@ -247,29 +255,31 @@ protected void processCursorToColumn(int x) throws IOException {
           protected void processSetForegroundColorExt(int paletteIndex) throws IOException {
               int color = Colors.roundColor(paletteIndex, 16);
               info.wAttributes = (short) ((info.wAttributes & ~0x0007) | ANSI_FOREGROUND_COLOR_MAP[color & 0x07]);
      -        info.wAttributes = (short) ((info.wAttributes & ~FOREGROUND_INTENSITY) | (color >= 8 ? FOREGROUND_INTENSITY : 0));
      +        info.wAttributes =
      +                (short) ((info.wAttributes & ~FOREGROUND_INTENSITY) | (color >= 8 ? FOREGROUND_INTENSITY : 0));
               applyAttribute();
           }
       
           protected void processSetBackgroundColorExt(int paletteIndex) throws IOException {
               int color = Colors.roundColor(paletteIndex, 16);
      -        info.wAttributes = (short)((info.wAttributes & ~0x0070 ) | ANSI_BACKGROUND_COLOR_MAP[color & 0x07]);
      -        info.wAttributes = (short) ((info.wAttributes & ~BACKGROUND_INTENSITY) | (color >= 8 ? BACKGROUND_INTENSITY : 0));
      +        info.wAttributes = (short) ((info.wAttributes & ~0x0070) | ANSI_BACKGROUND_COLOR_MAP[color & 0x07]);
      +        info.wAttributes =
      +                (short) ((info.wAttributes & ~BACKGROUND_INTENSITY) | (color >= 8 ? BACKGROUND_INTENSITY : 0));
               applyAttribute();
           }
       
           protected void processDefaultTextColor() throws IOException {
      -        info.wAttributes = (short)((info.wAttributes & ~0x000F ) | (originalColors & 0x000F));
      +        info.wAttributes = (short) ((info.wAttributes & ~0x000F) | (originalColors & 0x000F));
               applyAttribute();
           }
       
           protected void processDefaultBackgroundColor() throws IOException {
      -        info.wAttributes = (short)((info.wAttributes & ~0x00F0 ) | (originalColors & 0x00F0));
      +        info.wAttributes = (short) ((info.wAttributes & ~0x00F0) | (originalColors & 0x00F0));
               applyAttribute();
           }
       
           protected void processAttributeRest() throws IOException {
      -        info.wAttributes = (short)((info.wAttributes & ~0x00FF ) | originalColors);
      +        info.wAttributes = (short) ((info.wAttributes & ~0x00FF) | originalColors);
               this.negative = false;
               this.bold = false;
               this.underline = false;
      @@ -277,7 +287,7 @@ protected void processAttributeRest() throws IOException {
           }
       
           protected void processSetAttribute(int attribute) throws IOException {
      -        switch(attribute) {
      +        switch (attribute) {
                   case ATTRIBUTE_INTENSITY_BOLD:
                       bold = true;
                       applyAttribute();
      @@ -330,7 +340,7 @@ protected void processInsertLine(int optionInt) throws IOException {
               scroll.Top = info.dwCursorPosition.Y;
               Kernel32.COORD org = new Kernel32.COORD();
               org.X = 0;
      -        org.Y = (short)(info.dwCursorPosition.Y + optionInt);
      +        org.Y = (short) (info.dwCursorPosition.Y + optionInt);
               Kernel32.CHAR_INFO info = new Kernel32.CHAR_INFO(' ', originalColors);
               Kernel32.INSTANCE.ScrollConsoleScreenBuffer(console, scroll, scroll, org, info);
           }
      @@ -342,7 +352,7 @@ protected void processDeleteLine(int optionInt) throws IOException {
               scroll.Top = info.dwCursorPosition.Y;
               Kernel32.COORD org = new Kernel32.COORD();
               org.X = 0;
      -        org.Y = (short)(info.dwCursorPosition.Y - optionInt);
      +        org.Y = (short) (info.dwCursorPosition.Y - optionInt);
               Kernel32.CHAR_INFO info = new Kernel32.CHAR_INFO(' ', originalColors);
               Kernel32.INSTANCE.ScrollConsoleScreenBuffer(console, scroll, scroll, org, info);
           }
      diff --git a/src/jdk.internal.le/windows/native/lible/Kernel32.cpp b/src/jdk.internal.le/windows/native/lible/Kernel32.cpp
      index 89fcc0acbdaae..e0a71f085596d 100644
      --- a/src/jdk.internal.le/windows/native/lible/Kernel32.cpp
      +++ b/src/jdk.internal.le/windows/native/lible/Kernel32.cpp
      @@ -327,16 +327,6 @@ JNIEXPORT jobject JNICALL Java_jdk_internal_org_jline_terminal_impl_jna_win_Kern
                                 nStdHandle);
       }
       
      -/*
      - * Class:     jdk_internal_org_jline_terminal_impl_jna_win_Kernel32Impl
      - * Method:    GetConsoleOutputCP
      - * Signature: ()I
      - */
      -JNIEXPORT jint JNICALL Java_jdk_internal_org_jline_terminal_impl_jna_win_Kernel32Impl_GetConsoleOutputCP
      -  (JNIEnv *, jobject) {
      -    return GetConsoleOutputCP();
      -}
      -
       /*
        * Class:     jdk_internal_org_jline_terminal_impl_jna_win_Kernel32Impl
        * Method:    FillConsoleOutputCharacter
      diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
      index df75739188f59..6957939cae4ba 100644
      --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
      +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
      @@ -2151,7 +2151,7 @@ void loadKeyStore(String keyStoreName, boolean prompt) {
                           && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
                       storepass = getPass
                               (rb.getString("Enter.Passphrase.for.keystore."));
      -            } else if (!token && storepass == null && prompt) {
      +            } else if (!token && storepass == null && prompt && !protectedPath) {
                       storepass = getPass
                               (rb.getString("Enter.Passphrase.for.keystore."));
                   }
      diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java b/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java
      index 939057ad8c64d..d64287a06392e 100644
      --- a/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java
      +++ b/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java
      @@ -212,6 +212,13 @@ void process(Main jartool, String opt, String arg) throws BadArgs {
                       }
                   },
       
      +            // Extract options
      +            new Option(false, OptionType.EXTRACT, "--keep-old-files", "-k") {
      +                void process(Main jartool, String opt, String arg) {
      +                    jartool.kflag = true;
      +                }
      +            },
      +
                   // Hidden options
                   new Option(false, OptionType.OTHER, "-P") {
                       void process(Main jartool, String opt, String arg) {
      @@ -254,6 +261,7 @@ enum OptionType {
               CREATE("create"),
               CREATE_UPDATE("create.update"),
               CREATE_UPDATE_INDEX("create.update.index"),
      +        EXTRACT("extract"),
               OTHER("other");
       
               /** Resource lookup section prefix. */
      diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java
      index 5ae7177ddb5db..7a60317a5f5b3 100644
      --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java
      +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java
      @@ -155,8 +155,9 @@ public int hashCode() {
            * nflag: Perform jar normalization at the end
            * pflag: preserve/don't strip leading slash and .. component from file name
            * dflag: print module descriptor
      +     * kflag: keep existing file
            */
      -    boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, pflag, dflag, validate;
      +    boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, pflag, dflag, kflag, validate;
       
           boolean suppressDeprecateMsg = false;
       
      @@ -581,6 +582,9 @@ boolean parseArgs(String args[]) {
                               case '0':
                                   flag0 = true;
                                   break;
      +                        case 'k':
      +                            kflag = true;
      +                            break;
                               case 'i':
                                   if (cflag || uflag || xflag || tflag) {
                                       usageError(getMsg("error.multiple.main.operations"));
      @@ -611,6 +615,9 @@ boolean parseArgs(String args[]) {
                   usageError(getMsg("error.bad.option"));
                   return false;
               }
      +        if (kflag && !xflag) {
      +            warn(formatMsg("warn.option.is.ignored", "--keep-old-files/-k/k"));
      +        }
       
               /* parse file arguments */
               int n = args.length - count;
      @@ -1451,6 +1458,12 @@ ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException {
                       output(formatMsg("out.create", name));
                   }
               } else {
      +            if (f.exists() && kflag) {
      +                if (vflag) {
      +                    output(formatMsg("out.kept", name));
      +                }
      +                return rc;
      +            }
                   if (f.getParent() != null) {
                       File d = new File(f.getParent());
                       if (!d.exists() && !d.mkdirs() || !d.isDirectory()) {
      diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties
      index 87bff6891029f..df2065918a6d4 100644
      --- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties
      +++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties
      @@ -137,6 +137,8 @@ warn.release.unexpected.versioned.entry=\
               unexpected versioned entry {0}
       warn.flag.is.deprecated=\
               Warning: The {0} option is deprecated, and is planned for removal in a future JDK release\n
      +warn.option.is.ignored=\
      +        Warning: The {0} option is not valid with current usage, will be ignored.
       out.added.manifest=\
               added manifest
       out.added.module-info=\
      @@ -159,6 +161,8 @@ out.create=\
               \ \ created: {0}
       out.extracted=\
               extracted: {0}
      +out.kept=\
      +        \ \ skipped: {0} exists
       out.inflated=\
               \ inflated: {0}
       out.size=\
      @@ -236,7 +240,10 @@ main.help.opt.main.list=\
       main.help.opt.main.update=\
       \  -u, --update               Update an existing jar archive
       main.help.opt.main.extract=\
      -\  -x, --extract              Extract named (or all) files from the archive
      +\  -x, --extract              Extract named (or all) files from the archive.\n\
      +\                             If a file with the same name appears more than once in\n\
      +\                             the archive, each copy will be extracted, with later copies\n\
      +\                             overwriting (replacing) earlier copies unless -k is specified.
       main.help.opt.main.describe-module=\
       \  -d, --describe-module      Print the module descriptor, or automatic module name
       main.help.opt.main.validate=\
      @@ -298,6 +305,15 @@ main.help.opt.create.update.index.date=\
       \      --date=TIMESTAMP       The timestamp in ISO-8601 extended offset date-time with\n\
       \                             optional time-zone format, to use for the timestamps of\n\
       \                             entries, e.g. "2022-02-12T12:30:00-05:00"
      +main.help.opt.extract=\
      +\ Operation modifiers valid only in extract mode:\n
      +main.help.opt.extract.keep-old-files=\
      +\  -k, --keep-old-files       Do not overwrite existing files.\n\
      +\                             If a Jar file entry with the same name exists in the target\n\
      +\                             directory, the existing file will not be overwritten.\n\
      +\                             As a result, if a file appears more than once in an\n\
      +\                             archive, later copies will not overwrite earlier copies.\n\
      +\                             Also note that some file system can be case insensitive.
       main.help.opt.other=\
       \ Other options:\n
       main.help.opt.other.help=\
      diff --git a/src/jdk.jartool/share/man/jar.1 b/src/jdk.jartool/share/man/jar.1
      index c0edcd46910cb..0e0c58bf426a2 100644
      --- a/src/jdk.jartool/share/man/jar.1
      +++ b/src/jdk.jartool/share/man/jar.1
      @@ -196,7 +196,8 @@ Specifies the location of module dependence for generating the hash.
       .RE
       .TP
       .B \f[CB]\@\f[R]\f[I]file\f[R]
      -Reads \f[CB]jar\f[R] options and file names from a text file.
      +Reads \f[CB]jar\f[R] options and file names from a text file as if they
      +were supplied on the command line
       .RS
       .RE
       .SH OPERATION MODIFIERS VALID ONLY IN CREATE, UPDATE, AND
      @@ -339,8 +340,15 @@ class files from the file \f[CB]classes.list\f[R].
       To shorten or simplify the \f[CB]jar\f[R] command, you can specify
       arguments in a separate text file and pass it to the \f[CB]jar\f[R]
       command with the at sign (\f[CB]\@\f[R]) as a prefix.
      +
      +To shorten or simplify the \f[CB]jar\f[R] command, you can provide an arg
      +file that lists the files to include in the JAR file and pass it to the
      +\f[CB]jar\f[R] command with the at sign (\f[CB]\@\f[R]) as a prefix.
       .RS
       .PP
       \f[CB]jar\ \-\-create\ \-\-file\ my.jar\ \@classes.list\f[R]
       .RE
      +.PP
      +If one or more entries in the arg file cannot be found then the jar
      +command fails without creating the JAR file.
       .RE
      diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java
      index dae63c2f0bfee..92f2b9081db05 100644
      --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java
      +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java
      @@ -310,7 +310,7 @@ protected void generateOtherFiles(ClassTree classtree)
       
           private void copyJqueryFiles() throws DocletException {
               List files = Arrays.asList(
      -                "jquery-3.6.1.min.js",
      +                "jquery-3.7.1.min.js",
                       "jquery-ui.min.js",
                       "jquery-ui.min.css");
               DocFile f;
      diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java
      index 86c57d2bf3a4e..494b78d8ade76 100644
      --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java
      +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java
      @@ -118,6 +118,7 @@
       import static com.sun.source.doctree.DocTree.Kind.LINK;
       import static com.sun.source.doctree.DocTree.Kind.LINK_PLAIN;
       import static com.sun.source.doctree.DocTree.Kind.SEE;
      +import static com.sun.source.doctree.DocTree.Kind.START_ELEMENT;
       import static com.sun.source.doctree.DocTree.Kind.TEXT;
       import static jdk.javadoc.internal.doclets.toolkit.util.CommentHelper.SPACER;
       
      @@ -1015,6 +1016,13 @@ public Content seeTagToContent(Element element, DocTree see, TagletWriterImpl.Co
                               // @see reference label...
                               label = ref.subList(1, ref.size());
                           }
      +                    case ERRONEOUS -> {
      +                        messages.warning(ch.getDocTreePath(see),
      +                            "doclet.tag.invalid_input",
      +                            "@" + tagName,
      +                            seeText);
      +                        return Text.of("invalid input: '" + seeText + "'");
      +                    }
                           default ->
                               throw new IllegalStateException(ref.get(0).getKind().toString());
                       }
      @@ -1266,21 +1274,37 @@ private void addCommentTags(Element element, List tags, boole
               }
           }
       
      -    boolean ignoreNonInlineTag(DocTree dtree) {
      +    // helper methods because jdk21 functionality is not allowed
      +    private static Name getLastHelper(List l) {
      +        return l.get(l.size() - 1);
      +    }
      +
      +    private static Name removeLastHelper(List l) {
      +        return l.remove(l.size() - 1);
      +    }
      +
      +    boolean ignoreNonInlineTag(DocTree dtree, List openTags) {
               Name name = null;
      -        if (dtree.getKind() == Kind.START_ELEMENT) {
      -            StartElementTree setree = (StartElementTree)dtree;
      -            name = setree.getName();
      -        } else if (dtree.getKind() == Kind.END_ELEMENT) {
      -            EndElementTree eetree = (EndElementTree)dtree;
      -            name = eetree.getName();
      +        Kind kind = dtree.getKind();
      +        if (kind == Kind.START_ELEMENT) {
      +            name = ((StartElementTree)dtree).getName();
      +        } else if (kind == Kind.END_ELEMENT) {
      +            name = ((EndElementTree)dtree).getName();
               }
       
               if (name != null) {
                   HtmlTag htmlTag = HtmlTag.get(name);
      -            if (htmlTag != null &&
      -                    htmlTag.blockType != jdk.javadoc.internal.doclint.HtmlTag.BlockType.INLINE) {
      -                return true;
      +            if (htmlTag != null) {
      +                if (htmlTag.blockType != HtmlTag.BlockType.INLINE) {
      +                    return true;
      +                }
      +                // Keep track of open inline tags that need to be closed, see 8326332
      +                if (kind == START_ELEMENT && htmlTag.endKind == HtmlTag.EndKind.REQUIRED) {
      +                    openTags.add(name);
      +                } else if (kind == Kind.END_ELEMENT && !openTags.isEmpty()
      +                        && getLastHelper(openTags).equals(name)) {
      +                    removeLastHelper(openTags);
      +                }
                   }
               }
               return false;
      @@ -1370,6 +1394,7 @@ public ContentBuilder add(CharSequence text) {
               // Array of all possible inline tags for this javadoc run
               configuration.tagletManager.checkTags(element, trees, true);
               commentRemoved = false;
      +        List openTags = new ArrayList<>();
       
               for (ListIterator iterator = trees.listIterator(); iterator.hasNext();) {
                   boolean isFirstNode = !iterator.hasPrevious();
      @@ -1378,14 +1403,16 @@ public ContentBuilder add(CharSequence text) {
       
                   if (context.isFirstSentence) {
                       // Ignore block tags
      -                if (ignoreNonInlineTag(tag))
      +                if (ignoreNonInlineTag(tag, openTags)) {
                           continue;
      +                }
       
                       // Ignore any trailing whitespace OR whitespace after removed html comment
                       if ((isLastNode || commentRemoved)
                               && tag.getKind() == TEXT
      -                        && isAllWhiteSpace(ch.getText(tag)))
      +                        && isAllWhiteSpace(ch.getText(tag))) {
                           continue;
      +                }
       
                       // Ignore any leading html comments
                       if ((isFirstNode || commentRemoved) && tag.getKind() == COMMENT) {
      @@ -1631,6 +1658,10 @@ protected Boolean defaultAction(DocTree node, Content c) {
                   if (allDone)
                       break;
               }
      +        // Close any open inline tags
      +        while (!openTags.isEmpty()) {
      +            result.add(RawHtml.endElement(removeLastHelper(openTags)));
      +        }
               return result;
           }
       
      diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/RawHtml.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/RawHtml.java
      index 3268bb553336a..d7a0785190d27 100644
      --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/RawHtml.java
      +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/RawHtml.java
      @@ -126,4 +126,14 @@ public boolean write(Writer out, boolean atNewline) throws IOException {
               out.write(rawHtmlContent);
               return rawHtmlContent.endsWith(DocletConstants.NL);
           }
      +
      +    /**
      +     * Creates HTML for the end of an element.
      +     *
      +     * @param name the name of the element
      +     * @return the HTML
      +     */
      +    public static RawHtml endElement(CharSequence name) {
      +        return new RawHtml("");
      +    }
       }
      diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script-dir/jquery-3.6.1.js b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script-dir/jquery-3.6.1.js
      deleted file mode 100644
      index 12e65d069fe3f..0000000000000
      --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script-dir/jquery-3.6.1.js
      +++ /dev/null
      @@ -1,10909 +0,0 @@
      -/*!
      - * jQuery JavaScript Library v3.6.1
      - * https://jquery.com/
      - *
      - * Includes Sizzle.js
      - * https://sizzlejs.com/
      - *
      - * Copyright OpenJS Foundation and other contributors
      - * Released under the MIT license
      - * https://jquery.org/license
      - *
      - * Date: 2022-08-26T17:52Z
      - */
      -( function( global, factory ) {
      -
      -	"use strict";
      -
      -	if ( typeof module === "object" && typeof module.exports === "object" ) {
      -
      -		// For CommonJS and CommonJS-like environments where a proper `window`
      -		// is present, execute the factory and get jQuery.
      -		// For environments that do not have a `window` with a `document`
      -		// (such as Node.js), expose a factory as module.exports.
      -		// This accentuates the need for the creation of a real `window`.
      -		// e.g. var jQuery = require("jquery")(window);
      -		// See ticket trac-14549 for more info.
      -		module.exports = global.document ?
      -			factory( global, true ) :
      -			function( w ) {
      -				if ( !w.document ) {
      -					throw new Error( "jQuery requires a window with a document" );
      -				}
      -				return factory( w );
      -			};
      -	} else {
      -		factory( global );
      -	}
      -
      -// Pass this if window is not defined yet
      -} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
      -
      -// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
      -// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
      -// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
      -// enough that all such attempts are guarded in a try block.
      -"use strict";
      -
      -var arr = [];
      -
      -var getProto = Object.getPrototypeOf;
      -
      -var slice = arr.slice;
      -
      -var flat = arr.flat ? function( array ) {
      -	return arr.flat.call( array );
      -} : function( array ) {
      -	return arr.concat.apply( [], array );
      -};
      -
      -
      -var push = arr.push;
      -
      -var indexOf = arr.indexOf;
      -
      -var class2type = {};
      -
      -var toString = class2type.toString;
      -
      -var hasOwn = class2type.hasOwnProperty;
      -
      -var fnToString = hasOwn.toString;
      -
      -var ObjectFunctionString = fnToString.call( Object );
      -
      -var support = {};
      -
      -var isFunction = function isFunction( obj ) {
      -
      -		// Support: Chrome <=57, Firefox <=52
      -		// In some browsers, typeof returns "function" for HTML  elements
      -		// (i.e., `typeof document.createElement( "object" ) === "function"`).
      -		// We don't want to classify *any* DOM node as a function.
      -		// Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5
      -		// Plus for old WebKit, typeof returns "function" for HTML collections
      -		// (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756)
      -		return typeof obj === "function" && typeof obj.nodeType !== "number" &&
      -			typeof obj.item !== "function";
      -	};
      -
      -
      -var isWindow = function isWindow( obj ) {
      -		return obj != null && obj === obj.window;
      -	};
      -
      -
      -var document = window.document;
      -
      -
      -
      -	var preservedScriptAttributes = {
      -		type: true,
      -		src: true,
      -		nonce: true,
      -		noModule: true
      -	};
      -
      -	function DOMEval( code, node, doc ) {
      -		doc = doc || document;
      -
      -		var i, val,
      -			script = doc.createElement( "script" );
      -
      -		script.text = code;
      -		if ( node ) {
      -			for ( i in preservedScriptAttributes ) {
      -
      -				// Support: Firefox 64+, Edge 18+
      -				// Some browsers don't support the "nonce" property on scripts.
      -				// On the other hand, just using `getAttribute` is not enough as
      -				// the `nonce` attribute is reset to an empty string whenever it
      -				// becomes browsing-context connected.
      -				// See https://github.com/whatwg/html/issues/2369
      -				// See https://html.spec.whatwg.org/#nonce-attributes
      -				// The `node.getAttribute` check was added for the sake of
      -				// `jQuery.globalEval` so that it can fake a nonce-containing node
      -				// via an object.
      -				val = node[ i ] || node.getAttribute && node.getAttribute( i );
      -				if ( val ) {
      -					script.setAttribute( i, val );
      -				}
      -			}
      -		}
      -		doc.head.appendChild( script ).parentNode.removeChild( script );
      -	}
      -
      -
      -function toType( obj ) {
      -	if ( obj == null ) {
      -		return obj + "";
      -	}
      -
      -	// Support: Android <=2.3 only (functionish RegExp)
      -	return typeof obj === "object" || typeof obj === "function" ?
      -		class2type[ toString.call( obj ) ] || "object" :
      -		typeof obj;
      -}
      -/* global Symbol */
      -// Defining this global in .eslintrc.json would create a danger of using the global
      -// unguarded in another place, it seems safer to define global only for this module
      -
      -
      -
      -var
      -	version = "3.6.1",
      -
      -	// Define a local copy of jQuery
      -	jQuery = function( selector, context ) {
      -
      -		// The jQuery object is actually just the init constructor 'enhanced'
      -		// Need init if jQuery is called (just allow error to be thrown if not included)
      -		return new jQuery.fn.init( selector, context );
      -	};
      -
      -jQuery.fn = jQuery.prototype = {
      -
      -	// The current version of jQuery being used
      -	jquery: version,
      -
      -	constructor: jQuery,
      -
      -	// The default length of a jQuery object is 0
      -	length: 0,
      -
      -	toArray: function() {
      -		return slice.call( this );
      -	},
      -
      -	// Get the Nth element in the matched element set OR
      -	// Get the whole matched element set as a clean array
      -	get: function( num ) {
      -
      -		// Return all the elements in a clean array
      -		if ( num == null ) {
      -			return slice.call( this );
      -		}
      -
      -		// Return just the one element from the set
      -		return num < 0 ? this[ num + this.length ] : this[ num ];
      -	},
      -
      -	// Take an array of elements and push it onto the stack
      -	// (returning the new matched element set)
      -	pushStack: function( elems ) {
      -
      -		// Build a new jQuery matched element set
      -		var ret = jQuery.merge( this.constructor(), elems );
      -
      -		// Add the old object onto the stack (as a reference)
      -		ret.prevObject = this;
      -
      -		// Return the newly-formed element set
      -		return ret;
      -	},
      -
      -	// Execute a callback for every element in the matched set.
      -	each: function( callback ) {
      -		return jQuery.each( this, callback );
      -	},
      -
      -	map: function( callback ) {
      -		return this.pushStack( jQuery.map( this, function( elem, i ) {
      -			return callback.call( elem, i, elem );
      -		} ) );
      -	},
      -
      -	slice: function() {
      -		return this.pushStack( slice.apply( this, arguments ) );
      -	},
      -
      -	first: function() {
      -		return this.eq( 0 );
      -	},
      -
      -	last: function() {
      -		return this.eq( -1 );
      -	},
      -
      -	even: function() {
      -		return this.pushStack( jQuery.grep( this, function( _elem, i ) {
      -			return ( i + 1 ) % 2;
      -		} ) );
      -	},
      -
      -	odd: function() {
      -		return this.pushStack( jQuery.grep( this, function( _elem, i ) {
      -			return i % 2;
      -		} ) );
      -	},
      -
      -	eq: function( i ) {
      -		var len = this.length,
      -			j = +i + ( i < 0 ? len : 0 );
      -		return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
      -	},
      -
      -	end: function() {
      -		return this.prevObject || this.constructor();
      -	},
      -
      -	// For internal use only.
      -	// Behaves like an Array's method, not like a jQuery method.
      -	push: push,
      -	sort: arr.sort,
      -	splice: arr.splice
      -};
      -
      -jQuery.extend = jQuery.fn.extend = function() {
      -	var options, name, src, copy, copyIsArray, clone,
      -		target = arguments[ 0 ] || {},
      -		i = 1,
      -		length = arguments.length,
      -		deep = false;
      -
      -	// Handle a deep copy situation
      -	if ( typeof target === "boolean" ) {
      -		deep = target;
      -
      -		// Skip the boolean and the target
      -		target = arguments[ i ] || {};
      -		i++;
      -	}
      -
      -	// Handle case when target is a string or something (possible in deep copy)
      -	if ( typeof target !== "object" && !isFunction( target ) ) {
      -		target = {};
      -	}
      -
      -	// Extend jQuery itself if only one argument is passed
      -	if ( i === length ) {
      -		target = this;
      -		i--;
      -	}
      -
      -	for ( ; i < length; i++ ) {
      -
      -		// Only deal with non-null/undefined values
      -		if ( ( options = arguments[ i ] ) != null ) {
      -
      -			// Extend the base object
      -			for ( name in options ) {
      -				copy = options[ name ];
      -
      -				// Prevent Object.prototype pollution
      -				// Prevent never-ending loop
      -				if ( name === "__proto__" || target === copy ) {
      -					continue;
      -				}
      -
      -				// Recurse if we're merging plain objects or arrays
      -				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
      -					( copyIsArray = Array.isArray( copy ) ) ) ) {
      -					src = target[ name ];
      -
      -					// Ensure proper type for the source value
      -					if ( copyIsArray && !Array.isArray( src ) ) {
      -						clone = [];
      -					} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
      -						clone = {};
      -					} else {
      -						clone = src;
      -					}
      -					copyIsArray = false;
      -
      -					// Never move original objects, clone them
      -					target[ name ] = jQuery.extend( deep, clone, copy );
      -
      -				// Don't bring in undefined values
      -				} else if ( copy !== undefined ) {
      -					target[ name ] = copy;
      -				}
      -			}
      -		}
      -	}
      -
      -	// Return the modified object
      -	return target;
      -};
      -
      -jQuery.extend( {
      -
      -	// Unique for each copy of jQuery on the page
      -	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
      -
      -	// Assume jQuery is ready without the ready module
      -	isReady: true,
      -
      -	error: function( msg ) {
      -		throw new Error( msg );
      -	},
      -
      -	noop: function() {},
      -
      -	isPlainObject: function( obj ) {
      -		var proto, Ctor;
      -
      -		// Detect obvious negatives
      -		// Use toString instead of jQuery.type to catch host objects
      -		if ( !obj || toString.call( obj ) !== "[object Object]" ) {
      -			return false;
      -		}
      -
      -		proto = getProto( obj );
      -
      -		// Objects with no prototype (e.g., `Object.create( null )`) are plain
      -		if ( !proto ) {
      -			return true;
      -		}
      -
      -		// Objects with prototype are plain iff they were constructed by a global Object function
      -		Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
      -		return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
      -	},
      -
      -	isEmptyObject: function( obj ) {
      -		var name;
      -
      -		for ( name in obj ) {
      -			return false;
      -		}
      -		return true;
      -	},
      -
      -	// Evaluates a script in a provided context; falls back to the global one
      -	// if not specified.
      -	globalEval: function( code, options, doc ) {
      -		DOMEval( code, { nonce: options && options.nonce }, doc );
      -	},
      -
      -	each: function( obj, callback ) {
      -		var length, i = 0;
      -
      -		if ( isArrayLike( obj ) ) {
      -			length = obj.length;
      -			for ( ; i < length; i++ ) {
      -				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
      -					break;
      -				}
      -			}
      -		} else {
      -			for ( i in obj ) {
      -				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
      -					break;
      -				}
      -			}
      -		}
      -
      -		return obj;
      -	},
      -
      -	// results is for internal usage only
      -	makeArray: function( arr, results ) {
      -		var ret = results || [];
      -
      -		if ( arr != null ) {
      -			if ( isArrayLike( Object( arr ) ) ) {
      -				jQuery.merge( ret,
      -					typeof arr === "string" ?
      -						[ arr ] : arr
      -				);
      -			} else {
      -				push.call( ret, arr );
      -			}
      -		}
      -
      -		return ret;
      -	},
      -
      -	inArray: function( elem, arr, i ) {
      -		return arr == null ? -1 : indexOf.call( arr, elem, i );
      -	},
      -
      -	// Support: Android <=4.0 only, PhantomJS 1 only
      -	// push.apply(_, arraylike) throws on ancient WebKit
      -	merge: function( first, second ) {
      -		var len = +second.length,
      -			j = 0,
      -			i = first.length;
      -
      -		for ( ; j < len; j++ ) {
      -			first[ i++ ] = second[ j ];
      -		}
      -
      -		first.length = i;
      -
      -		return first;
      -	},
      -
      -	grep: function( elems, callback, invert ) {
      -		var callbackInverse,
      -			matches = [],
      -			i = 0,
      -			length = elems.length,
      -			callbackExpect = !invert;
      -
      -		// Go through the array, only saving the items
      -		// that pass the validator function
      -		for ( ; i < length; i++ ) {
      -			callbackInverse = !callback( elems[ i ], i );
      -			if ( callbackInverse !== callbackExpect ) {
      -				matches.push( elems[ i ] );
      -			}
      -		}
      -
      -		return matches;
      -	},
      -
      -	// arg is for internal usage only
      -	map: function( elems, callback, arg ) {
      -		var length, value,
      -			i = 0,
      -			ret = [];
      -
      -		// Go through the array, translating each of the items to their new values
      -		if ( isArrayLike( elems ) ) {
      -			length = elems.length;
      -			for ( ; i < length; i++ ) {
      -				value = callback( elems[ i ], i, arg );
      -
      -				if ( value != null ) {
      -					ret.push( value );
      -				}
      -			}
      -
      -		// Go through every key on the object,
      -		} else {
      -			for ( i in elems ) {
      -				value = callback( elems[ i ], i, arg );
      -
      -				if ( value != null ) {
      -					ret.push( value );
      -				}
      -			}
      -		}
      -
      -		// Flatten any nested arrays
      -		return flat( ret );
      -	},
      -
      -	// A global GUID counter for objects
      -	guid: 1,
      -
      -	// jQuery.support is not used in Core but other projects attach their
      -	// properties to it so it needs to exist.
      -	support: support
      -} );
      -
      -if ( typeof Symbol === "function" ) {
      -	jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
      -}
      -
      -// Populate the class2type map
      -jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
      -	function( _i, name ) {
      -		class2type[ "[object " + name + "]" ] = name.toLowerCase();
      -	} );
      -
      -function isArrayLike( obj ) {
      -
      -	// Support: real iOS 8.2 only (not reproducible in simulator)
      -	// `in` check used to prevent JIT error (gh-2145)
      -	// hasOwn isn't used here due to false negatives
      -	// regarding Nodelist length in IE
      -	var length = !!obj && "length" in obj && obj.length,
      -		type = toType( obj );
      -
      -	if ( isFunction( obj ) || isWindow( obj ) ) {
      -		return false;
      -	}
      -
      -	return type === "array" || length === 0 ||
      -		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
      -}
      -var Sizzle =
      -/*!
      - * Sizzle CSS Selector Engine v2.3.6
      - * https://sizzlejs.com/
      - *
      - * Copyright JS Foundation and other contributors
      - * Released under the MIT license
      - * https://js.foundation/
      - *
      - * Date: 2021-02-16
      - */
      -( function( window ) {
      -var i,
      -	support,
      -	Expr,
      -	getText,
      -	isXML,
      -	tokenize,
      -	compile,
      -	select,
      -	outermostContext,
      -	sortInput,
      -	hasDuplicate,
      -
      -	// Local document vars
      -	setDocument,
      -	document,
      -	docElem,
      -	documentIsHTML,
      -	rbuggyQSA,
      -	rbuggyMatches,
      -	matches,
      -	contains,
      -
      -	// Instance-specific data
      -	expando = "sizzle" + 1 * new Date(),
      -	preferredDoc = window.document,
      -	dirruns = 0,
      -	done = 0,
      -	classCache = createCache(),
      -	tokenCache = createCache(),
      -	compilerCache = createCache(),
      -	nonnativeSelectorCache = createCache(),
      -	sortOrder = function( a, b ) {
      -		if ( a === b ) {
      -			hasDuplicate = true;
      -		}
      -		return 0;
      -	},
      -
      -	// Instance methods
      -	hasOwn = ( {} ).hasOwnProperty,
      -	arr = [],
      -	pop = arr.pop,
      -	pushNative = arr.push,
      -	push = arr.push,
      -	slice = arr.slice,
      -
      -	// Use a stripped-down indexOf as it's faster than native
      -	// https://jsperf.com/thor-indexof-vs-for/5
      -	indexOf = function( list, elem ) {
      -		var i = 0,
      -			len = list.length;
      -		for ( ; i < len; i++ ) {
      -			if ( list[ i ] === elem ) {
      -				return i;
      -			}
      -		}
      -		return -1;
      -	},
      -
      -	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" +
      -		"ismap|loop|multiple|open|readonly|required|scoped",
      -
      -	// Regular expressions
      -
      -	// http://www.w3.org/TR/css3-selectors/#whitespace
      -	whitespace = "[\\x20\\t\\r\\n\\f]",
      -
      -	// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
      -	identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
      -		"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",
      -
      -	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
      -	attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
      -
      -		// Operator (capture 2)
      -		"*([*^$|!~]?=)" + whitespace +
      -
      -		// "Attribute values must be CSS identifiers [capture 5]
      -		// or strings [capture 3 or capture 4]"
      -		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
      -		whitespace + "*\\]",
      -
      -	pseudos = ":(" + identifier + ")(?:\\((" +
      -
      -		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
      -		// 1. quoted (capture 3; capture 4 or capture 5)
      -		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
      -
      -		// 2. simple (capture 6)
      -		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
      -
      -		// 3. anything else (capture 2)
      -		".*" +
      -		")\\)|)",
      -
      -	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
      -	rwhitespace = new RegExp( whitespace + "+", "g" ),
      -	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" +
      -		whitespace + "+$", "g" ),
      -
      -	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
      -	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace +
      -		"*" ),
      -	rdescend = new RegExp( whitespace + "|>" ),
      -
      -	rpseudo = new RegExp( pseudos ),
      -	ridentifier = new RegExp( "^" + identifier + "$" ),
      -
      -	matchExpr = {
      -		"ID": new RegExp( "^#(" + identifier + ")" ),
      -		"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
      -		"TAG": new RegExp( "^(" + identifier + "|[*])" ),
      -		"ATTR": new RegExp( "^" + attributes ),
      -		"PSEUDO": new RegExp( "^" + pseudos ),
      -		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
      -			whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
      -			whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
      -		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
      -
      -		// For use in libraries implementing .is()
      -		// We use this for POS matching in `select`
      -		"needsContext": new RegExp( "^" + whitespace +
      -			"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
      -			"*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
      -	},
      -
      -	rhtml = /HTML$/i,
      -	rinputs = /^(?:input|select|textarea|button)$/i,
      -	rheader = /^h\d$/i,
      -
      -	rnative = /^[^{]+\{\s*\[native \w/,
      -
      -	// Easily-parseable/retrievable ID or TAG or CLASS selectors
      -	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
      -
      -	rsibling = /[+~]/,
      -
      -	// CSS escapes
      -	// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
      -	runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ),
      -	funescape = function( escape, nonHex ) {
      -		var high = "0x" + escape.slice( 1 ) - 0x10000;
      -
      -		return nonHex ?
      -
      -			// Strip the backslash prefix from a non-hex escape sequence
      -			nonHex :
      -
      -			// Replace a hexadecimal escape sequence with the encoded Unicode code point
      -			// Support: IE <=11+
      -			// For values outside the Basic Multilingual Plane (BMP), manually construct a
      -			// surrogate pair
      -			high < 0 ?
      -				String.fromCharCode( high + 0x10000 ) :
      -				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
      -	},
      -
      -	// CSS string/identifier serialization
      -	// https://drafts.csswg.org/cssom/#common-serializing-idioms
      -	rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
      -	fcssescape = function( ch, asCodePoint ) {
      -		if ( asCodePoint ) {
      -
      -			// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
      -			if ( ch === "\0" ) {
      -				return "\uFFFD";
      -			}
      -
      -			// Control characters and (dependent upon position) numbers get escaped as code points
      -			return ch.slice( 0, -1 ) + "\\" +
      -				ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
      -		}
      -
      -		// Other potentially-special ASCII characters get backslash-escaped
      -		return "\\" + ch;
      -	},
      -
      -	// Used for iframes
      -	// See setDocument()
      -	// Removing the function wrapper causes a "Permission Denied"
      -	// error in IE
      -	unloadHandler = function() {
      -		setDocument();
      -	},
      -
      -	inDisabledFieldset = addCombinator(
      -		function( elem ) {
      -			return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset";
      -		},
      -		{ dir: "parentNode", next: "legend" }
      -	);
      -
      -// Optimize for push.apply( _, NodeList )
      -try {
      -	push.apply(
      -		( arr = slice.call( preferredDoc.childNodes ) ),
      -		preferredDoc.childNodes
      -	);
      -
      -	// Support: Android<4.0
      -	// Detect silently failing push.apply
      -	// eslint-disable-next-line no-unused-expressions
      -	arr[ preferredDoc.childNodes.length ].nodeType;
      -} catch ( e ) {
      -	push = { apply: arr.length ?
      -
      -		// Leverage slice if possible
      -		function( target, els ) {
      -			pushNative.apply( target, slice.call( els ) );
      -		} :
      -
      -		// Support: IE<9
      -		// Otherwise append directly
      -		function( target, els ) {
      -			var j = target.length,
      -				i = 0;
      -
      -			// Can't trust NodeList.length
      -			while ( ( target[ j++ ] = els[ i++ ] ) ) {}
      -			target.length = j - 1;
      -		}
      -	};
      -}
      -
      -function Sizzle( selector, context, results, seed ) {
      -	var m, i, elem, nid, match, groups, newSelector,
      -		newContext = context && context.ownerDocument,
      -
      -		// nodeType defaults to 9, since context defaults to document
      -		nodeType = context ? context.nodeType : 9;
      -
      -	results = results || [];
      -
      -	// Return early from calls with invalid selector or context
      -	if ( typeof selector !== "string" || !selector ||
      -		nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
      -
      -		return results;
      -	}
      -
      -	// Try to shortcut find operations (as opposed to filters) in HTML documents
      -	if ( !seed ) {
      -		setDocument( context );
      -		context = context || document;
      -
      -		if ( documentIsHTML ) {
      -
      -			// If the selector is sufficiently simple, try using a "get*By*" DOM method
      -			// (excepting DocumentFragment context, where the methods don't exist)
      -			if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {
      -
      -				// ID selector
      -				if ( ( m = match[ 1 ] ) ) {
      -
      -					// Document context
      -					if ( nodeType === 9 ) {
      -						if ( ( elem = context.getElementById( m ) ) ) {
      -
      -							// Support: IE, Opera, Webkit
      -							// TODO: identify versions
      -							// getElementById can match elements by name instead of ID
      -							if ( elem.id === m ) {
      -								results.push( elem );
      -								return results;
      -							}
      -						} else {
      -							return results;
      -						}
      -
      -					// Element context
      -					} else {
      -
      -						// Support: IE, Opera, Webkit
      -						// TODO: identify versions
      -						// getElementById can match elements by name instead of ID
      -						if ( newContext && ( elem = newContext.getElementById( m ) ) &&
      -							contains( context, elem ) &&
      -							elem.id === m ) {
      -
      -							results.push( elem );
      -							return results;
      -						}
      -					}
      -
      -				// Type selector
      -				} else if ( match[ 2 ] ) {
      -					push.apply( results, context.getElementsByTagName( selector ) );
      -					return results;
      -
      -				// Class selector
      -				} else if ( ( m = match[ 3 ] ) && support.getElementsByClassName &&
      -					context.getElementsByClassName ) {
      -
      -					push.apply( results, context.getElementsByClassName( m ) );
      -					return results;
      -				}
      -			}
      -
      -			// Take advantage of querySelectorAll
      -			if ( support.qsa &&
      -				!nonnativeSelectorCache[ selector + " " ] &&
      -				( !rbuggyQSA || !rbuggyQSA.test( selector ) ) &&
      -
      -				// Support: IE 8 only
      -				// Exclude object elements
      -				( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) {
      -
      -				newSelector = selector;
      -				newContext = context;
      -
      -				// qSA considers elements outside a scoping root when evaluating child or
      -				// descendant combinators, which is not what we want.
      -				// In such cases, we work around the behavior by prefixing every selector in the
      -				// list with an ID selector referencing the scope context.
      -				// The technique has to be used as well when a leading combinator is used
      -				// as such selectors are not recognized by querySelectorAll.
      -				// Thanks to Andrew Dupont for this technique.
      -				if ( nodeType === 1 &&
      -					( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {
      -
      -					// Expand context for sibling selectors
      -					newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
      -						context;
      -
      -					// We can use :scope instead of the ID hack if the browser
      -					// supports it & if we're not changing the context.
      -					if ( newContext !== context || !support.scope ) {
      -
      -						// Capture the context ID, setting it first if necessary
      -						if ( ( nid = context.getAttribute( "id" ) ) ) {
      -							nid = nid.replace( rcssescape, fcssescape );
      -						} else {
      -							context.setAttribute( "id", ( nid = expando ) );
      -						}
      -					}
      -
      -					// Prefix every selector in the list
      -					groups = tokenize( selector );
      -					i = groups.length;
      -					while ( i-- ) {
      -						groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
      -							toSelector( groups[ i ] );
      -					}
      -					newSelector = groups.join( "," );
      -				}
      -
      -				try {
      -					push.apply( results,
      -						newContext.querySelectorAll( newSelector )
      -					);
      -					return results;
      -				} catch ( qsaError ) {
      -					nonnativeSelectorCache( selector, true );
      -				} finally {
      -					if ( nid === expando ) {
      -						context.removeAttribute( "id" );
      -					}
      -				}
      -			}
      -		}
      -	}
      -
      -	// All others
      -	return select( selector.replace( rtrim, "$1" ), context, results, seed );
      -}
      -
      -/**
      - * Create key-value caches of limited size
      - * @returns {function(string, object)} Returns the Object data after storing it on itself with
      - *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
      - *	deleting the oldest entry
      - */
      -function createCache() {
      -	var keys = [];
      -
      -	function cache( key, value ) {
      -
      -		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
      -		if ( keys.push( key + " " ) > Expr.cacheLength ) {
      -
      -			// Only keep the most recent entries
      -			delete cache[ keys.shift() ];
      -		}
      -		return ( cache[ key + " " ] = value );
      -	}
      -	return cache;
      -}
      -
      -/**
      - * Mark a function for special use by Sizzle
      - * @param {Function} fn The function to mark
      - */
      -function markFunction( fn ) {
      -	fn[ expando ] = true;
      -	return fn;
      -}
      -
      -/**
      - * Support testing using an element
      - * @param {Function} fn Passed the created element and returns a boolean result
      - */
      -function assert( fn ) {
      -	var el = document.createElement( "fieldset" );
      -
      -	try {
      -		return !!fn( el );
      -	} catch ( e ) {
      -		return false;
      -	} finally {
      -
      -		// Remove from its parent by default
      -		if ( el.parentNode ) {
      -			el.parentNode.removeChild( el );
      -		}
      -
      -		// release memory in IE
      -		el = null;
      -	}
      -}
      -
      -/**
      - * Adds the same handler for all of the specified attrs
      - * @param {String} attrs Pipe-separated list of attributes
      - * @param {Function} handler The method that will be applied
      - */
      -function addHandle( attrs, handler ) {
      -	var arr = attrs.split( "|" ),
      -		i = arr.length;
      -
      -	while ( i-- ) {
      -		Expr.attrHandle[ arr[ i ] ] = handler;
      -	}
      -}
      -
      -/**
      - * Checks document order of two siblings
      - * @param {Element} a
      - * @param {Element} b
      - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
      - */
      -function siblingCheck( a, b ) {
      -	var cur = b && a,
      -		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
      -			a.sourceIndex - b.sourceIndex;
      -
      -	// Use IE sourceIndex if available on both nodes
      -	if ( diff ) {
      -		return diff;
      -	}
      -
      -	// Check if b follows a
      -	if ( cur ) {
      -		while ( ( cur = cur.nextSibling ) ) {
      -			if ( cur === b ) {
      -				return -1;
      -			}
      -		}
      -	}
      -
      -	return a ? 1 : -1;
      -}
      -
      -/**
      - * Returns a function to use in pseudos for input types
      - * @param {String} type
      - */
      -function createInputPseudo( type ) {
      -	return function( elem ) {
      -		var name = elem.nodeName.toLowerCase();
      -		return name === "input" && elem.type === type;
      -	};
      -}
      -
      -/**
      - * Returns a function to use in pseudos for buttons
      - * @param {String} type
      - */
      -function createButtonPseudo( type ) {
      -	return function( elem ) {
      -		var name = elem.nodeName.toLowerCase();
      -		return ( name === "input" || name === "button" ) && elem.type === type;
      -	};
      -}
      -
      -/**
      - * Returns a function to use in pseudos for :enabled/:disabled
      - * @param {Boolean} disabled true for :disabled; false for :enabled
      - */
      -function createDisabledPseudo( disabled ) {
      -
      -	// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
      -	return function( elem ) {
      -
      -		// Only certain elements can match :enabled or :disabled
      -		// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
      -		// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
      -		if ( "form" in elem ) {
      -
      -			// Check for inherited disabledness on relevant non-disabled elements:
      -			// * listed form-associated elements in a disabled fieldset
      -			//   https://html.spec.whatwg.org/multipage/forms.html#category-listed
      -			//   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
      -			// * option elements in a disabled optgroup
      -			//   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
      -			// All such elements have a "form" property.
      -			if ( elem.parentNode && elem.disabled === false ) {
      -
      -				// Option elements defer to a parent optgroup if present
      -				if ( "label" in elem ) {
      -					if ( "label" in elem.parentNode ) {
      -						return elem.parentNode.disabled === disabled;
      -					} else {
      -						return elem.disabled === disabled;
      -					}
      -				}
      -
      -				// Support: IE 6 - 11
      -				// Use the isDisabled shortcut property to check for disabled fieldset ancestors
      -				return elem.isDisabled === disabled ||
      -
      -					// Where there is no isDisabled, check manually
      -					/* jshint -W018 */
      -					elem.isDisabled !== !disabled &&
      -					inDisabledFieldset( elem ) === disabled;
      -			}
      -
      -			return elem.disabled === disabled;
      -
      -		// Try to winnow out elements that can't be disabled before trusting the disabled property.
      -		// Some victims get caught in our net (label, legend, menu, track), but it shouldn't
      -		// even exist on them, let alone have a boolean value.
      -		} else if ( "label" in elem ) {
      -			return elem.disabled === disabled;
      -		}
      -
      -		// Remaining elements are neither :enabled nor :disabled
      -		return false;
      -	};
      -}
      -
      -/**
      - * Returns a function to use in pseudos for positionals
      - * @param {Function} fn
      - */
      -function createPositionalPseudo( fn ) {
      -	return markFunction( function( argument ) {
      -		argument = +argument;
      -		return markFunction( function( seed, matches ) {
      -			var j,
      -				matchIndexes = fn( [], seed.length, argument ),
      -				i = matchIndexes.length;
      -
      -			// Match elements found at the specified indexes
      -			while ( i-- ) {
      -				if ( seed[ ( j = matchIndexes[ i ] ) ] ) {
      -					seed[ j ] = !( matches[ j ] = seed[ j ] );
      -				}
      -			}
      -		} );
      -	} );
      -}
      -
      -/**
      - * Checks a node for validity as a Sizzle context
      - * @param {Element|Object=} context
      - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
      - */
      -function testContext( context ) {
      -	return context && typeof context.getElementsByTagName !== "undefined" && context;
      -}
      -
      -// Expose support vars for convenience
      -support = Sizzle.support = {};
      -
      -/**
      - * Detects XML nodes
      - * @param {Element|Object} elem An element or a document
      - * @returns {Boolean} True iff elem is a non-HTML XML node
      - */
      -isXML = Sizzle.isXML = function( elem ) {
      -	var namespace = elem && elem.namespaceURI,
      -		docElem = elem && ( elem.ownerDocument || elem ).documentElement;
      -
      -	// Support: IE <=8
      -	// Assume HTML when documentElement doesn't yet exist, such as inside loading iframes
      -	// https://bugs.jquery.com/ticket/4833
      -	return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" );
      -};
      -
      -/**
      - * Sets document-related variables once based on the current document
      - * @param {Element|Object} [doc] An element or document object to use to set the document
      - * @returns {Object} Returns the current document
      - */
      -setDocument = Sizzle.setDocument = function( node ) {
      -	var hasCompare, subWindow,
      -		doc = node ? node.ownerDocument || node : preferredDoc;
      -
      -	// Return early if doc is invalid or already selected
      -	// Support: IE 11+, Edge 17 - 18+
      -	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -	// two documents; shallow comparisons work.
      -	// eslint-disable-next-line eqeqeq
      -	if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {
      -		return document;
      -	}
      -
      -	// Update global variables
      -	document = doc;
      -	docElem = document.documentElement;
      -	documentIsHTML = !isXML( document );
      -
      -	// Support: IE 9 - 11+, Edge 12 - 18+
      -	// Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
      -	// Support: IE 11+, Edge 17 - 18+
      -	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -	// two documents; shallow comparisons work.
      -	// eslint-disable-next-line eqeqeq
      -	if ( preferredDoc != document &&
      -		( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {
      -
      -		// Support: IE 11, Edge
      -		if ( subWindow.addEventListener ) {
      -			subWindow.addEventListener( "unload", unloadHandler, false );
      -
      -		// Support: IE 9 - 10 only
      -		} else if ( subWindow.attachEvent ) {
      -			subWindow.attachEvent( "onunload", unloadHandler );
      -		}
      -	}
      -
      -	// Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only,
      -	// Safari 4 - 5 only, Opera <=11.6 - 12.x only
      -	// IE/Edge & older browsers don't support the :scope pseudo-class.
      -	// Support: Safari 6.0 only
      -	// Safari 6.0 supports :scope but it's an alias of :root there.
      -	support.scope = assert( function( el ) {
      -		docElem.appendChild( el ).appendChild( document.createElement( "div" ) );
      -		return typeof el.querySelectorAll !== "undefined" &&
      -			!el.querySelectorAll( ":scope fieldset div" ).length;
      -	} );
      -
      -	/* Attributes
      -	---------------------------------------------------------------------- */
      -
      -	// Support: IE<8
      -	// Verify that getAttribute really returns attributes and not properties
      -	// (excepting IE8 booleans)
      -	support.attributes = assert( function( el ) {
      -		el.className = "i";
      -		return !el.getAttribute( "className" );
      -	} );
      -
      -	/* getElement(s)By*
      -	---------------------------------------------------------------------- */
      -
      -	// Check if getElementsByTagName("*") returns only elements
      -	support.getElementsByTagName = assert( function( el ) {
      -		el.appendChild( document.createComment( "" ) );
      -		return !el.getElementsByTagName( "*" ).length;
      -	} );
      -
      -	// Support: IE<9
      -	support.getElementsByClassName = rnative.test( document.getElementsByClassName );
      -
      -	// Support: IE<10
      -	// Check if getElementById returns elements by name
      -	// The broken getElementById methods don't pick up programmatically-set names,
      -	// so use a roundabout getElementsByName test
      -	support.getById = assert( function( el ) {
      -		docElem.appendChild( el ).id = expando;
      -		return !document.getElementsByName || !document.getElementsByName( expando ).length;
      -	} );
      -
      -	// ID filter and find
      -	if ( support.getById ) {
      -		Expr.filter[ "ID" ] = function( id ) {
      -			var attrId = id.replace( runescape, funescape );
      -			return function( elem ) {
      -				return elem.getAttribute( "id" ) === attrId;
      -			};
      -		};
      -		Expr.find[ "ID" ] = function( id, context ) {
      -			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
      -				var elem = context.getElementById( id );
      -				return elem ? [ elem ] : [];
      -			}
      -		};
      -	} else {
      -		Expr.filter[ "ID" ] =  function( id ) {
      -			var attrId = id.replace( runescape, funescape );
      -			return function( elem ) {
      -				var node = typeof elem.getAttributeNode !== "undefined" &&
      -					elem.getAttributeNode( "id" );
      -				return node && node.value === attrId;
      -			};
      -		};
      -
      -		// Support: IE 6 - 7 only
      -		// getElementById is not reliable as a find shortcut
      -		Expr.find[ "ID" ] = function( id, context ) {
      -			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
      -				var node, i, elems,
      -					elem = context.getElementById( id );
      -
      -				if ( elem ) {
      -
      -					// Verify the id attribute
      -					node = elem.getAttributeNode( "id" );
      -					if ( node && node.value === id ) {
      -						return [ elem ];
      -					}
      -
      -					// Fall back on getElementsByName
      -					elems = context.getElementsByName( id );
      -					i = 0;
      -					while ( ( elem = elems[ i++ ] ) ) {
      -						node = elem.getAttributeNode( "id" );
      -						if ( node && node.value === id ) {
      -							return [ elem ];
      -						}
      -					}
      -				}
      -
      -				return [];
      -			}
      -		};
      -	}
      -
      -	// Tag
      -	Expr.find[ "TAG" ] = support.getElementsByTagName ?
      -		function( tag, context ) {
      -			if ( typeof context.getElementsByTagName !== "undefined" ) {
      -				return context.getElementsByTagName( tag );
      -
      -			// DocumentFragment nodes don't have gEBTN
      -			} else if ( support.qsa ) {
      -				return context.querySelectorAll( tag );
      -			}
      -		} :
      -
      -		function( tag, context ) {
      -			var elem,
      -				tmp = [],
      -				i = 0,
      -
      -				// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
      -				results = context.getElementsByTagName( tag );
      -
      -			// Filter out possible comments
      -			if ( tag === "*" ) {
      -				while ( ( elem = results[ i++ ] ) ) {
      -					if ( elem.nodeType === 1 ) {
      -						tmp.push( elem );
      -					}
      -				}
      -
      -				return tmp;
      -			}
      -			return results;
      -		};
      -
      -	// Class
      -	Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) {
      -		if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
      -			return context.getElementsByClassName( className );
      -		}
      -	};
      -
      -	/* QSA/matchesSelector
      -	---------------------------------------------------------------------- */
      -
      -	// QSA and matchesSelector support
      -
      -	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
      -	rbuggyMatches = [];
      -
      -	// qSa(:focus) reports false when true (Chrome 21)
      -	// We allow this because of a bug in IE8/9 that throws an error
      -	// whenever `document.activeElement` is accessed on an iframe
      -	// So, we allow :focus to pass through QSA all the time to avoid the IE error
      -	// See https://bugs.jquery.com/ticket/13378
      -	rbuggyQSA = [];
      -
      -	if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) {
      -
      -		// Build QSA regex
      -		// Regex strategy adopted from Diego Perini
      -		assert( function( el ) {
      -
      -			var input;
      -
      -			// Select is set to empty string on purpose
      -			// This is to test IE's treatment of not explicitly
      -			// setting a boolean content attribute,
      -			// since its presence should be enough
      -			// https://bugs.jquery.com/ticket/12359
      -			docElem.appendChild( el ).innerHTML = "" +
      -				"";
      -
      -			// Support: IE8, Opera 11-12.16
      -			// Nothing should be selected when empty strings follow ^= or $= or *=
      -			// The test attribute must be unknown in Opera but "safe" for WinRT
      -			// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
      -			if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) {
      -				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
      -			}
      -
      -			// Support: IE8
      -			// Boolean attributes and "value" are not treated correctly
      -			if ( !el.querySelectorAll( "[selected]" ).length ) {
      -				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
      -			}
      -
      -			// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
      -			if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
      -				rbuggyQSA.push( "~=" );
      -			}
      -
      -			// Support: IE 11+, Edge 15 - 18+
      -			// IE 11/Edge don't find elements on a `[name='']` query in some cases.
      -			// Adding a temporary attribute to the document before the selection works
      -			// around the issue.
      -			// Interestingly, IE 10 & older don't seem to have the issue.
      -			input = document.createElement( "input" );
      -			input.setAttribute( "name", "" );
      -			el.appendChild( input );
      -			if ( !el.querySelectorAll( "[name='']" ).length ) {
      -				rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" +
      -					whitespace + "*(?:''|\"\")" );
      -			}
      -
      -			// Webkit/Opera - :checked should return selected option elements
      -			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
      -			// IE8 throws error here and will not see later tests
      -			if ( !el.querySelectorAll( ":checked" ).length ) {
      -				rbuggyQSA.push( ":checked" );
      -			}
      -
      -			// Support: Safari 8+, iOS 8+
      -			// https://bugs.webkit.org/show_bug.cgi?id=136851
      -			// In-page `selector#id sibling-combinator selector` fails
      -			if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
      -				rbuggyQSA.push( ".#.+[+~]" );
      -			}
      -
      -			// Support: Firefox <=3.6 - 5 only
      -			// Old Firefox doesn't throw on a badly-escaped identifier.
      -			el.querySelectorAll( "\\\f" );
      -			rbuggyQSA.push( "[\\r\\n\\f]" );
      -		} );
      -
      -		assert( function( el ) {
      -			el.innerHTML = "" +
      -				"";
      -
      -			// Support: Windows 8 Native Apps
      -			// The type and name attributes are restricted during .innerHTML assignment
      -			var input = document.createElement( "input" );
      -			input.setAttribute( "type", "hidden" );
      -			el.appendChild( input ).setAttribute( "name", "D" );
      -
      -			// Support: IE8
      -			// Enforce case-sensitivity of name attribute
      -			if ( el.querySelectorAll( "[name=d]" ).length ) {
      -				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
      -			}
      -
      -			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
      -			// IE8 throws error here and will not see later tests
      -			if ( el.querySelectorAll( ":enabled" ).length !== 2 ) {
      -				rbuggyQSA.push( ":enabled", ":disabled" );
      -			}
      -
      -			// Support: IE9-11+
      -			// IE's :disabled selector does not pick up the children of disabled fieldsets
      -			docElem.appendChild( el ).disabled = true;
      -			if ( el.querySelectorAll( ":disabled" ).length !== 2 ) {
      -				rbuggyQSA.push( ":enabled", ":disabled" );
      -			}
      -
      -			// Support: Opera 10 - 11 only
      -			// Opera 10-11 does not throw on post-comma invalid pseudos
      -			el.querySelectorAll( "*,:x" );
      -			rbuggyQSA.push( ",.*:" );
      -		} );
      -	}
      -
      -	if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches ||
      -		docElem.webkitMatchesSelector ||
      -		docElem.mozMatchesSelector ||
      -		docElem.oMatchesSelector ||
      -		docElem.msMatchesSelector ) ) ) ) {
      -
      -		assert( function( el ) {
      -
      -			// Check to see if it's possible to do matchesSelector
      -			// on a disconnected node (IE 9)
      -			support.disconnectedMatch = matches.call( el, "*" );
      -
      -			// This should fail with an exception
      -			// Gecko does not error, returns false instead
      -			matches.call( el, "[s!='']:x" );
      -			rbuggyMatches.push( "!=", pseudos );
      -		} );
      -	}
      -
      -	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
      -	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) );
      -
      -	/* Contains
      -	---------------------------------------------------------------------- */
      -	hasCompare = rnative.test( docElem.compareDocumentPosition );
      -
      -	// Element contains another
      -	// Purposefully self-exclusive
      -	// As in, an element does not contain itself
      -	contains = hasCompare || rnative.test( docElem.contains ) ?
      -		function( a, b ) {
      -			var adown = a.nodeType === 9 ? a.documentElement : a,
      -				bup = b && b.parentNode;
      -			return a === bup || !!( bup && bup.nodeType === 1 && (
      -				adown.contains ?
      -					adown.contains( bup ) :
      -					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
      -			) );
      -		} :
      -		function( a, b ) {
      -			if ( b ) {
      -				while ( ( b = b.parentNode ) ) {
      -					if ( b === a ) {
      -						return true;
      -					}
      -				}
      -			}
      -			return false;
      -		};
      -
      -	/* Sorting
      -	---------------------------------------------------------------------- */
      -
      -	// Document order sorting
      -	sortOrder = hasCompare ?
      -	function( a, b ) {
      -
      -		// Flag for duplicate removal
      -		if ( a === b ) {
      -			hasDuplicate = true;
      -			return 0;
      -		}
      -
      -		// Sort on method existence if only one input has compareDocumentPosition
      -		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
      -		if ( compare ) {
      -			return compare;
      -		}
      -
      -		// Calculate position if both inputs belong to the same document
      -		// Support: IE 11+, Edge 17 - 18+
      -		// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -		// two documents; shallow comparisons work.
      -		// eslint-disable-next-line eqeqeq
      -		compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?
      -			a.compareDocumentPosition( b ) :
      -
      -			// Otherwise we know they are disconnected
      -			1;
      -
      -		// Disconnected nodes
      -		if ( compare & 1 ||
      -			( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {
      -
      -			// Choose the first element that is related to our preferred document
      -			// Support: IE 11+, Edge 17 - 18+
      -			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -			// two documents; shallow comparisons work.
      -			// eslint-disable-next-line eqeqeq
      -			if ( a == document || a.ownerDocument == preferredDoc &&
      -				contains( preferredDoc, a ) ) {
      -				return -1;
      -			}
      -
      -			// Support: IE 11+, Edge 17 - 18+
      -			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -			// two documents; shallow comparisons work.
      -			// eslint-disable-next-line eqeqeq
      -			if ( b == document || b.ownerDocument == preferredDoc &&
      -				contains( preferredDoc, b ) ) {
      -				return 1;
      -			}
      -
      -			// Maintain original order
      -			return sortInput ?
      -				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
      -				0;
      -		}
      -
      -		return compare & 4 ? -1 : 1;
      -	} :
      -	function( a, b ) {
      -
      -		// Exit early if the nodes are identical
      -		if ( a === b ) {
      -			hasDuplicate = true;
      -			return 0;
      -		}
      -
      -		var cur,
      -			i = 0,
      -			aup = a.parentNode,
      -			bup = b.parentNode,
      -			ap = [ a ],
      -			bp = [ b ];
      -
      -		// Parentless nodes are either documents or disconnected
      -		if ( !aup || !bup ) {
      -
      -			// Support: IE 11+, Edge 17 - 18+
      -			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -			// two documents; shallow comparisons work.
      -			/* eslint-disable eqeqeq */
      -			return a == document ? -1 :
      -				b == document ? 1 :
      -				/* eslint-enable eqeqeq */
      -				aup ? -1 :
      -				bup ? 1 :
      -				sortInput ?
      -				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
      -				0;
      -
      -		// If the nodes are siblings, we can do a quick check
      -		} else if ( aup === bup ) {
      -			return siblingCheck( a, b );
      -		}
      -
      -		// Otherwise we need full lists of their ancestors for comparison
      -		cur = a;
      -		while ( ( cur = cur.parentNode ) ) {
      -			ap.unshift( cur );
      -		}
      -		cur = b;
      -		while ( ( cur = cur.parentNode ) ) {
      -			bp.unshift( cur );
      -		}
      -
      -		// Walk down the tree looking for a discrepancy
      -		while ( ap[ i ] === bp[ i ] ) {
      -			i++;
      -		}
      -
      -		return i ?
      -
      -			// Do a sibling check if the nodes have a common ancestor
      -			siblingCheck( ap[ i ], bp[ i ] ) :
      -
      -			// Otherwise nodes in our document sort first
      -			// Support: IE 11+, Edge 17 - 18+
      -			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -			// two documents; shallow comparisons work.
      -			/* eslint-disable eqeqeq */
      -			ap[ i ] == preferredDoc ? -1 :
      -			bp[ i ] == preferredDoc ? 1 :
      -			/* eslint-enable eqeqeq */
      -			0;
      -	};
      -
      -	return document;
      -};
      -
      -Sizzle.matches = function( expr, elements ) {
      -	return Sizzle( expr, null, null, elements );
      -};
      -
      -Sizzle.matchesSelector = function( elem, expr ) {
      -	setDocument( elem );
      -
      -	if ( support.matchesSelector && documentIsHTML &&
      -		!nonnativeSelectorCache[ expr + " " ] &&
      -		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
      -		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
      -
      -		try {
      -			var ret = matches.call( elem, expr );
      -
      -			// IE 9's matchesSelector returns false on disconnected nodes
      -			if ( ret || support.disconnectedMatch ||
      -
      -				// As well, disconnected nodes are said to be in a document
      -				// fragment in IE 9
      -				elem.document && elem.document.nodeType !== 11 ) {
      -				return ret;
      -			}
      -		} catch ( e ) {
      -			nonnativeSelectorCache( expr, true );
      -		}
      -	}
      -
      -	return Sizzle( expr, document, null, [ elem ] ).length > 0;
      -};
      -
      -Sizzle.contains = function( context, elem ) {
      -
      -	// Set document vars if needed
      -	// Support: IE 11+, Edge 17 - 18+
      -	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -	// two documents; shallow comparisons work.
      -	// eslint-disable-next-line eqeqeq
      -	if ( ( context.ownerDocument || context ) != document ) {
      -		setDocument( context );
      -	}
      -	return contains( context, elem );
      -};
      -
      -Sizzle.attr = function( elem, name ) {
      -
      -	// Set document vars if needed
      -	// Support: IE 11+, Edge 17 - 18+
      -	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -	// two documents; shallow comparisons work.
      -	// eslint-disable-next-line eqeqeq
      -	if ( ( elem.ownerDocument || elem ) != document ) {
      -		setDocument( elem );
      -	}
      -
      -	var fn = Expr.attrHandle[ name.toLowerCase() ],
      -
      -		// Don't get fooled by Object.prototype properties (jQuery #13807)
      -		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
      -			fn( elem, name, !documentIsHTML ) :
      -			undefined;
      -
      -	return val !== undefined ?
      -		val :
      -		support.attributes || !documentIsHTML ?
      -			elem.getAttribute( name ) :
      -			( val = elem.getAttributeNode( name ) ) && val.specified ?
      -				val.value :
      -				null;
      -};
      -
      -Sizzle.escape = function( sel ) {
      -	return ( sel + "" ).replace( rcssescape, fcssescape );
      -};
      -
      -Sizzle.error = function( msg ) {
      -	throw new Error( "Syntax error, unrecognized expression: " + msg );
      -};
      -
      -/**
      - * Document sorting and removing duplicates
      - * @param {ArrayLike} results
      - */
      -Sizzle.uniqueSort = function( results ) {
      -	var elem,
      -		duplicates = [],
      -		j = 0,
      -		i = 0;
      -
      -	// Unless we *know* we can detect duplicates, assume their presence
      -	hasDuplicate = !support.detectDuplicates;
      -	sortInput = !support.sortStable && results.slice( 0 );
      -	results.sort( sortOrder );
      -
      -	if ( hasDuplicate ) {
      -		while ( ( elem = results[ i++ ] ) ) {
      -			if ( elem === results[ i ] ) {
      -				j = duplicates.push( i );
      -			}
      -		}
      -		while ( j-- ) {
      -			results.splice( duplicates[ j ], 1 );
      -		}
      -	}
      -
      -	// Clear input after sorting to release objects
      -	// See https://github.com/jquery/sizzle/pull/225
      -	sortInput = null;
      -
      -	return results;
      -};
      -
      -/**
      - * Utility function for retrieving the text value of an array of DOM nodes
      - * @param {Array|Element} elem
      - */
      -getText = Sizzle.getText = function( elem ) {
      -	var node,
      -		ret = "",
      -		i = 0,
      -		nodeType = elem.nodeType;
      -
      -	if ( !nodeType ) {
      -
      -		// If no nodeType, this is expected to be an array
      -		while ( ( node = elem[ i++ ] ) ) {
      -
      -			// Do not traverse comment nodes
      -			ret += getText( node );
      -		}
      -	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
      -
      -		// Use textContent for elements
      -		// innerText usage removed for consistency of new lines (jQuery #11153)
      -		if ( typeof elem.textContent === "string" ) {
      -			return elem.textContent;
      -		} else {
      -
      -			// Traverse its children
      -			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
      -				ret += getText( elem );
      -			}
      -		}
      -	} else if ( nodeType === 3 || nodeType === 4 ) {
      -		return elem.nodeValue;
      -	}
      -
      -	// Do not include comment or processing instruction nodes
      -
      -	return ret;
      -};
      -
      -Expr = Sizzle.selectors = {
      -
      -	// Can be adjusted by the user
      -	cacheLength: 50,
      -
      -	createPseudo: markFunction,
      -
      -	match: matchExpr,
      -
      -	attrHandle: {},
      -
      -	find: {},
      -
      -	relative: {
      -		">": { dir: "parentNode", first: true },
      -		" ": { dir: "parentNode" },
      -		"+": { dir: "previousSibling", first: true },
      -		"~": { dir: "previousSibling" }
      -	},
      -
      -	preFilter: {
      -		"ATTR": function( match ) {
      -			match[ 1 ] = match[ 1 ].replace( runescape, funescape );
      -
      -			// Move the given value to match[3] whether quoted or unquoted
      -			match[ 3 ] = ( match[ 3 ] || match[ 4 ] ||
      -				match[ 5 ] || "" ).replace( runescape, funescape );
      -
      -			if ( match[ 2 ] === "~=" ) {
      -				match[ 3 ] = " " + match[ 3 ] + " ";
      -			}
      -
      -			return match.slice( 0, 4 );
      -		},
      -
      -		"CHILD": function( match ) {
      -
      -			/* matches from matchExpr["CHILD"]
      -				1 type (only|nth|...)
      -				2 what (child|of-type)
      -				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
      -				4 xn-component of xn+y argument ([+-]?\d*n|)
      -				5 sign of xn-component
      -				6 x of xn-component
      -				7 sign of y-component
      -				8 y of y-component
      -			*/
      -			match[ 1 ] = match[ 1 ].toLowerCase();
      -
      -			if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {
      -
      -				// nth-* requires argument
      -				if ( !match[ 3 ] ) {
      -					Sizzle.error( match[ 0 ] );
      -				}
      -
      -				// numeric x and y parameters for Expr.filter.CHILD
      -				// remember that false/true cast respectively to 0/1
      -				match[ 4 ] = +( match[ 4 ] ?
      -					match[ 5 ] + ( match[ 6 ] || 1 ) :
      -					2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) );
      -				match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );
      -
      -				// other types prohibit arguments
      -			} else if ( match[ 3 ] ) {
      -				Sizzle.error( match[ 0 ] );
      -			}
      -
      -			return match;
      -		},
      -
      -		"PSEUDO": function( match ) {
      -			var excess,
      -				unquoted = !match[ 6 ] && match[ 2 ];
      -
      -			if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) {
      -				return null;
      -			}
      -
      -			// Accept quoted arguments as-is
      -			if ( match[ 3 ] ) {
      -				match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
      -
      -			// Strip excess characters from unquoted arguments
      -			} else if ( unquoted && rpseudo.test( unquoted ) &&
      -
      -				// Get excess from tokenize (recursively)
      -				( excess = tokenize( unquoted, true ) ) &&
      -
      -				// advance to the next closing parenthesis
      -				( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) {
      -
      -				// excess is a negative index
      -				match[ 0 ] = match[ 0 ].slice( 0, excess );
      -				match[ 2 ] = unquoted.slice( 0, excess );
      -			}
      -
      -			// Return only captures needed by the pseudo filter method (type and argument)
      -			return match.slice( 0, 3 );
      -		}
      -	},
      -
      -	filter: {
      -
      -		"TAG": function( nodeNameSelector ) {
      -			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
      -			return nodeNameSelector === "*" ?
      -				function() {
      -					return true;
      -				} :
      -				function( elem ) {
      -					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
      -				};
      -		},
      -
      -		"CLASS": function( className ) {
      -			var pattern = classCache[ className + " " ];
      -
      -			return pattern ||
      -				( pattern = new RegExp( "(^|" + whitespace +
      -					")" + className + "(" + whitespace + "|$)" ) ) && classCache(
      -						className, function( elem ) {
      -							return pattern.test(
      -								typeof elem.className === "string" && elem.className ||
      -								typeof elem.getAttribute !== "undefined" &&
      -									elem.getAttribute( "class" ) ||
      -								""
      -							);
      -				} );
      -		},
      -
      -		"ATTR": function( name, operator, check ) {
      -			return function( elem ) {
      -				var result = Sizzle.attr( elem, name );
      -
      -				if ( result == null ) {
      -					return operator === "!=";
      -				}
      -				if ( !operator ) {
      -					return true;
      -				}
      -
      -				result += "";
      -
      -				/* eslint-disable max-len */
      -
      -				return operator === "=" ? result === check :
      -					operator === "!=" ? result !== check :
      -					operator === "^=" ? check && result.indexOf( check ) === 0 :
      -					operator === "*=" ? check && result.indexOf( check ) > -1 :
      -					operator === "$=" ? check && result.slice( -check.length ) === check :
      -					operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
      -					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
      -					false;
      -				/* eslint-enable max-len */
      -
      -			};
      -		},
      -
      -		"CHILD": function( type, what, _argument, first, last ) {
      -			var simple = type.slice( 0, 3 ) !== "nth",
      -				forward = type.slice( -4 ) !== "last",
      -				ofType = what === "of-type";
      -
      -			return first === 1 && last === 0 ?
      -
      -				// Shortcut for :nth-*(n)
      -				function( elem ) {
      -					return !!elem.parentNode;
      -				} :
      -
      -				function( elem, _context, xml ) {
      -					var cache, uniqueCache, outerCache, node, nodeIndex, start,
      -						dir = simple !== forward ? "nextSibling" : "previousSibling",
      -						parent = elem.parentNode,
      -						name = ofType && elem.nodeName.toLowerCase(),
      -						useCache = !xml && !ofType,
      -						diff = false;
      -
      -					if ( parent ) {
      -
      -						// :(first|last|only)-(child|of-type)
      -						if ( simple ) {
      -							while ( dir ) {
      -								node = elem;
      -								while ( ( node = node[ dir ] ) ) {
      -									if ( ofType ?
      -										node.nodeName.toLowerCase() === name :
      -										node.nodeType === 1 ) {
      -
      -										return false;
      -									}
      -								}
      -
      -								// Reverse direction for :only-* (if we haven't yet done so)
      -								start = dir = type === "only" && !start && "nextSibling";
      -							}
      -							return true;
      -						}
      -
      -						start = [ forward ? parent.firstChild : parent.lastChild ];
      -
      -						// non-xml :nth-child(...) stores cache data on `parent`
      -						if ( forward && useCache ) {
      -
      -							// Seek `elem` from a previously-cached index
      -
      -							// ...in a gzip-friendly way
      -							node = parent;
      -							outerCache = node[ expando ] || ( node[ expando ] = {} );
      -
      -							// Support: IE <9 only
      -							// Defend against cloned attroperties (jQuery gh-1709)
      -							uniqueCache = outerCache[ node.uniqueID ] ||
      -								( outerCache[ node.uniqueID ] = {} );
      -
      -							cache = uniqueCache[ type ] || [];
      -							nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
      -							diff = nodeIndex && cache[ 2 ];
      -							node = nodeIndex && parent.childNodes[ nodeIndex ];
      -
      -							while ( ( node = ++nodeIndex && node && node[ dir ] ||
      -
      -								// Fallback to seeking `elem` from the start
      -								( diff = nodeIndex = 0 ) || start.pop() ) ) {
      -
      -								// When found, cache indexes on `parent` and break
      -								if ( node.nodeType === 1 && ++diff && node === elem ) {
      -									uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
      -									break;
      -								}
      -							}
      -
      -						} else {
      -
      -							// Use previously-cached element index if available
      -							if ( useCache ) {
      -
      -								// ...in a gzip-friendly way
      -								node = elem;
      -								outerCache = node[ expando ] || ( node[ expando ] = {} );
      -
      -								// Support: IE <9 only
      -								// Defend against cloned attroperties (jQuery gh-1709)
      -								uniqueCache = outerCache[ node.uniqueID ] ||
      -									( outerCache[ node.uniqueID ] = {} );
      -
      -								cache = uniqueCache[ type ] || [];
      -								nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
      -								diff = nodeIndex;
      -							}
      -
      -							// xml :nth-child(...)
      -							// or :nth-last-child(...) or :nth(-last)?-of-type(...)
      -							if ( diff === false ) {
      -
      -								// Use the same loop as above to seek `elem` from the start
      -								while ( ( node = ++nodeIndex && node && node[ dir ] ||
      -									( diff = nodeIndex = 0 ) || start.pop() ) ) {
      -
      -									if ( ( ofType ?
      -										node.nodeName.toLowerCase() === name :
      -										node.nodeType === 1 ) &&
      -										++diff ) {
      -
      -										// Cache the index of each encountered element
      -										if ( useCache ) {
      -											outerCache = node[ expando ] ||
      -												( node[ expando ] = {} );
      -
      -											// Support: IE <9 only
      -											// Defend against cloned attroperties (jQuery gh-1709)
      -											uniqueCache = outerCache[ node.uniqueID ] ||
      -												( outerCache[ node.uniqueID ] = {} );
      -
      -											uniqueCache[ type ] = [ dirruns, diff ];
      -										}
      -
      -										if ( node === elem ) {
      -											break;
      -										}
      -									}
      -								}
      -							}
      -						}
      -
      -						// Incorporate the offset, then check against cycle size
      -						diff -= last;
      -						return diff === first || ( diff % first === 0 && diff / first >= 0 );
      -					}
      -				};
      -		},
      -
      -		"PSEUDO": function( pseudo, argument ) {
      -
      -			// pseudo-class names are case-insensitive
      -			// http://www.w3.org/TR/selectors/#pseudo-classes
      -			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
      -			// Remember that setFilters inherits from pseudos
      -			var args,
      -				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
      -					Sizzle.error( "unsupported pseudo: " + pseudo );
      -
      -			// The user may use createPseudo to indicate that
      -			// arguments are needed to create the filter function
      -			// just as Sizzle does
      -			if ( fn[ expando ] ) {
      -				return fn( argument );
      -			}
      -
      -			// But maintain support for old signatures
      -			if ( fn.length > 1 ) {
      -				args = [ pseudo, pseudo, "", argument ];
      -				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
      -					markFunction( function( seed, matches ) {
      -						var idx,
      -							matched = fn( seed, argument ),
      -							i = matched.length;
      -						while ( i-- ) {
      -							idx = indexOf( seed, matched[ i ] );
      -							seed[ idx ] = !( matches[ idx ] = matched[ i ] );
      -						}
      -					} ) :
      -					function( elem ) {
      -						return fn( elem, 0, args );
      -					};
      -			}
      -
      -			return fn;
      -		}
      -	},
      -
      -	pseudos: {
      -
      -		// Potentially complex pseudos
      -		"not": markFunction( function( selector ) {
      -
      -			// Trim the selector passed to compile
      -			// to avoid treating leading and trailing
      -			// spaces as combinators
      -			var input = [],
      -				results = [],
      -				matcher = compile( selector.replace( rtrim, "$1" ) );
      -
      -			return matcher[ expando ] ?
      -				markFunction( function( seed, matches, _context, xml ) {
      -					var elem,
      -						unmatched = matcher( seed, null, xml, [] ),
      -						i = seed.length;
      -
      -					// Match elements unmatched by `matcher`
      -					while ( i-- ) {
      -						if ( ( elem = unmatched[ i ] ) ) {
      -							seed[ i ] = !( matches[ i ] = elem );
      -						}
      -					}
      -				} ) :
      -				function( elem, _context, xml ) {
      -					input[ 0 ] = elem;
      -					matcher( input, null, xml, results );
      -
      -					// Don't keep the element (issue #299)
      -					input[ 0 ] = null;
      -					return !results.pop();
      -				};
      -		} ),
      -
      -		"has": markFunction( function( selector ) {
      -			return function( elem ) {
      -				return Sizzle( selector, elem ).length > 0;
      -			};
      -		} ),
      -
      -		"contains": markFunction( function( text ) {
      -			text = text.replace( runescape, funescape );
      -			return function( elem ) {
      -				return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1;
      -			};
      -		} ),
      -
      -		// "Whether an element is represented by a :lang() selector
      -		// is based solely on the element's language value
      -		// being equal to the identifier C,
      -		// or beginning with the identifier C immediately followed by "-".
      -		// The matching of C against the element's language value is performed case-insensitively.
      -		// The identifier C does not have to be a valid language name."
      -		// http://www.w3.org/TR/selectors/#lang-pseudo
      -		"lang": markFunction( function( lang ) {
      -
      -			// lang value must be a valid identifier
      -			if ( !ridentifier.test( lang || "" ) ) {
      -				Sizzle.error( "unsupported lang: " + lang );
      -			}
      -			lang = lang.replace( runescape, funescape ).toLowerCase();
      -			return function( elem ) {
      -				var elemLang;
      -				do {
      -					if ( ( elemLang = documentIsHTML ?
      -						elem.lang :
      -						elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) {
      -
      -						elemLang = elemLang.toLowerCase();
      -						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
      -					}
      -				} while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );
      -				return false;
      -			};
      -		} ),
      -
      -		// Miscellaneous
      -		"target": function( elem ) {
      -			var hash = window.location && window.location.hash;
      -			return hash && hash.slice( 1 ) === elem.id;
      -		},
      -
      -		"root": function( elem ) {
      -			return elem === docElem;
      -		},
      -
      -		"focus": function( elem ) {
      -			return elem === document.activeElement &&
      -				( !document.hasFocus || document.hasFocus() ) &&
      -				!!( elem.type || elem.href || ~elem.tabIndex );
      -		},
      -
      -		// Boolean properties
      -		"enabled": createDisabledPseudo( false ),
      -		"disabled": createDisabledPseudo( true ),
      -
      -		"checked": function( elem ) {
      -
      -			// In CSS3, :checked should return both checked and selected elements
      -			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
      -			var nodeName = elem.nodeName.toLowerCase();
      -			return ( nodeName === "input" && !!elem.checked ) ||
      -				( nodeName === "option" && !!elem.selected );
      -		},
      -
      -		"selected": function( elem ) {
      -
      -			// Accessing this property makes selected-by-default
      -			// options in Safari work properly
      -			if ( elem.parentNode ) {
      -				// eslint-disable-next-line no-unused-expressions
      -				elem.parentNode.selectedIndex;
      -			}
      -
      -			return elem.selected === true;
      -		},
      -
      -		// Contents
      -		"empty": function( elem ) {
      -
      -			// http://www.w3.org/TR/selectors/#empty-pseudo
      -			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
      -			//   but not by others (comment: 8; processing instruction: 7; etc.)
      -			// nodeType < 6 works because attributes (2) do not appear as children
      -			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
      -				if ( elem.nodeType < 6 ) {
      -					return false;
      -				}
      -			}
      -			return true;
      -		},
      -
      -		"parent": function( elem ) {
      -			return !Expr.pseudos[ "empty" ]( elem );
      -		},
      -
      -		// Element/input types
      -		"header": function( elem ) {
      -			return rheader.test( elem.nodeName );
      -		},
      -
      -		"input": function( elem ) {
      -			return rinputs.test( elem.nodeName );
      -		},
      -
      -		"button": function( elem ) {
      -			var name = elem.nodeName.toLowerCase();
      -			return name === "input" && elem.type === "button" || name === "button";
      -		},
      -
      -		"text": function( elem ) {
      -			var attr;
      -			return elem.nodeName.toLowerCase() === "input" &&
      -				elem.type === "text" &&
      -
      -				// Support: IE<8
      -				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
      -				( ( attr = elem.getAttribute( "type" ) ) == null ||
      -					attr.toLowerCase() === "text" );
      -		},
      -
      -		// Position-in-collection
      -		"first": createPositionalPseudo( function() {
      -			return [ 0 ];
      -		} ),
      -
      -		"last": createPositionalPseudo( function( _matchIndexes, length ) {
      -			return [ length - 1 ];
      -		} ),
      -
      -		"eq": createPositionalPseudo( function( _matchIndexes, length, argument ) {
      -			return [ argument < 0 ? argument + length : argument ];
      -		} ),
      -
      -		"even": createPositionalPseudo( function( matchIndexes, length ) {
      -			var i = 0;
      -			for ( ; i < length; i += 2 ) {
      -				matchIndexes.push( i );
      -			}
      -			return matchIndexes;
      -		} ),
      -
      -		"odd": createPositionalPseudo( function( matchIndexes, length ) {
      -			var i = 1;
      -			for ( ; i < length; i += 2 ) {
      -				matchIndexes.push( i );
      -			}
      -			return matchIndexes;
      -		} ),
      -
      -		"lt": createPositionalPseudo( function( matchIndexes, length, argument ) {
      -			var i = argument < 0 ?
      -				argument + length :
      -				argument > length ?
      -					length :
      -					argument;
      -			for ( ; --i >= 0; ) {
      -				matchIndexes.push( i );
      -			}
      -			return matchIndexes;
      -		} ),
      -
      -		"gt": createPositionalPseudo( function( matchIndexes, length, argument ) {
      -			var i = argument < 0 ? argument + length : argument;
      -			for ( ; ++i < length; ) {
      -				matchIndexes.push( i );
      -			}
      -			return matchIndexes;
      -		} )
      -	}
      -};
      -
      -Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ];
      -
      -// Add button/input type pseudos
      -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
      -	Expr.pseudos[ i ] = createInputPseudo( i );
      -}
      -for ( i in { submit: true, reset: true } ) {
      -	Expr.pseudos[ i ] = createButtonPseudo( i );
      -}
      -
      -// Easy API for creating new setFilters
      -function setFilters() {}
      -setFilters.prototype = Expr.filters = Expr.pseudos;
      -Expr.setFilters = new setFilters();
      -
      -tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
      -	var matched, match, tokens, type,
      -		soFar, groups, preFilters,
      -		cached = tokenCache[ selector + " " ];
      -
      -	if ( cached ) {
      -		return parseOnly ? 0 : cached.slice( 0 );
      -	}
      -
      -	soFar = selector;
      -	groups = [];
      -	preFilters = Expr.preFilter;
      -
      -	while ( soFar ) {
      -
      -		// Comma and first run
      -		if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
      -			if ( match ) {
      -
      -				// Don't consume trailing commas as valid
      -				soFar = soFar.slice( match[ 0 ].length ) || soFar;
      -			}
      -			groups.push( ( tokens = [] ) );
      -		}
      -
      -		matched = false;
      -
      -		// Combinators
      -		if ( ( match = rcombinators.exec( soFar ) ) ) {
      -			matched = match.shift();
      -			tokens.push( {
      -				value: matched,
      -
      -				// Cast descendant combinators to space
      -				type: match[ 0 ].replace( rtrim, " " )
      -			} );
      -			soFar = soFar.slice( matched.length );
      -		}
      -
      -		// Filters
      -		for ( type in Expr.filter ) {
      -			if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
      -				( match = preFilters[ type ]( match ) ) ) ) {
      -				matched = match.shift();
      -				tokens.push( {
      -					value: matched,
      -					type: type,
      -					matches: match
      -				} );
      -				soFar = soFar.slice( matched.length );
      -			}
      -		}
      -
      -		if ( !matched ) {
      -			break;
      -		}
      -	}
      -
      -	// Return the length of the invalid excess
      -	// if we're just parsing
      -	// Otherwise, throw an error or return tokens
      -	return parseOnly ?
      -		soFar.length :
      -		soFar ?
      -			Sizzle.error( selector ) :
      -
      -			// Cache the tokens
      -			tokenCache( selector, groups ).slice( 0 );
      -};
      -
      -function toSelector( tokens ) {
      -	var i = 0,
      -		len = tokens.length,
      -		selector = "";
      -	for ( ; i < len; i++ ) {
      -		selector += tokens[ i ].value;
      -	}
      -	return selector;
      -}
      -
      -function addCombinator( matcher, combinator, base ) {
      -	var dir = combinator.dir,
      -		skip = combinator.next,
      -		key = skip || dir,
      -		checkNonElements = base && key === "parentNode",
      -		doneName = done++;
      -
      -	return combinator.first ?
      -
      -		// Check against closest ancestor/preceding element
      -		function( elem, context, xml ) {
      -			while ( ( elem = elem[ dir ] ) ) {
      -				if ( elem.nodeType === 1 || checkNonElements ) {
      -					return matcher( elem, context, xml );
      -				}
      -			}
      -			return false;
      -		} :
      -
      -		// Check against all ancestor/preceding elements
      -		function( elem, context, xml ) {
      -			var oldCache, uniqueCache, outerCache,
      -				newCache = [ dirruns, doneName ];
      -
      -			// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
      -			if ( xml ) {
      -				while ( ( elem = elem[ dir ] ) ) {
      -					if ( elem.nodeType === 1 || checkNonElements ) {
      -						if ( matcher( elem, context, xml ) ) {
      -							return true;
      -						}
      -					}
      -				}
      -			} else {
      -				while ( ( elem = elem[ dir ] ) ) {
      -					if ( elem.nodeType === 1 || checkNonElements ) {
      -						outerCache = elem[ expando ] || ( elem[ expando ] = {} );
      -
      -						// Support: IE <9 only
      -						// Defend against cloned attroperties (jQuery gh-1709)
      -						uniqueCache = outerCache[ elem.uniqueID ] ||
      -							( outerCache[ elem.uniqueID ] = {} );
      -
      -						if ( skip && skip === elem.nodeName.toLowerCase() ) {
      -							elem = elem[ dir ] || elem;
      -						} else if ( ( oldCache = uniqueCache[ key ] ) &&
      -							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
      -
      -							// Assign to newCache so results back-propagate to previous elements
      -							return ( newCache[ 2 ] = oldCache[ 2 ] );
      -						} else {
      -
      -							// Reuse newcache so results back-propagate to previous elements
      -							uniqueCache[ key ] = newCache;
      -
      -							// A match means we're done; a fail means we have to keep checking
      -							if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
      -								return true;
      -							}
      -						}
      -					}
      -				}
      -			}
      -			return false;
      -		};
      -}
      -
      -function elementMatcher( matchers ) {
      -	return matchers.length > 1 ?
      -		function( elem, context, xml ) {
      -			var i = matchers.length;
      -			while ( i-- ) {
      -				if ( !matchers[ i ]( elem, context, xml ) ) {
      -					return false;
      -				}
      -			}
      -			return true;
      -		} :
      -		matchers[ 0 ];
      -}
      -
      -function multipleContexts( selector, contexts, results ) {
      -	var i = 0,
      -		len = contexts.length;
      -	for ( ; i < len; i++ ) {
      -		Sizzle( selector, contexts[ i ], results );
      -	}
      -	return results;
      -}
      -
      -function condense( unmatched, map, filter, context, xml ) {
      -	var elem,
      -		newUnmatched = [],
      -		i = 0,
      -		len = unmatched.length,
      -		mapped = map != null;
      -
      -	for ( ; i < len; i++ ) {
      -		if ( ( elem = unmatched[ i ] ) ) {
      -			if ( !filter || filter( elem, context, xml ) ) {
      -				newUnmatched.push( elem );
      -				if ( mapped ) {
      -					map.push( i );
      -				}
      -			}
      -		}
      -	}
      -
      -	return newUnmatched;
      -}
      -
      -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
      -	if ( postFilter && !postFilter[ expando ] ) {
      -		postFilter = setMatcher( postFilter );
      -	}
      -	if ( postFinder && !postFinder[ expando ] ) {
      -		postFinder = setMatcher( postFinder, postSelector );
      -	}
      -	return markFunction( function( seed, results, context, xml ) {
      -		var temp, i, elem,
      -			preMap = [],
      -			postMap = [],
      -			preexisting = results.length,
      -
      -			// Get initial elements from seed or context
      -			elems = seed || multipleContexts(
      -				selector || "*",
      -				context.nodeType ? [ context ] : context,
      -				[]
      -			),
      -
      -			// Prefilter to get matcher input, preserving a map for seed-results synchronization
      -			matcherIn = preFilter && ( seed || !selector ) ?
      -				condense( elems, preMap, preFilter, context, xml ) :
      -				elems,
      -
      -			matcherOut = matcher ?
      -
      -				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
      -				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
      -
      -					// ...intermediate processing is necessary
      -					[] :
      -
      -					// ...otherwise use results directly
      -					results :
      -				matcherIn;
      -
      -		// Find primary matches
      -		if ( matcher ) {
      -			matcher( matcherIn, matcherOut, context, xml );
      -		}
      -
      -		// Apply postFilter
      -		if ( postFilter ) {
      -			temp = condense( matcherOut, postMap );
      -			postFilter( temp, [], context, xml );
      -
      -			// Un-match failing elements by moving them back to matcherIn
      -			i = temp.length;
      -			while ( i-- ) {
      -				if ( ( elem = temp[ i ] ) ) {
      -					matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );
      -				}
      -			}
      -		}
      -
      -		if ( seed ) {
      -			if ( postFinder || preFilter ) {
      -				if ( postFinder ) {
      -
      -					// Get the final matcherOut by condensing this intermediate into postFinder contexts
      -					temp = [];
      -					i = matcherOut.length;
      -					while ( i-- ) {
      -						if ( ( elem = matcherOut[ i ] ) ) {
      -
      -							// Restore matcherIn since elem is not yet a final match
      -							temp.push( ( matcherIn[ i ] = elem ) );
      -						}
      -					}
      -					postFinder( null, ( matcherOut = [] ), temp, xml );
      -				}
      -
      -				// Move matched elements from seed to results to keep them synchronized
      -				i = matcherOut.length;
      -				while ( i-- ) {
      -					if ( ( elem = matcherOut[ i ] ) &&
      -						( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) {
      -
      -						seed[ temp ] = !( results[ temp ] = elem );
      -					}
      -				}
      -			}
      -
      -		// Add elements to results, through postFinder if defined
      -		} else {
      -			matcherOut = condense(
      -				matcherOut === results ?
      -					matcherOut.splice( preexisting, matcherOut.length ) :
      -					matcherOut
      -			);
      -			if ( postFinder ) {
      -				postFinder( null, results, matcherOut, xml );
      -			} else {
      -				push.apply( results, matcherOut );
      -			}
      -		}
      -	} );
      -}
      -
      -function matcherFromTokens( tokens ) {
      -	var checkContext, matcher, j,
      -		len = tokens.length,
      -		leadingRelative = Expr.relative[ tokens[ 0 ].type ],
      -		implicitRelative = leadingRelative || Expr.relative[ " " ],
      -		i = leadingRelative ? 1 : 0,
      -
      -		// The foundational matcher ensures that elements are reachable from top-level context(s)
      -		matchContext = addCombinator( function( elem ) {
      -			return elem === checkContext;
      -		}, implicitRelative, true ),
      -		matchAnyContext = addCombinator( function( elem ) {
      -			return indexOf( checkContext, elem ) > -1;
      -		}, implicitRelative, true ),
      -		matchers = [ function( elem, context, xml ) {
      -			var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
      -				( checkContext = context ).nodeType ?
      -					matchContext( elem, context, xml ) :
      -					matchAnyContext( elem, context, xml ) );
      -
      -			// Avoid hanging onto element (issue #299)
      -			checkContext = null;
      -			return ret;
      -		} ];
      -
      -	for ( ; i < len; i++ ) {
      -		if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {
      -			matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
      -		} else {
      -			matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );
      -
      -			// Return special upon seeing a positional matcher
      -			if ( matcher[ expando ] ) {
      -
      -				// Find the next relative operator (if any) for proper handling
      -				j = ++i;
      -				for ( ; j < len; j++ ) {
      -					if ( Expr.relative[ tokens[ j ].type ] ) {
      -						break;
      -					}
      -				}
      -				return setMatcher(
      -					i > 1 && elementMatcher( matchers ),
      -					i > 1 && toSelector(
      -
      -					// If the preceding token was a descendant combinator, insert an implicit any-element `*`
      -					tokens
      -						.slice( 0, i - 1 )
      -						.concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } )
      -					).replace( rtrim, "$1" ),
      -					matcher,
      -					i < j && matcherFromTokens( tokens.slice( i, j ) ),
      -					j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),
      -					j < len && toSelector( tokens )
      -				);
      -			}
      -			matchers.push( matcher );
      -		}
      -	}
      -
      -	return elementMatcher( matchers );
      -}
      -
      -function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
      -	var bySet = setMatchers.length > 0,
      -		byElement = elementMatchers.length > 0,
      -		superMatcher = function( seed, context, xml, results, outermost ) {
      -			var elem, j, matcher,
      -				matchedCount = 0,
      -				i = "0",
      -				unmatched = seed && [],
      -				setMatched = [],
      -				contextBackup = outermostContext,
      -
      -				// We must always have either seed elements or outermost context
      -				elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ),
      -
      -				// Use integer dirruns iff this is the outermost matcher
      -				dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),
      -				len = elems.length;
      -
      -			if ( outermost ) {
      -
      -				// Support: IE 11+, Edge 17 - 18+
      -				// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -				// two documents; shallow comparisons work.
      -				// eslint-disable-next-line eqeqeq
      -				outermostContext = context == document || context || outermost;
      -			}
      -
      -			// Add elements passing elementMatchers directly to results
      -			// Support: IE<9, Safari
      -			// Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id
      -			for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {
      -				if ( byElement && elem ) {
      -					j = 0;
      -
      -					// Support: IE 11+, Edge 17 - 18+
      -					// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
      -					// two documents; shallow comparisons work.
      -					// eslint-disable-next-line eqeqeq
      -					if ( !context && elem.ownerDocument != document ) {
      -						setDocument( elem );
      -						xml = !documentIsHTML;
      -					}
      -					while ( ( matcher = elementMatchers[ j++ ] ) ) {
      -						if ( matcher( elem, context || document, xml ) ) {
      -							results.push( elem );
      -							break;
      -						}
      -					}
      -					if ( outermost ) {
      -						dirruns = dirrunsUnique;
      -					}
      -				}
      -
      -				// Track unmatched elements for set filters
      -				if ( bySet ) {
      -
      -					// They will have gone through all possible matchers
      -					if ( ( elem = !matcher && elem ) ) {
      -						matchedCount--;
      -					}
      -
      -					// Lengthen the array for every element, matched or not
      -					if ( seed ) {
      -						unmatched.push( elem );
      -					}
      -				}
      -			}
      -
      -			// `i` is now the count of elements visited above, and adding it to `matchedCount`
      -			// makes the latter nonnegative.
      -			matchedCount += i;
      -
      -			// Apply set filters to unmatched elements
      -			// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
      -			// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
      -			// no element matchers and no seed.
      -			// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
      -			// case, which will result in a "00" `matchedCount` that differs from `i` but is also
      -			// numerically zero.
      -			if ( bySet && i !== matchedCount ) {
      -				j = 0;
      -				while ( ( matcher = setMatchers[ j++ ] ) ) {
      -					matcher( unmatched, setMatched, context, xml );
      -				}
      -
      -				if ( seed ) {
      -
      -					// Reintegrate element matches to eliminate the need for sorting
      -					if ( matchedCount > 0 ) {
      -						while ( i-- ) {
      -							if ( !( unmatched[ i ] || setMatched[ i ] ) ) {
      -								setMatched[ i ] = pop.call( results );
      -							}
      -						}
      -					}
      -
      -					// Discard index placeholder values to get only actual matches
      -					setMatched = condense( setMatched );
      -				}
      -
      -				// Add matches to results
      -				push.apply( results, setMatched );
      -
      -				// Seedless set matches succeeding multiple successful matchers stipulate sorting
      -				if ( outermost && !seed && setMatched.length > 0 &&
      -					( matchedCount + setMatchers.length ) > 1 ) {
      -
      -					Sizzle.uniqueSort( results );
      -				}
      -			}
      -
      -			// Override manipulation of globals by nested matchers
      -			if ( outermost ) {
      -				dirruns = dirrunsUnique;
      -				outermostContext = contextBackup;
      -			}
      -
      -			return unmatched;
      -		};
      -
      -	return bySet ?
      -		markFunction( superMatcher ) :
      -		superMatcher;
      -}
      -
      -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
      -	var i,
      -		setMatchers = [],
      -		elementMatchers = [],
      -		cached = compilerCache[ selector + " " ];
      -
      -	if ( !cached ) {
      -
      -		// Generate a function of recursive functions that can be used to check each element
      -		if ( !match ) {
      -			match = tokenize( selector );
      -		}
      -		i = match.length;
      -		while ( i-- ) {
      -			cached = matcherFromTokens( match[ i ] );
      -			if ( cached[ expando ] ) {
      -				setMatchers.push( cached );
      -			} else {
      -				elementMatchers.push( cached );
      -			}
      -		}
      -
      -		// Cache the compiled function
      -		cached = compilerCache(
      -			selector,
      -			matcherFromGroupMatchers( elementMatchers, setMatchers )
      -		);
      -
      -		// Save selector and tokenization
      -		cached.selector = selector;
      -	}
      -	return cached;
      -};
      -
      -/**
      - * A low-level selection function that works with Sizzle's compiled
      - *  selector functions
      - * @param {String|Function} selector A selector or a pre-compiled
      - *  selector function built with Sizzle.compile
      - * @param {Element} context
      - * @param {Array} [results]
      - * @param {Array} [seed] A set of elements to match against
      - */
      -select = Sizzle.select = function( selector, context, results, seed ) {
      -	var i, tokens, token, type, find,
      -		compiled = typeof selector === "function" && selector,
      -		match = !seed && tokenize( ( selector = compiled.selector || selector ) );
      -
      -	results = results || [];
      -
      -	// Try to minimize operations if there is only one selector in the list and no seed
      -	// (the latter of which guarantees us context)
      -	if ( match.length === 1 ) {
      -
      -		// Reduce context if the leading compound selector is an ID
      -		tokens = match[ 0 ] = match[ 0 ].slice( 0 );
      -		if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
      -			context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {
      -
      -			context = ( Expr.find[ "ID" ]( token.matches[ 0 ]
      -				.replace( runescape, funescape ), context ) || [] )[ 0 ];
      -			if ( !context ) {
      -				return results;
      -
      -			// Precompiled matchers will still verify ancestry, so step up a level
      -			} else if ( compiled ) {
      -				context = context.parentNode;
      -			}
      -
      -			selector = selector.slice( tokens.shift().value.length );
      -		}
      -
      -		// Fetch a seed set for right-to-left matching
      -		i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length;
      -		while ( i-- ) {
      -			token = tokens[ i ];
      -
      -			// Abort if we hit a combinator
      -			if ( Expr.relative[ ( type = token.type ) ] ) {
      -				break;
      -			}
      -			if ( ( find = Expr.find[ type ] ) ) {
      -
      -				// Search, expanding context for leading sibling combinators
      -				if ( ( seed = find(
      -					token.matches[ 0 ].replace( runescape, funescape ),
      -					rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) ||
      -						context
      -				) ) ) {
      -
      -					// If seed is empty or no tokens remain, we can return early
      -					tokens.splice( i, 1 );
      -					selector = seed.length && toSelector( tokens );
      -					if ( !selector ) {
      -						push.apply( results, seed );
      -						return results;
      -					}
      -
      -					break;
      -				}
      -			}
      -		}
      -	}
      -
      -	// Compile and execute a filtering function if one is not provided
      -	// Provide `match` to avoid retokenization if we modified the selector above
      -	( compiled || compile( selector, match ) )(
      -		seed,
      -		context,
      -		!documentIsHTML,
      -		results,
      -		!context || rsibling.test( selector ) && testContext( context.parentNode ) || context
      -	);
      -	return results;
      -};
      -
      -// One-time assignments
      -
      -// Sort stability
      -support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando;
      -
      -// Support: Chrome 14-35+
      -// Always assume duplicates if they aren't passed to the comparison function
      -support.detectDuplicates = !!hasDuplicate;
      -
      -// Initialize against the default document
      -setDocument();
      -
      -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
      -// Detached nodes confoundingly follow *each other*
      -support.sortDetached = assert( function( el ) {
      -
      -	// Should return 1, but returns 4 (following)
      -	return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1;
      -} );
      -
      -// Support: IE<8
      -// Prevent attribute/property "interpolation"
      -// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
      -if ( !assert( function( el ) {
      -	el.innerHTML = "";
      -	return el.firstChild.getAttribute( "href" ) === "#";
      -} ) ) {
      -	addHandle( "type|href|height|width", function( elem, name, isXML ) {
      -		if ( !isXML ) {
      -			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
      -		}
      -	} );
      -}
      -
      -// Support: IE<9
      -// Use defaultValue in place of getAttribute("value")
      -if ( !support.attributes || !assert( function( el ) {
      -	el.innerHTML = "";
      -	el.firstChild.setAttribute( "value", "" );
      -	return el.firstChild.getAttribute( "value" ) === "";
      -} ) ) {
      -	addHandle( "value", function( elem, _name, isXML ) {
      -		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
      -			return elem.defaultValue;
      -		}
      -	} );
      -}
      -
      -// Support: IE<9
      -// Use getAttributeNode to fetch booleans when getAttribute lies
      -if ( !assert( function( el ) {
      -	return el.getAttribute( "disabled" ) == null;
      -} ) ) {
      -	addHandle( booleans, function( elem, name, isXML ) {
      -		var val;
      -		if ( !isXML ) {
      -			return elem[ name ] === true ? name.toLowerCase() :
      -				( val = elem.getAttributeNode( name ) ) && val.specified ?
      -					val.value :
      -					null;
      -		}
      -	} );
      -}
      -
      -return Sizzle;
      -
      -} )( window );
      -
      -
      -
      -jQuery.find = Sizzle;
      -jQuery.expr = Sizzle.selectors;
      -
      -// Deprecated
      -jQuery.expr[ ":" ] = jQuery.expr.pseudos;
      -jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
      -jQuery.text = Sizzle.getText;
      -jQuery.isXMLDoc = Sizzle.isXML;
      -jQuery.contains = Sizzle.contains;
      -jQuery.escapeSelector = Sizzle.escape;
      -
      -
      -
      -
      -var dir = function( elem, dir, until ) {
      -	var matched = [],
      -		truncate = until !== undefined;
      -
      -	while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
      -		if ( elem.nodeType === 1 ) {
      -			if ( truncate && jQuery( elem ).is( until ) ) {
      -				break;
      -			}
      -			matched.push( elem );
      -		}
      -	}
      -	return matched;
      -};
      -
      -
      -var siblings = function( n, elem ) {
      -	var matched = [];
      -
      -	for ( ; n; n = n.nextSibling ) {
      -		if ( n.nodeType === 1 && n !== elem ) {
      -			matched.push( n );
      -		}
      -	}
      -
      -	return matched;
      -};
      -
      -
      -var rneedsContext = jQuery.expr.match.needsContext;
      -
      -
      -
      -function nodeName( elem, name ) {
      -
      -	return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
      -
      -}
      -var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
      -
      -
      -
      -// Implement the identical functionality for filter and not
      -function winnow( elements, qualifier, not ) {
      -	if ( isFunction( qualifier ) ) {
      -		return jQuery.grep( elements, function( elem, i ) {
      -			return !!qualifier.call( elem, i, elem ) !== not;
      -		} );
      -	}
      -
      -	// Single element
      -	if ( qualifier.nodeType ) {
      -		return jQuery.grep( elements, function( elem ) {
      -			return ( elem === qualifier ) !== not;
      -		} );
      -	}
      -
      -	// Arraylike of elements (jQuery, arguments, Array)
      -	if ( typeof qualifier !== "string" ) {
      -		return jQuery.grep( elements, function( elem ) {
      -			return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
      -		} );
      -	}
      -
      -	// Filtered directly for both simple and complex selectors
      -	return jQuery.filter( qualifier, elements, not );
      -}
      -
      -jQuery.filter = function( expr, elems, not ) {
      -	var elem = elems[ 0 ];
      -
      -	if ( not ) {
      -		expr = ":not(" + expr + ")";
      -	}
      -
      -	if ( elems.length === 1 && elem.nodeType === 1 ) {
      -		return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
      -	}
      -
      -	return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
      -		return elem.nodeType === 1;
      -	} ) );
      -};
      -
      -jQuery.fn.extend( {
      -	find: function( selector ) {
      -		var i, ret,
      -			len = this.length,
      -			self = this;
      -
      -		if ( typeof selector !== "string" ) {
      -			return this.pushStack( jQuery( selector ).filter( function() {
      -				for ( i = 0; i < len; i++ ) {
      -					if ( jQuery.contains( self[ i ], this ) ) {
      -						return true;
      -					}
      -				}
      -			} ) );
      -		}
      -
      -		ret = this.pushStack( [] );
      -
      -		for ( i = 0; i < len; i++ ) {
      -			jQuery.find( selector, self[ i ], ret );
      -		}
      -
      -		return len > 1 ? jQuery.uniqueSort( ret ) : ret;
      -	},
      -	filter: function( selector ) {
      -		return this.pushStack( winnow( this, selector || [], false ) );
      -	},
      -	not: function( selector ) {
      -		return this.pushStack( winnow( this, selector || [], true ) );
      -	},
      -	is: function( selector ) {
      -		return !!winnow(
      -			this,
      -
      -			// If this is a positional/relative selector, check membership in the returned set
      -			// so $("p:first").is("p:last") won't return true for a doc with two "p".
      -			typeof selector === "string" && rneedsContext.test( selector ) ?
      -				jQuery( selector ) :
      -				selector || [],
      -			false
      -		).length;
      -	}
      -} );
      -
      -
      -// Initialize a jQuery object
      -
      -
      -// A central reference to the root jQuery(document)
      -var rootjQuery,
      -
      -	// A simple way to check for HTML strings
      -	// Prioritize #id over  to avoid XSS via location.hash (trac-9521)
      -	// Strict HTML recognition (trac-11290: must start with <)
      -	// Shortcut simple #id case for speed
      -	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
      -
      -	init = jQuery.fn.init = function( selector, context, root ) {
      -		var match, elem;
      -
      -		// HANDLE: $(""), $(null), $(undefined), $(false)
      -		if ( !selector ) {
      -			return this;
      -		}
      -
      -		// Method init() accepts an alternate rootjQuery
      -		// so migrate can support jQuery.sub (gh-2101)
      -		root = root || rootjQuery;
      -
      -		// Handle HTML strings
      -		if ( typeof selector === "string" ) {
      -			if ( selector[ 0 ] === "<" &&
      -				selector[ selector.length - 1 ] === ">" &&
      -				selector.length >= 3 ) {
      -
      -				// Assume that strings that start and end with <> are HTML and skip the regex check
      -				match = [ null, selector, null ];
      -
      -			} else {
      -				match = rquickExpr.exec( selector );
      -			}
      -
      -			// Match html or make sure no context is specified for #id
      -			if ( match && ( match[ 1 ] || !context ) ) {
      -
      -				// HANDLE: $(html) -> $(array)
      -				if ( match[ 1 ] ) {
      -					context = context instanceof jQuery ? context[ 0 ] : context;
      -
      -					// Option to run scripts is true for back-compat
      -					// Intentionally let the error be thrown if parseHTML is not present
      -					jQuery.merge( this, jQuery.parseHTML(
      -						match[ 1 ],
      -						context && context.nodeType ? context.ownerDocument || context : document,
      -						true
      -					) );
      -
      -					// HANDLE: $(html, props)
      -					if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
      -						for ( match in context ) {
      -
      -							// Properties of context are called as methods if possible
      -							if ( isFunction( this[ match ] ) ) {
      -								this[ match ]( context[ match ] );
      -
      -							// ...and otherwise set as attributes
      -							} else {
      -								this.attr( match, context[ match ] );
      -							}
      -						}
      -					}
      -
      -					return this;
      -
      -				// HANDLE: $(#id)
      -				} else {
      -					elem = document.getElementById( match[ 2 ] );
      -
      -					if ( elem ) {
      -
      -						// Inject the element directly into the jQuery object
      -						this[ 0 ] = elem;
      -						this.length = 1;
      -					}
      -					return this;
      -				}
      -
      -			// HANDLE: $(expr, $(...))
      -			} else if ( !context || context.jquery ) {
      -				return ( context || root ).find( selector );
      -
      -			// HANDLE: $(expr, context)
      -			// (which is just equivalent to: $(context).find(expr)
      -			} else {
      -				return this.constructor( context ).find( selector );
      -			}
      -
      -		// HANDLE: $(DOMElement)
      -		} else if ( selector.nodeType ) {
      -			this[ 0 ] = selector;
      -			this.length = 1;
      -			return this;
      -
      -		// HANDLE: $(function)
      -		// Shortcut for document ready
      -		} else if ( isFunction( selector ) ) {
      -			return root.ready !== undefined ?
      -				root.ready( selector ) :
      -
      -				// Execute immediately if ready is not present
      -				selector( jQuery );
      -		}
      -
      -		return jQuery.makeArray( selector, this );
      -	};
      -
      -// Give the init function the jQuery prototype for later instantiation
      -init.prototype = jQuery.fn;
      -
      -// Initialize central reference
      -rootjQuery = jQuery( document );
      -
      -
      -var rparentsprev = /^(?:parents|prev(?:Until|All))/,
      -
      -	// Methods guaranteed to produce a unique set when starting from a unique set
      -	guaranteedUnique = {
      -		children: true,
      -		contents: true,
      -		next: true,
      -		prev: true
      -	};
      -
      -jQuery.fn.extend( {
      -	has: function( target ) {
      -		var targets = jQuery( target, this ),
      -			l = targets.length;
      -
      -		return this.filter( function() {
      -			var i = 0;
      -			for ( ; i < l; i++ ) {
      -				if ( jQuery.contains( this, targets[ i ] ) ) {
      -					return true;
      -				}
      -			}
      -		} );
      -	},
      -
      -	closest: function( selectors, context ) {
      -		var cur,
      -			i = 0,
      -			l = this.length,
      -			matched = [],
      -			targets = typeof selectors !== "string" && jQuery( selectors );
      -
      -		// Positional selectors never match, since there's no _selection_ context
      -		if ( !rneedsContext.test( selectors ) ) {
      -			for ( ; i < l; i++ ) {
      -				for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
      -
      -					// Always skip document fragments
      -					if ( cur.nodeType < 11 && ( targets ?
      -						targets.index( cur ) > -1 :
      -
      -						// Don't pass non-elements to Sizzle
      -						cur.nodeType === 1 &&
      -							jQuery.find.matchesSelector( cur, selectors ) ) ) {
      -
      -						matched.push( cur );
      -						break;
      -					}
      -				}
      -			}
      -		}
      -
      -		return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
      -	},
      -
      -	// Determine the position of an element within the set
      -	index: function( elem ) {
      -
      -		// No argument, return index in parent
      -		if ( !elem ) {
      -			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
      -		}
      -
      -		// Index in selector
      -		if ( typeof elem === "string" ) {
      -			return indexOf.call( jQuery( elem ), this[ 0 ] );
      -		}
      -
      -		// Locate the position of the desired element
      -		return indexOf.call( this,
      -
      -			// If it receives a jQuery object, the first element is used
      -			elem.jquery ? elem[ 0 ] : elem
      -		);
      -	},
      -
      -	add: function( selector, context ) {
      -		return this.pushStack(
      -			jQuery.uniqueSort(
      -				jQuery.merge( this.get(), jQuery( selector, context ) )
      -			)
      -		);
      -	},
      -
      -	addBack: function( selector ) {
      -		return this.add( selector == null ?
      -			this.prevObject : this.prevObject.filter( selector )
      -		);
      -	}
      -} );
      -
      -function sibling( cur, dir ) {
      -	while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
      -	return cur;
      -}
      -
      -jQuery.each( {
      -	parent: function( elem ) {
      -		var parent = elem.parentNode;
      -		return parent && parent.nodeType !== 11 ? parent : null;
      -	},
      -	parents: function( elem ) {
      -		return dir( elem, "parentNode" );
      -	},
      -	parentsUntil: function( elem, _i, until ) {
      -		return dir( elem, "parentNode", until );
      -	},
      -	next: function( elem ) {
      -		return sibling( elem, "nextSibling" );
      -	},
      -	prev: function( elem ) {
      -		return sibling( elem, "previousSibling" );
      -	},
      -	nextAll: function( elem ) {
      -		return dir( elem, "nextSibling" );
      -	},
      -	prevAll: function( elem ) {
      -		return dir( elem, "previousSibling" );
      -	},
      -	nextUntil: function( elem, _i, until ) {
      -		return dir( elem, "nextSibling", until );
      -	},
      -	prevUntil: function( elem, _i, until ) {
      -		return dir( elem, "previousSibling", until );
      -	},
      -	siblings: function( elem ) {
      -		return siblings( ( elem.parentNode || {} ).firstChild, elem );
      -	},
      -	children: function( elem ) {
      -		return siblings( elem.firstChild );
      -	},
      -	contents: function( elem ) {
      -		if ( elem.contentDocument != null &&
      -
      -			// Support: IE 11+
      -			//  elements with no `data` attribute has an object
      -			// `contentDocument` with a `null` prototype.
      -			getProto( elem.contentDocument ) ) {
      -
      -			return elem.contentDocument;
      -		}
      -
      -		// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
      -		// Treat the template element as a regular one in browsers that
      -		// don't support it.
      -		if ( nodeName( elem, "template" ) ) {
      -			elem = elem.content || elem;
      -		}
      -
      -		return jQuery.merge( [], elem.childNodes );
      -	}
      -}, function( name, fn ) {
      -	jQuery.fn[ name ] = function( until, selector ) {
      -		var matched = jQuery.map( this, fn, until );
      -
      -		if ( name.slice( -5 ) !== "Until" ) {
      -			selector = until;
      -		}
      -
      -		if ( selector && typeof selector === "string" ) {
      -			matched = jQuery.filter( selector, matched );
      -		}
      -
      -		if ( this.length > 1 ) {
      -
      -			// Remove duplicates
      -			if ( !guaranteedUnique[ name ] ) {
      -				jQuery.uniqueSort( matched );
      -			}
      -
      -			// Reverse order for parents* and prev-derivatives
      -			if ( rparentsprev.test( name ) ) {
      -				matched.reverse();
      -			}
      -		}
      -
      -		return this.pushStack( matched );
      -	};
      -} );
      -var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
      -
      -
      -
      -// Convert String-formatted options into Object-formatted ones
      -function createOptions( options ) {
      -	var object = {};
      -	jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
      -		object[ flag ] = true;
      -	} );
      -	return object;
      -}
      -
      -/*
      - * Create a callback list using the following parameters:
      - *
      - *	options: an optional list of space-separated options that will change how
      - *			the callback list behaves or a more traditional option object
      - *
      - * By default a callback list will act like an event callback list and can be
      - * "fired" multiple times.
      - *
      - * Possible options:
      - *
      - *	once:			will ensure the callback list can only be fired once (like a Deferred)
      - *
      - *	memory:			will keep track of previous values and will call any callback added
      - *					after the list has been fired right away with the latest "memorized"
      - *					values (like a Deferred)
      - *
      - *	unique:			will ensure a callback can only be added once (no duplicate in the list)
      - *
      - *	stopOnFalse:	interrupt callings when a callback returns false
      - *
      - */
      -jQuery.Callbacks = function( options ) {
      -
      -	// Convert options from String-formatted to Object-formatted if needed
      -	// (we check in cache first)
      -	options = typeof options === "string" ?
      -		createOptions( options ) :
      -		jQuery.extend( {}, options );
      -
      -	var // Flag to know if list is currently firing
      -		firing,
      -
      -		// Last fire value for non-forgettable lists
      -		memory,
      -
      -		// Flag to know if list was already fired
      -		fired,
      -
      -		// Flag to prevent firing
      -		locked,
      -
      -		// Actual callback list
      -		list = [],
      -
      -		// Queue of execution data for repeatable lists
      -		queue = [],
      -
      -		// Index of currently firing callback (modified by add/remove as needed)
      -		firingIndex = -1,
      -
      -		// Fire callbacks
      -		fire = function() {
      -
      -			// Enforce single-firing
      -			locked = locked || options.once;
      -
      -			// Execute callbacks for all pending executions,
      -			// respecting firingIndex overrides and runtime changes
      -			fired = firing = true;
      -			for ( ; queue.length; firingIndex = -1 ) {
      -				memory = queue.shift();
      -				while ( ++firingIndex < list.length ) {
      -
      -					// Run callback and check for early termination
      -					if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
      -						options.stopOnFalse ) {
      -
      -						// Jump to end and forget the data so .add doesn't re-fire
      -						firingIndex = list.length;
      -						memory = false;
      -					}
      -				}
      -			}
      -
      -			// Forget the data if we're done with it
      -			if ( !options.memory ) {
      -				memory = false;
      -			}
      -
      -			firing = false;
      -
      -			// Clean up if we're done firing for good
      -			if ( locked ) {
      -
      -				// Keep an empty list if we have data for future add calls
      -				if ( memory ) {
      -					list = [];
      -
      -				// Otherwise, this object is spent
      -				} else {
      -					list = "";
      -				}
      -			}
      -		},
      -
      -		// Actual Callbacks object
      -		self = {
      -
      -			// Add a callback or a collection of callbacks to the list
      -			add: function() {
      -				if ( list ) {
      -
      -					// If we have memory from a past run, we should fire after adding
      -					if ( memory && !firing ) {
      -						firingIndex = list.length - 1;
      -						queue.push( memory );
      -					}
      -
      -					( function add( args ) {
      -						jQuery.each( args, function( _, arg ) {
      -							if ( isFunction( arg ) ) {
      -								if ( !options.unique || !self.has( arg ) ) {
      -									list.push( arg );
      -								}
      -							} else if ( arg && arg.length && toType( arg ) !== "string" ) {
      -
      -								// Inspect recursively
      -								add( arg );
      -							}
      -						} );
      -					} )( arguments );
      -
      -					if ( memory && !firing ) {
      -						fire();
      -					}
      -				}
      -				return this;
      -			},
      -
      -			// Remove a callback from the list
      -			remove: function() {
      -				jQuery.each( arguments, function( _, arg ) {
      -					var index;
      -					while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
      -						list.splice( index, 1 );
      -
      -						// Handle firing indexes
      -						if ( index <= firingIndex ) {
      -							firingIndex--;
      -						}
      -					}
      -				} );
      -				return this;
      -			},
      -
      -			// Check if a given callback is in the list.
      -			// If no argument is given, return whether or not list has callbacks attached.
      -			has: function( fn ) {
      -				return fn ?
      -					jQuery.inArray( fn, list ) > -1 :
      -					list.length > 0;
      -			},
      -
      -			// Remove all callbacks from the list
      -			empty: function() {
      -				if ( list ) {
      -					list = [];
      -				}
      -				return this;
      -			},
      -
      -			// Disable .fire and .add
      -			// Abort any current/pending executions
      -			// Clear all callbacks and values
      -			disable: function() {
      -				locked = queue = [];
      -				list = memory = "";
      -				return this;
      -			},
      -			disabled: function() {
      -				return !list;
      -			},
      -
      -			// Disable .fire
      -			// Also disable .add unless we have memory (since it would have no effect)
      -			// Abort any pending executions
      -			lock: function() {
      -				locked = queue = [];
      -				if ( !memory && !firing ) {
      -					list = memory = "";
      -				}
      -				return this;
      -			},
      -			locked: function() {
      -				return !!locked;
      -			},
      -
      -			// Call all callbacks with the given context and arguments
      -			fireWith: function( context, args ) {
      -				if ( !locked ) {
      -					args = args || [];
      -					args = [ context, args.slice ? args.slice() : args ];
      -					queue.push( args );
      -					if ( !firing ) {
      -						fire();
      -					}
      -				}
      -				return this;
      -			},
      -
      -			// Call all the callbacks with the given arguments
      -			fire: function() {
      -				self.fireWith( this, arguments );
      -				return this;
      -			},
      -
      -			// To know if the callbacks have already been called at least once
      -			fired: function() {
      -				return !!fired;
      -			}
      -		};
      -
      -	return self;
      -};
      -
      -
      -function Identity( v ) {
      -	return v;
      -}
      -function Thrower( ex ) {
      -	throw ex;
      -}
      -
      -function adoptValue( value, resolve, reject, noValue ) {
      -	var method;
      -
      -	try {
      -
      -		// Check for promise aspect first to privilege synchronous behavior
      -		if ( value && isFunction( ( method = value.promise ) ) ) {
      -			method.call( value ).done( resolve ).fail( reject );
      -
      -		// Other thenables
      -		} else if ( value && isFunction( ( method = value.then ) ) ) {
      -			method.call( value, resolve, reject );
      -
      -		// Other non-thenables
      -		} else {
      -
      -			// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
      -			// * false: [ value ].slice( 0 ) => resolve( value )
      -			// * true: [ value ].slice( 1 ) => resolve()
      -			resolve.apply( undefined, [ value ].slice( noValue ) );
      -		}
      -
      -	// For Promises/A+, convert exceptions into rejections
      -	// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
      -	// Deferred#then to conditionally suppress rejection.
      -	} catch ( value ) {
      -
      -		// Support: Android 4.0 only
      -		// Strict mode functions invoked without .call/.apply get global-object context
      -		reject.apply( undefined, [ value ] );
      -	}
      -}
      -
      -jQuery.extend( {
      -
      -	Deferred: function( func ) {
      -		var tuples = [
      -
      -				// action, add listener, callbacks,
      -				// ... .then handlers, argument index, [final state]
      -				[ "notify", "progress", jQuery.Callbacks( "memory" ),
      -					jQuery.Callbacks( "memory" ), 2 ],
      -				[ "resolve", "done", jQuery.Callbacks( "once memory" ),
      -					jQuery.Callbacks( "once memory" ), 0, "resolved" ],
      -				[ "reject", "fail", jQuery.Callbacks( "once memory" ),
      -					jQuery.Callbacks( "once memory" ), 1, "rejected" ]
      -			],
      -			state = "pending",
      -			promise = {
      -				state: function() {
      -					return state;
      -				},
      -				always: function() {
      -					deferred.done( arguments ).fail( arguments );
      -					return this;
      -				},
      -				"catch": function( fn ) {
      -					return promise.then( null, fn );
      -				},
      -
      -				// Keep pipe for back-compat
      -				pipe: function( /* fnDone, fnFail, fnProgress */ ) {
      -					var fns = arguments;
      -
      -					return jQuery.Deferred( function( newDefer ) {
      -						jQuery.each( tuples, function( _i, tuple ) {
      -
      -							// Map tuples (progress, done, fail) to arguments (done, fail, progress)
      -							var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
      -
      -							// deferred.progress(function() { bind to newDefer or newDefer.notify })
      -							// deferred.done(function() { bind to newDefer or newDefer.resolve })
      -							// deferred.fail(function() { bind to newDefer or newDefer.reject })
      -							deferred[ tuple[ 1 ] ]( function() {
      -								var returned = fn && fn.apply( this, arguments );
      -								if ( returned && isFunction( returned.promise ) ) {
      -									returned.promise()
      -										.progress( newDefer.notify )
      -										.done( newDefer.resolve )
      -										.fail( newDefer.reject );
      -								} else {
      -									newDefer[ tuple[ 0 ] + "With" ](
      -										this,
      -										fn ? [ returned ] : arguments
      -									);
      -								}
      -							} );
      -						} );
      -						fns = null;
      -					} ).promise();
      -				},
      -				then: function( onFulfilled, onRejected, onProgress ) {
      -					var maxDepth = 0;
      -					function resolve( depth, deferred, handler, special ) {
      -						return function() {
      -							var that = this,
      -								args = arguments,
      -								mightThrow = function() {
      -									var returned, then;
      -
      -									// Support: Promises/A+ section 2.3.3.3.3
      -									// https://promisesaplus.com/#point-59
      -									// Ignore double-resolution attempts
      -									if ( depth < maxDepth ) {
      -										return;
      -									}
      -
      -									returned = handler.apply( that, args );
      -
      -									// Support: Promises/A+ section 2.3.1
      -									// https://promisesaplus.com/#point-48
      -									if ( returned === deferred.promise() ) {
      -										throw new TypeError( "Thenable self-resolution" );
      -									}
      -
      -									// Support: Promises/A+ sections 2.3.3.1, 3.5
      -									// https://promisesaplus.com/#point-54
      -									// https://promisesaplus.com/#point-75
      -									// Retrieve `then` only once
      -									then = returned &&
      -
      -										// Support: Promises/A+ section 2.3.4
      -										// https://promisesaplus.com/#point-64
      -										// Only check objects and functions for thenability
      -										( typeof returned === "object" ||
      -											typeof returned === "function" ) &&
      -										returned.then;
      -
      -									// Handle a returned thenable
      -									if ( isFunction( then ) ) {
      -
      -										// Special processors (notify) just wait for resolution
      -										if ( special ) {
      -											then.call(
      -												returned,
      -												resolve( maxDepth, deferred, Identity, special ),
      -												resolve( maxDepth, deferred, Thrower, special )
      -											);
      -
      -										// Normal processors (resolve) also hook into progress
      -										} else {
      -
      -											// ...and disregard older resolution values
      -											maxDepth++;
      -
      -											then.call(
      -												returned,
      -												resolve( maxDepth, deferred, Identity, special ),
      -												resolve( maxDepth, deferred, Thrower, special ),
      -												resolve( maxDepth, deferred, Identity,
      -													deferred.notifyWith )
      -											);
      -										}
      -
      -									// Handle all other returned values
      -									} else {
      -
      -										// Only substitute handlers pass on context
      -										// and multiple values (non-spec behavior)
      -										if ( handler !== Identity ) {
      -											that = undefined;
      -											args = [ returned ];
      -										}
      -
      -										// Process the value(s)
      -										// Default process is resolve
      -										( special || deferred.resolveWith )( that, args );
      -									}
      -								},
      -
      -								// Only normal processors (resolve) catch and reject exceptions
      -								process = special ?
      -									mightThrow :
      -									function() {
      -										try {
      -											mightThrow();
      -										} catch ( e ) {
      -
      -											if ( jQuery.Deferred.exceptionHook ) {
      -												jQuery.Deferred.exceptionHook( e,
      -													process.stackTrace );
      -											}
      -
      -											// Support: Promises/A+ section 2.3.3.3.4.1
      -											// https://promisesaplus.com/#point-61
      -											// Ignore post-resolution exceptions
      -											if ( depth + 1 >= maxDepth ) {
      -
      -												// Only substitute handlers pass on context
      -												// and multiple values (non-spec behavior)
      -												if ( handler !== Thrower ) {
      -													that = undefined;
      -													args = [ e ];
      -												}
      -
      -												deferred.rejectWith( that, args );
      -											}
      -										}
      -									};
      -
      -							// Support: Promises/A+ section 2.3.3.3.1
      -							// https://promisesaplus.com/#point-57
      -							// Re-resolve promises immediately to dodge false rejection from
      -							// subsequent errors
      -							if ( depth ) {
      -								process();
      -							} else {
      -
      -								// Call an optional hook to record the stack, in case of exception
      -								// since it's otherwise lost when execution goes async
      -								if ( jQuery.Deferred.getStackHook ) {
      -									process.stackTrace = jQuery.Deferred.getStackHook();
      -								}
      -								window.setTimeout( process );
      -							}
      -						};
      -					}
      -
      -					return jQuery.Deferred( function( newDefer ) {
      -
      -						// progress_handlers.add( ... )
      -						tuples[ 0 ][ 3 ].add(
      -							resolve(
      -								0,
      -								newDefer,
      -								isFunction( onProgress ) ?
      -									onProgress :
      -									Identity,
      -								newDefer.notifyWith
      -							)
      -						);
      -
      -						// fulfilled_handlers.add( ... )
      -						tuples[ 1 ][ 3 ].add(
      -							resolve(
      -								0,
      -								newDefer,
      -								isFunction( onFulfilled ) ?
      -									onFulfilled :
      -									Identity
      -							)
      -						);
      -
      -						// rejected_handlers.add( ... )
      -						tuples[ 2 ][ 3 ].add(
      -							resolve(
      -								0,
      -								newDefer,
      -								isFunction( onRejected ) ?
      -									onRejected :
      -									Thrower
      -							)
      -						);
      -					} ).promise();
      -				},
      -
      -				// Get a promise for this deferred
      -				// If obj is provided, the promise aspect is added to the object
      -				promise: function( obj ) {
      -					return obj != null ? jQuery.extend( obj, promise ) : promise;
      -				}
      -			},
      -			deferred = {};
      -
      -		// Add list-specific methods
      -		jQuery.each( tuples, function( i, tuple ) {
      -			var list = tuple[ 2 ],
      -				stateString = tuple[ 5 ];
      -
      -			// promise.progress = list.add
      -			// promise.done = list.add
      -			// promise.fail = list.add
      -			promise[ tuple[ 1 ] ] = list.add;
      -
      -			// Handle state
      -			if ( stateString ) {
      -				list.add(
      -					function() {
      -
      -						// state = "resolved" (i.e., fulfilled)
      -						// state = "rejected"
      -						state = stateString;
      -					},
      -
      -					// rejected_callbacks.disable
      -					// fulfilled_callbacks.disable
      -					tuples[ 3 - i ][ 2 ].disable,
      -
      -					// rejected_handlers.disable
      -					// fulfilled_handlers.disable
      -					tuples[ 3 - i ][ 3 ].disable,
      -
      -					// progress_callbacks.lock
      -					tuples[ 0 ][ 2 ].lock,
      -
      -					// progress_handlers.lock
      -					tuples[ 0 ][ 3 ].lock
      -				);
      -			}
      -
      -			// progress_handlers.fire
      -			// fulfilled_handlers.fire
      -			// rejected_handlers.fire
      -			list.add( tuple[ 3 ].fire );
      -
      -			// deferred.notify = function() { deferred.notifyWith(...) }
      -			// deferred.resolve = function() { deferred.resolveWith(...) }
      -			// deferred.reject = function() { deferred.rejectWith(...) }
      -			deferred[ tuple[ 0 ] ] = function() {
      -				deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
      -				return this;
      -			};
      -
      -			// deferred.notifyWith = list.fireWith
      -			// deferred.resolveWith = list.fireWith
      -			// deferred.rejectWith = list.fireWith
      -			deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
      -		} );
      -
      -		// Make the deferred a promise
      -		promise.promise( deferred );
      -
      -		// Call given func if any
      -		if ( func ) {
      -			func.call( deferred, deferred );
      -		}
      -
      -		// All done!
      -		return deferred;
      -	},
      -
      -	// Deferred helper
      -	when: function( singleValue ) {
      -		var
      -
      -			// count of uncompleted subordinates
      -			remaining = arguments.length,
      -
      -			// count of unprocessed arguments
      -			i = remaining,
      -
      -			// subordinate fulfillment data
      -			resolveContexts = Array( i ),
      -			resolveValues = slice.call( arguments ),
      -
      -			// the primary Deferred
      -			primary = jQuery.Deferred(),
      -
      -			// subordinate callback factory
      -			updateFunc = function( i ) {
      -				return function( value ) {
      -					resolveContexts[ i ] = this;
      -					resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
      -					if ( !( --remaining ) ) {
      -						primary.resolveWith( resolveContexts, resolveValues );
      -					}
      -				};
      -			};
      -
      -		// Single- and empty arguments are adopted like Promise.resolve
      -		if ( remaining <= 1 ) {
      -			adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject,
      -				!remaining );
      -
      -			// Use .then() to unwrap secondary thenables (cf. gh-3000)
      -			if ( primary.state() === "pending" ||
      -				isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
      -
      -				return primary.then();
      -			}
      -		}
      -
      -		// Multiple arguments are aggregated like Promise.all array elements
      -		while ( i-- ) {
      -			adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject );
      -		}
      -
      -		return primary.promise();
      -	}
      -} );
      -
      -
      -// These usually indicate a programmer mistake during development,
      -// warn about them ASAP rather than swallowing them by default.
      -var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
      -
      -jQuery.Deferred.exceptionHook = function( error, stack ) {
      -
      -	// Support: IE 8 - 9 only
      -	// Console exists when dev tools are open, which can happen at any time
      -	if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
      -		window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack );
      -	}
      -};
      -
      -
      -
      -
      -jQuery.readyException = function( error ) {
      -	window.setTimeout( function() {
      -		throw error;
      -	} );
      -};
      -
      -
      -
      -
      -// The deferred used on DOM ready
      -var readyList = jQuery.Deferred();
      -
      -jQuery.fn.ready = function( fn ) {
      -
      -	readyList
      -		.then( fn )
      -
      -		// Wrap jQuery.readyException in a function so that the lookup
      -		// happens at the time of error handling instead of callback
      -		// registration.
      -		.catch( function( error ) {
      -			jQuery.readyException( error );
      -		} );
      -
      -	return this;
      -};
      -
      -jQuery.extend( {
      -
      -	// Is the DOM ready to be used? Set to true once it occurs.
      -	isReady: false,
      -
      -	// A counter to track how many items to wait for before
      -	// the ready event fires. See trac-6781
      -	readyWait: 1,
      -
      -	// Handle when the DOM is ready
      -	ready: function( wait ) {
      -
      -		// Abort if there are pending holds or we're already ready
      -		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
      -			return;
      -		}
      -
      -		// Remember that the DOM is ready
      -		jQuery.isReady = true;
      -
      -		// If a normal DOM Ready event fired, decrement, and wait if need be
      -		if ( wait !== true && --jQuery.readyWait > 0 ) {
      -			return;
      -		}
      -
      -		// If there are functions bound, to execute
      -		readyList.resolveWith( document, [ jQuery ] );
      -	}
      -} );
      -
      -jQuery.ready.then = readyList.then;
      -
      -// The ready event handler and self cleanup method
      -function completed() {
      -	document.removeEventListener( "DOMContentLoaded", completed );
      -	window.removeEventListener( "load", completed );
      -	jQuery.ready();
      -}
      -
      -// Catch cases where $(document).ready() is called
      -// after the browser event has already occurred.
      -// Support: IE <=9 - 10 only
      -// Older IE sometimes signals "interactive" too soon
      -if ( document.readyState === "complete" ||
      -	( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
      -
      -	// Handle it asynchronously to allow scripts the opportunity to delay ready
      -	window.setTimeout( jQuery.ready );
      -
      -} else {
      -
      -	// Use the handy event callback
      -	document.addEventListener( "DOMContentLoaded", completed );
      -
      -	// A fallback to window.onload, that will always work
      -	window.addEventListener( "load", completed );
      -}
      -
      -
      -
      -
      -// Multifunctional method to get and set values of a collection
      -// The value/s can optionally be executed if it's a function
      -var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
      -	var i = 0,
      -		len = elems.length,
      -		bulk = key == null;
      -
      -	// Sets many values
      -	if ( toType( key ) === "object" ) {
      -		chainable = true;
      -		for ( i in key ) {
      -			access( elems, fn, i, key[ i ], true, emptyGet, raw );
      -		}
      -
      -	// Sets one value
      -	} else if ( value !== undefined ) {
      -		chainable = true;
      -
      -		if ( !isFunction( value ) ) {
      -			raw = true;
      -		}
      -
      -		if ( bulk ) {
      -
      -			// Bulk operations run against the entire set
      -			if ( raw ) {
      -				fn.call( elems, value );
      -				fn = null;
      -
      -			// ...except when executing function values
      -			} else {
      -				bulk = fn;
      -				fn = function( elem, _key, value ) {
      -					return bulk.call( jQuery( elem ), value );
      -				};
      -			}
      -		}
      -
      -		if ( fn ) {
      -			for ( ; i < len; i++ ) {
      -				fn(
      -					elems[ i ], key, raw ?
      -						value :
      -						value.call( elems[ i ], i, fn( elems[ i ], key ) )
      -				);
      -			}
      -		}
      -	}
      -
      -	if ( chainable ) {
      -		return elems;
      -	}
      -
      -	// Gets
      -	if ( bulk ) {
      -		return fn.call( elems );
      -	}
      -
      -	return len ? fn( elems[ 0 ], key ) : emptyGet;
      -};
      -
      -
      -// Matches dashed string for camelizing
      -var rmsPrefix = /^-ms-/,
      -	rdashAlpha = /-([a-z])/g;
      -
      -// Used by camelCase as callback to replace()
      -function fcamelCase( _all, letter ) {
      -	return letter.toUpperCase();
      -}
      -
      -// Convert dashed to camelCase; used by the css and data modules
      -// Support: IE <=9 - 11, Edge 12 - 15
      -// Microsoft forgot to hump their vendor prefix (trac-9572)
      -function camelCase( string ) {
      -	return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
      -}
      -var acceptData = function( owner ) {
      -
      -	// Accepts only:
      -	//  - Node
      -	//    - Node.ELEMENT_NODE
      -	//    - Node.DOCUMENT_NODE
      -	//  - Object
      -	//    - Any
      -	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
      -};
      -
      -
      -
      -
      -function Data() {
      -	this.expando = jQuery.expando + Data.uid++;
      -}
      -
      -Data.uid = 1;
      -
      -Data.prototype = {
      -
      -	cache: function( owner ) {
      -
      -		// Check if the owner object already has a cache
      -		var value = owner[ this.expando ];
      -
      -		// If not, create one
      -		if ( !value ) {
      -			value = {};
      -
      -			// We can accept data for non-element nodes in modern browsers,
      -			// but we should not, see trac-8335.
      -			// Always return an empty object.
      -			if ( acceptData( owner ) ) {
      -
      -				// If it is a node unlikely to be stringify-ed or looped over
      -				// use plain assignment
      -				if ( owner.nodeType ) {
      -					owner[ this.expando ] = value;
      -
      -				// Otherwise secure it in a non-enumerable property
      -				// configurable must be true to allow the property to be
      -				// deleted when data is removed
      -				} else {
      -					Object.defineProperty( owner, this.expando, {
      -						value: value,
      -						configurable: true
      -					} );
      -				}
      -			}
      -		}
      -
      -		return value;
      -	},
      -	set: function( owner, data, value ) {
      -		var prop,
      -			cache = this.cache( owner );
      -
      -		// Handle: [ owner, key, value ] args
      -		// Always use camelCase key (gh-2257)
      -		if ( typeof data === "string" ) {
      -			cache[ camelCase( data ) ] = value;
      -
      -		// Handle: [ owner, { properties } ] args
      -		} else {
      -
      -			// Copy the properties one-by-one to the cache object
      -			for ( prop in data ) {
      -				cache[ camelCase( prop ) ] = data[ prop ];
      -			}
      -		}
      -		return cache;
      -	},
      -	get: function( owner, key ) {
      -		return key === undefined ?
      -			this.cache( owner ) :
      -
      -			// Always use camelCase key (gh-2257)
      -			owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
      -	},
      -	access: function( owner, key, value ) {
      -
      -		// In cases where either:
      -		//
      -		//   1. No key was specified
      -		//   2. A string key was specified, but no value provided
      -		//
      -		// Take the "read" path and allow the get method to determine
      -		// which value to return, respectively either:
      -		//
      -		//   1. The entire cache object
      -		//   2. The data stored at the key
      -		//
      -		if ( key === undefined ||
      -				( ( key && typeof key === "string" ) && value === undefined ) ) {
      -
      -			return this.get( owner, key );
      -		}
      -
      -		// When the key is not a string, or both a key and value
      -		// are specified, set or extend (existing objects) with either:
      -		//
      -		//   1. An object of properties
      -		//   2. A key and value
      -		//
      -		this.set( owner, key, value );
      -
      -		// Since the "set" path can have two possible entry points
      -		// return the expected data based on which path was taken[*]
      -		return value !== undefined ? value : key;
      -	},
      -	remove: function( owner, key ) {
      -		var i,
      -			cache = owner[ this.expando ];
      -
      -		if ( cache === undefined ) {
      -			return;
      -		}
      -
      -		if ( key !== undefined ) {
      -
      -			// Support array or space separated string of keys
      -			if ( Array.isArray( key ) ) {
      -
      -				// If key is an array of keys...
      -				// We always set camelCase keys, so remove that.
      -				key = key.map( camelCase );
      -			} else {
      -				key = camelCase( key );
      -
      -				// If a key with the spaces exists, use it.
      -				// Otherwise, create an array by matching non-whitespace
      -				key = key in cache ?
      -					[ key ] :
      -					( key.match( rnothtmlwhite ) || [] );
      -			}
      -
      -			i = key.length;
      -
      -			while ( i-- ) {
      -				delete cache[ key[ i ] ];
      -			}
      -		}
      -
      -		// Remove the expando if there's no more data
      -		if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
      -
      -			// Support: Chrome <=35 - 45
      -			// Webkit & Blink performance suffers when deleting properties
      -			// from DOM nodes, so set to undefined instead
      -			// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
      -			if ( owner.nodeType ) {
      -				owner[ this.expando ] = undefined;
      -			} else {
      -				delete owner[ this.expando ];
      -			}
      -		}
      -	},
      -	hasData: function( owner ) {
      -		var cache = owner[ this.expando ];
      -		return cache !== undefined && !jQuery.isEmptyObject( cache );
      -	}
      -};
      -var dataPriv = new Data();
      -
      -var dataUser = new Data();
      -
      -
      -
      -//	Implementation Summary
      -//
      -//	1. Enforce API surface and semantic compatibility with 1.9.x branch
      -//	2. Improve the module's maintainability by reducing the storage
      -//		paths to a single mechanism.
      -//	3. Use the same single mechanism to support "private" and "user" data.
      -//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
      -//	5. Avoid exposing implementation details on user objects (eg. expando properties)
      -//	6. Provide a clear path for implementation upgrade to WeakMap in 2014
      -
      -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
      -	rmultiDash = /[A-Z]/g;
      -
      -function getData( data ) {
      -	if ( data === "true" ) {
      -		return true;
      -	}
      -
      -	if ( data === "false" ) {
      -		return false;
      -	}
      -
      -	if ( data === "null" ) {
      -		return null;
      -	}
      -
      -	// Only convert to a number if it doesn't change the string
      -	if ( data === +data + "" ) {
      -		return +data;
      -	}
      -
      -	if ( rbrace.test( data ) ) {
      -		return JSON.parse( data );
      -	}
      -
      -	return data;
      -}
      -
      -function dataAttr( elem, key, data ) {
      -	var name;
      -
      -	// If nothing was found internally, try to fetch any
      -	// data from the HTML5 data-* attribute
      -	if ( data === undefined && elem.nodeType === 1 ) {
      -		name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
      -		data = elem.getAttribute( name );
      -
      -		if ( typeof data === "string" ) {
      -			try {
      -				data = getData( data );
      -			} catch ( e ) {}
      -
      -			// Make sure we set the data so it isn't changed later
      -			dataUser.set( elem, key, data );
      -		} else {
      -			data = undefined;
      -		}
      -	}
      -	return data;
      -}
      -
      -jQuery.extend( {
      -	hasData: function( elem ) {
      -		return dataUser.hasData( elem ) || dataPriv.hasData( elem );
      -	},
      -
      -	data: function( elem, name, data ) {
      -		return dataUser.access( elem, name, data );
      -	},
      -
      -	removeData: function( elem, name ) {
      -		dataUser.remove( elem, name );
      -	},
      -
      -	// TODO: Now that all calls to _data and _removeData have been replaced
      -	// with direct calls to dataPriv methods, these can be deprecated.
      -	_data: function( elem, name, data ) {
      -		return dataPriv.access( elem, name, data );
      -	},
      -
      -	_removeData: function( elem, name ) {
      -		dataPriv.remove( elem, name );
      -	}
      -} );
      -
      -jQuery.fn.extend( {
      -	data: function( key, value ) {
      -		var i, name, data,
      -			elem = this[ 0 ],
      -			attrs = elem && elem.attributes;
      -
      -		// Gets all values
      -		if ( key === undefined ) {
      -			if ( this.length ) {
      -				data = dataUser.get( elem );
      -
      -				if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
      -					i = attrs.length;
      -					while ( i-- ) {
      -
      -						// Support: IE 11 only
      -						// The attrs elements can be null (trac-14894)
      -						if ( attrs[ i ] ) {
      -							name = attrs[ i ].name;
      -							if ( name.indexOf( "data-" ) === 0 ) {
      -								name = camelCase( name.slice( 5 ) );
      -								dataAttr( elem, name, data[ name ] );
      -							}
      -						}
      -					}
      -					dataPriv.set( elem, "hasDataAttrs", true );
      -				}
      -			}
      -
      -			return data;
      -		}
      -
      -		// Sets multiple values
      -		if ( typeof key === "object" ) {
      -			return this.each( function() {
      -				dataUser.set( this, key );
      -			} );
      -		}
      -
      -		return access( this, function( value ) {
      -			var data;
      -
      -			// The calling jQuery object (element matches) is not empty
      -			// (and therefore has an element appears at this[ 0 ]) and the
      -			// `value` parameter was not undefined. An empty jQuery object
      -			// will result in `undefined` for elem = this[ 0 ] which will
      -			// throw an exception if an attempt to read a data cache is made.
      -			if ( elem && value === undefined ) {
      -
      -				// Attempt to get data from the cache
      -				// The key will always be camelCased in Data
      -				data = dataUser.get( elem, key );
      -				if ( data !== undefined ) {
      -					return data;
      -				}
      -
      -				// Attempt to "discover" the data in
      -				// HTML5 custom data-* attrs
      -				data = dataAttr( elem, key );
      -				if ( data !== undefined ) {
      -					return data;
      -				}
      -
      -				// We tried really hard, but the data doesn't exist.
      -				return;
      -			}
      -
      -			// Set the data...
      -			this.each( function() {
      -
      -				// We always store the camelCased key
      -				dataUser.set( this, key, value );
      -			} );
      -		}, null, value, arguments.length > 1, null, true );
      -	},
      -
      -	removeData: function( key ) {
      -		return this.each( function() {
      -			dataUser.remove( this, key );
      -		} );
      -	}
      -} );
      -
      -
      -jQuery.extend( {
      -	queue: function( elem, type, data ) {
      -		var queue;
      -
      -		if ( elem ) {
      -			type = ( type || "fx" ) + "queue";
      -			queue = dataPriv.get( elem, type );
      -
      -			// Speed up dequeue by getting out quickly if this is just a lookup
      -			if ( data ) {
      -				if ( !queue || Array.isArray( data ) ) {
      -					queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
      -				} else {
      -					queue.push( data );
      -				}
      -			}
      -			return queue || [];
      -		}
      -	},
      -
      -	dequeue: function( elem, type ) {
      -		type = type || "fx";
      -
      -		var queue = jQuery.queue( elem, type ),
      -			startLength = queue.length,
      -			fn = queue.shift(),
      -			hooks = jQuery._queueHooks( elem, type ),
      -			next = function() {
      -				jQuery.dequeue( elem, type );
      -			};
      -
      -		// If the fx queue is dequeued, always remove the progress sentinel
      -		if ( fn === "inprogress" ) {
      -			fn = queue.shift();
      -			startLength--;
      -		}
      -
      -		if ( fn ) {
      -
      -			// Add a progress sentinel to prevent the fx queue from being
      -			// automatically dequeued
      -			if ( type === "fx" ) {
      -				queue.unshift( "inprogress" );
      -			}
      -
      -			// Clear up the last queue stop function
      -			delete hooks.stop;
      -			fn.call( elem, next, hooks );
      -		}
      -
      -		if ( !startLength && hooks ) {
      -			hooks.empty.fire();
      -		}
      -	},
      -
      -	// Not public - generate a queueHooks object, or return the current one
      -	_queueHooks: function( elem, type ) {
      -		var key = type + "queueHooks";
      -		return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
      -			empty: jQuery.Callbacks( "once memory" ).add( function() {
      -				dataPriv.remove( elem, [ type + "queue", key ] );
      -			} )
      -		} );
      -	}
      -} );
      -
      -jQuery.fn.extend( {
      -	queue: function( type, data ) {
      -		var setter = 2;
      -
      -		if ( typeof type !== "string" ) {
      -			data = type;
      -			type = "fx";
      -			setter--;
      -		}
      -
      -		if ( arguments.length < setter ) {
      -			return jQuery.queue( this[ 0 ], type );
      -		}
      -
      -		return data === undefined ?
      -			this :
      -			this.each( function() {
      -				var queue = jQuery.queue( this, type, data );
      -
      -				// Ensure a hooks for this queue
      -				jQuery._queueHooks( this, type );
      -
      -				if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
      -					jQuery.dequeue( this, type );
      -				}
      -			} );
      -	},
      -	dequeue: function( type ) {
      -		return this.each( function() {
      -			jQuery.dequeue( this, type );
      -		} );
      -	},
      -	clearQueue: function( type ) {
      -		return this.queue( type || "fx", [] );
      -	},
      -
      -	// Get a promise resolved when queues of a certain type
      -	// are emptied (fx is the type by default)
      -	promise: function( type, obj ) {
      -		var tmp,
      -			count = 1,
      -			defer = jQuery.Deferred(),
      -			elements = this,
      -			i = this.length,
      -			resolve = function() {
      -				if ( !( --count ) ) {
      -					defer.resolveWith( elements, [ elements ] );
      -				}
      -			};
      -
      -		if ( typeof type !== "string" ) {
      -			obj = type;
      -			type = undefined;
      -		}
      -		type = type || "fx";
      -
      -		while ( i-- ) {
      -			tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
      -			if ( tmp && tmp.empty ) {
      -				count++;
      -				tmp.empty.add( resolve );
      -			}
      -		}
      -		resolve();
      -		return defer.promise( obj );
      -	}
      -} );
      -var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
      -
      -var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
      -
      -
      -var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
      -
      -var documentElement = document.documentElement;
      -
      -
      -
      -	var isAttached = function( elem ) {
      -			return jQuery.contains( elem.ownerDocument, elem );
      -		},
      -		composed = { composed: true };
      -
      -	// Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only
      -	// Check attachment across shadow DOM boundaries when possible (gh-3504)
      -	// Support: iOS 10.0-10.2 only
      -	// Early iOS 10 versions support `attachShadow` but not `getRootNode`,
      -	// leading to errors. We need to check for `getRootNode`.
      -	if ( documentElement.getRootNode ) {
      -		isAttached = function( elem ) {
      -			return jQuery.contains( elem.ownerDocument, elem ) ||
      -				elem.getRootNode( composed ) === elem.ownerDocument;
      -		};
      -	}
      -var isHiddenWithinTree = function( elem, el ) {
      -
      -		// isHiddenWithinTree might be called from jQuery#filter function;
      -		// in that case, element will be second argument
      -		elem = el || elem;
      -
      -		// Inline style trumps all
      -		return elem.style.display === "none" ||
      -			elem.style.display === "" &&
      -
      -			// Otherwise, check computed style
      -			// Support: Firefox <=43 - 45
      -			// Disconnected elements can have computed display: none, so first confirm that elem is
      -			// in the document.
      -			isAttached( elem ) &&
      -
      -			jQuery.css( elem, "display" ) === "none";
      -	};
      -
      -
      -
      -function adjustCSS( elem, prop, valueParts, tween ) {
      -	var adjusted, scale,
      -		maxIterations = 20,
      -		currentValue = tween ?
      -			function() {
      -				return tween.cur();
      -			} :
      -			function() {
      -				return jQuery.css( elem, prop, "" );
      -			},
      -		initial = currentValue(),
      -		unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
      -
      -		// Starting value computation is required for potential unit mismatches
      -		initialInUnit = elem.nodeType &&
      -			( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
      -			rcssNum.exec( jQuery.css( elem, prop ) );
      -
      -	if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
      -
      -		// Support: Firefox <=54
      -		// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
      -		initial = initial / 2;
      -
      -		// Trust units reported by jQuery.css
      -		unit = unit || initialInUnit[ 3 ];
      -
      -		// Iteratively approximate from a nonzero starting point
      -		initialInUnit = +initial || 1;
      -
      -		while ( maxIterations-- ) {
      -
      -			// Evaluate and update our best guess (doubling guesses that zero out).
      -			// Finish if the scale equals or crosses 1 (making the old*new product non-positive).
      -			jQuery.style( elem, prop, initialInUnit + unit );
      -			if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
      -				maxIterations = 0;
      -			}
      -			initialInUnit = initialInUnit / scale;
      -
      -		}
      -
      -		initialInUnit = initialInUnit * 2;
      -		jQuery.style( elem, prop, initialInUnit + unit );
      -
      -		// Make sure we update the tween properties later on
      -		valueParts = valueParts || [];
      -	}
      -
      -	if ( valueParts ) {
      -		initialInUnit = +initialInUnit || +initial || 0;
      -
      -		// Apply relative offset (+=/-=) if specified
      -		adjusted = valueParts[ 1 ] ?
      -			initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
      -			+valueParts[ 2 ];
      -		if ( tween ) {
      -			tween.unit = unit;
      -			tween.start = initialInUnit;
      -			tween.end = adjusted;
      -		}
      -	}
      -	return adjusted;
      -}
      -
      -
      -var defaultDisplayMap = {};
      -
      -function getDefaultDisplay( elem ) {
      -	var temp,
      -		doc = elem.ownerDocument,
      -		nodeName = elem.nodeName,
      -		display = defaultDisplayMap[ nodeName ];
      -
      -	if ( display ) {
      -		return display;
      -	}
      -
      -	temp = doc.body.appendChild( doc.createElement( nodeName ) );
      -	display = jQuery.css( temp, "display" );
      -
      -	temp.parentNode.removeChild( temp );
      -
      -	if ( display === "none" ) {
      -		display = "block";
      -	}
      -	defaultDisplayMap[ nodeName ] = display;
      -
      -	return display;
      -}
      -
      -function showHide( elements, show ) {
      -	var display, elem,
      -		values = [],
      -		index = 0,
      -		length = elements.length;
      -
      -	// Determine new display value for elements that need to change
      -	for ( ; index < length; index++ ) {
      -		elem = elements[ index ];
      -		if ( !elem.style ) {
      -			continue;
      -		}
      -
      -		display = elem.style.display;
      -		if ( show ) {
      -
      -			// Since we force visibility upon cascade-hidden elements, an immediate (and slow)
      -			// check is required in this first loop unless we have a nonempty display value (either
      -			// inline or about-to-be-restored)
      -			if ( display === "none" ) {
      -				values[ index ] = dataPriv.get( elem, "display" ) || null;
      -				if ( !values[ index ] ) {
      -					elem.style.display = "";
      -				}
      -			}
      -			if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
      -				values[ index ] = getDefaultDisplay( elem );
      -			}
      -		} else {
      -			if ( display !== "none" ) {
      -				values[ index ] = "none";
      -
      -				// Remember what we're overwriting
      -				dataPriv.set( elem, "display", display );
      -			}
      -		}
      -	}
      -
      -	// Set the display of the elements in a second loop to avoid constant reflow
      -	for ( index = 0; index < length; index++ ) {
      -		if ( values[ index ] != null ) {
      -			elements[ index ].style.display = values[ index ];
      -		}
      -	}
      -
      -	return elements;
      -}
      -
      -jQuery.fn.extend( {
      -	show: function() {
      -		return showHide( this, true );
      -	},
      -	hide: function() {
      -		return showHide( this );
      -	},
      -	toggle: function( state ) {
      -		if ( typeof state === "boolean" ) {
      -			return state ? this.show() : this.hide();
      -		}
      -
      -		return this.each( function() {
      -			if ( isHiddenWithinTree( this ) ) {
      -				jQuery( this ).show();
      -			} else {
      -				jQuery( this ).hide();
      -			}
      -		} );
      -	}
      -} );
      -var rcheckableType = ( /^(?:checkbox|radio)$/i );
      -
      -var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );
      -
      -var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
      -
      -
      -
      -( function() {
      -	var fragment = document.createDocumentFragment(),
      -		div = fragment.appendChild( document.createElement( "div" ) ),
      -		input = document.createElement( "input" );
      -
      -	// Support: Android 4.0 - 4.3 only
      -	// Check state lost if the name is set (trac-11217)
      -	// Support: Windows Web Apps (WWA)
      -	// `name` and `type` must use .setAttribute for WWA (trac-14901)
      -	input.setAttribute( "type", "radio" );
      -	input.setAttribute( "checked", "checked" );
      -	input.setAttribute( "name", "t" );
      -
      -	div.appendChild( input );
      -
      -	// Support: Android <=4.1 only
      -	// Older WebKit doesn't clone checked state correctly in fragments
      -	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
      -
      -	// Support: IE <=11 only
      -	// Make sure textarea (and checkbox) defaultValue is properly cloned
      -	div.innerHTML = "";
      -	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
      -
      -	// Support: IE <=9 only
      -	// IE <=9 replaces ";
      -	support.option = !!div.lastChild;
      -} )();
      -
      -
      -// We have to close these tags to support XHTML (trac-13200)
      -var wrapMap = {
      -
      -	// XHTML parsers do not magically insert elements in the
      -	// same way that tag soup parsers do. So we cannot shorten
      -	// this by omitting  or other required elements.
      -	thead: [ 1, "", "
      " ], - col: [ 2, "", "
      " ], - tr: [ 2, "", "
      " ], - td: [ 3, "", "
      " ], - - _default: [ 0, "", "" ] -}; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// Support: IE <=9 only -if ( !support.option ) { - wrapMap.optgroup = wrapMap.option = [ 1, "" ]; -} - - -function getAll( context, tag ) { - - // Support: IE <=9 - 11 only - // Use typeof to avoid zero-argument method invocation on host objects (trac-15151) - var ret; - - if ( typeof context.getElementsByTagName !== "undefined" ) { - ret = context.getElementsByTagName( tag || "*" ); - - } else if ( typeof context.querySelectorAll !== "undefined" ) { - ret = context.querySelectorAll( tag || "*" ); - - } else { - ret = []; - } - - if ( tag === undefined || tag && nodeName( context, tag ) ) { - return jQuery.merge( [ context ], ret ); - } - - return ret; -} - - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - dataPriv.set( - elems[ i ], - "globalEval", - !refElements || dataPriv.get( refElements[ i ], "globalEval" ) - ); - } -} - - -var rhtml = /<|&#?\w+;/; - -function buildFragment( elems, context, scripts, selection, ignored ) { - var elem, tmp, tag, wrap, attached, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( toType( elem ) === "object" ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Ensure the created nodes are orphaned (trac-12392) - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( ( elem = nodes[ i++ ] ) ) { - - // Skip elements already in the context collection (trac-4087) - if ( selection && jQuery.inArray( elem, selection ) > -1 ) { - if ( ignored ) { - ignored.push( elem ); - } - continue; - } - - attached = isAttached( elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( attached ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( ( elem = tmp[ j++ ] ) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; -} - - -var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -// Support: IE <=9 - 11+ -// focus() and blur() are asynchronous, except when they are no-op. -// So expect focus to be synchronous when the element is already active, -// and blur to be synchronous when the element is not already active. -// (focus and blur are always synchronous in other supported browsers, -// this just defines when we can count on it). -function expectSync( elem, type ) { - return ( elem === safeActiveElement() ) === ( type === "focus" ); -} - -// Support: IE <=9 only -// Accessing document.activeElement can throw unexpectedly -// https://bugs.jquery.com/ticket/13393 -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -function on( elem, types, selector, data, fn, one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - on( elem, type, selector, data, types[ type ], one ); - } - return elem; - } - - if ( data == null && fn == null ) { - - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return elem; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return elem.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - } ); -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.get( elem ); - - // Only attach events to objects that accept data - if ( !acceptData( elem ) ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Ensure that invalid selectors throw exceptions at attach time - // Evaluate against documentElement in case elem is a non-element node (e.g., document) - if ( selector ) { - jQuery.find.matchesSelector( documentElement, selector ); - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !( events = elemData.events ) ) { - events = elemData.events = Object.create( null ); - } - if ( !( eventHandle = elemData.handle ) ) { - eventHandle = elemData.handle = function( e ) { - - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend( { - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join( "." ) - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !( handlers = events[ type ] ) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || - special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); - - if ( !elemData || !( events = elemData.events ) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[ 2 ] && - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || - selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || - special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove data and the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - dataPriv.remove( elem, "handle events" ); - } - }, - - dispatch: function( nativeEvent ) { - - var i, j, ret, matched, handleObj, handlerQueue, - args = new Array( arguments.length ), - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( nativeEvent ), - - handlers = ( - dataPriv.get( this, "events" ) || Object.create( null ) - )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[ 0 ] = event; - - for ( i = 1; i < arguments.length; i++ ) { - args[ i ] = arguments[ i ]; - } - - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( ( handleObj = matched.handlers[ j++ ] ) && - !event.isImmediatePropagationStopped() ) { - - // If the event is namespaced, then each handler is only invoked if it is - // specially universal or its namespaces are a superset of the event's. - if ( !event.rnamespace || handleObj.namespace === false || - event.rnamespace.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || - handleObj.handler ).apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( ( event.result = ret ) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, handleObj, sel, matchedHandlers, matchedSelectors, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - if ( delegateCount && - - // Support: IE <=9 - // Black-hole SVG instance trees (trac-13180) - cur.nodeType && - - // Support: Firefox <=42 - // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) - // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click - // Support: IE 11 only - // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) - !( event.type === "click" && event.button >= 1 ) ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't check non-elements (trac-13208) - // Don't process clicks on disabled elements (trac-6911, trac-8165, trac-11382, trac-11764) - if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { - matchedHandlers = []; - matchedSelectors = {}; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (trac-13203) - sel = handleObj.selector + " "; - - if ( matchedSelectors[ sel ] === undefined ) { - matchedSelectors[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) > -1 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matchedSelectors[ sel ] ) { - matchedHandlers.push( handleObj ); - } - } - if ( matchedHandlers.length ) { - handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); - } - } - } - } - - // Add the remaining (directly-bound) handlers - cur = this; - if ( delegateCount < handlers.length ) { - handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); - } - - return handlerQueue; - }, - - addProp: function( name, hook ) { - Object.defineProperty( jQuery.Event.prototype, name, { - enumerable: true, - configurable: true, - - get: isFunction( hook ) ? - function() { - if ( this.originalEvent ) { - return hook( this.originalEvent ); - } - } : - function() { - if ( this.originalEvent ) { - return this.originalEvent[ name ]; - } - }, - - set: function( value ) { - Object.defineProperty( this, name, { - enumerable: true, - configurable: true, - writable: true, - value: value - } ); - } - } ); - }, - - fix: function( originalEvent ) { - return originalEvent[ jQuery.expando ] ? - originalEvent : - new jQuery.Event( originalEvent ); - }, - - special: { - load: { - - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - click: { - - // Utilize native event to ensure correct state for checkable inputs - setup: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Claim the first handler - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - // dataPriv.set( el, "click", ... ) - leverageNative( el, "click", returnTrue ); - } - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Force setup before triggering a click - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - leverageNative( el, "click" ); - } - - // Return non-false to allow normal event-path propagation - return true; - }, - - // For cross-browser consistency, suppress native .click() on links - // Also prevent it if we're currently inside a leveraged native-event stack - _default: function( event ) { - var target = event.target; - return rcheckableType.test( target.type ) && - target.click && nodeName( target, "input" ) && - dataPriv.get( target, "click" ) || - nodeName( target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - } -}; - -// Ensure the presence of an event listener that handles manually-triggered -// synthetic events by interrupting progress until reinvoked in response to -// *native* events that it fires directly, ensuring that state changes have -// already occurred before other listeners are invoked. -function leverageNative( el, type, expectSync ) { - - // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add - if ( !expectSync ) { - if ( dataPriv.get( el, type ) === undefined ) { - jQuery.event.add( el, type, returnTrue ); - } - return; - } - - // Register the controller as a special universal handler for all event namespaces - dataPriv.set( el, type, false ); - jQuery.event.add( el, type, { - namespace: false, - handler: function( event ) { - var notAsync, result, - saved = dataPriv.get( this, type ); - - if ( ( event.isTrigger & 1 ) && this[ type ] ) { - - // Interrupt processing of the outer synthetic .trigger()ed event - // Saved data should be false in such cases, but might be a leftover capture object - // from an async native handler (gh-4350) - if ( !saved.length ) { - - // Store arguments for use when handling the inner native event - // There will always be at least one argument (an event object), so this array - // will not be confused with a leftover capture object. - saved = slice.call( arguments ); - dataPriv.set( this, type, saved ); - - // Trigger the native event and capture its result - // Support: IE <=9 - 11+ - // focus() and blur() are asynchronous - notAsync = expectSync( this, type ); - this[ type ](); - result = dataPriv.get( this, type ); - if ( saved !== result || notAsync ) { - dataPriv.set( this, type, false ); - } else { - result = {}; - } - if ( saved !== result ) { - - // Cancel the outer synthetic event - event.stopImmediatePropagation(); - event.preventDefault(); - - // Support: Chrome 86+ - // In Chrome, if an element having a focusout handler is blurred by - // clicking outside of it, it invokes the handler synchronously. If - // that handler calls `.remove()` on the element, the data is cleared, - // leaving `result` undefined. We need to guard against this. - return result && result.value; - } - - // If this is an inner synthetic event for an event with a bubbling surrogate - // (focus or blur), assume that the surrogate already propagated from triggering the - // native event and prevent that from happening again here. - // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the - // bubbling surrogate propagates *after* the non-bubbling base), but that seems - // less bad than duplication. - } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { - event.stopPropagation(); - } - - // If this is a native event triggered above, everything is now in order - // Fire an inner synthetic event with the original arguments - } else if ( saved.length ) { - - // ...and capture the result - dataPriv.set( this, type, { - value: jQuery.event.trigger( - - // Support: IE <=9 - 11+ - // Extend with the prototype to reset the above stopImmediatePropagation() - jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), - saved.slice( 1 ), - this - ) - } ); - - // Abort handling of the native event - event.stopImmediatePropagation(); - } - } - } ); -} - -jQuery.removeEvent = function( elem, type, handle ) { - - // This "if" is needed for plain objects - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle ); - } -}; - -jQuery.Event = function( src, props ) { - - // Allow instantiation without the 'new' keyword - if ( !( this instanceof jQuery.Event ) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - - // Support: Android <=2.3 only - src.returnValue === false ? - returnTrue : - returnFalse; - - // Create target properties - // Support: Safari <=6 - 7 only - // Target should not be a text node (trac-504, trac-13143) - this.target = ( src.target && src.target.nodeType === 3 ) ? - src.target.parentNode : - src.target; - - this.currentTarget = src.currentTarget; - this.relatedTarget = src.relatedTarget; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || Date.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - constructor: jQuery.Event, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - isSimulated: false, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && !this.isSimulated ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Includes all common event props including KeyEvent and MouseEvent specific props -jQuery.each( { - altKey: true, - bubbles: true, - cancelable: true, - changedTouches: true, - ctrlKey: true, - detail: true, - eventPhase: true, - metaKey: true, - pageX: true, - pageY: true, - shiftKey: true, - view: true, - "char": true, - code: true, - charCode: true, - key: true, - keyCode: true, - button: true, - buttons: true, - clientX: true, - clientY: true, - offsetX: true, - offsetY: true, - pointerId: true, - pointerType: true, - screenX: true, - screenY: true, - targetTouches: true, - toElement: true, - touches: true, - which: true -}, jQuery.event.addProp ); - -jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { - jQuery.event.special[ type ] = { - - // Utilize native event if possible so blur/focus sequence is correct - setup: function() { - - // Claim the first handler - // dataPriv.set( this, "focus", ... ) - // dataPriv.set( this, "blur", ... ) - leverageNative( this, type, expectSync ); - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function() { - - // Force setup before trigger - leverageNative( this, type ); - - // Return non-false to allow normal event-path propagation - return true; - }, - - // Suppress native focus or blur if we're currently inside - // a leveraged native-event stack - _default: function( event ) { - return dataPriv.get( event.target, type ); - }, - - delegateType: delegateType - }; -} ); - -// Create mouseenter/leave events using mouseover/out and event-time checks -// so that event delegation works in jQuery. -// Do the same for pointerenter/pointerleave and pointerover/pointerout -// -// Support: Safari 7 only -// Safari sends mouseenter too often; see: -// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 -// for the description of the bug (it existed in older Chrome versions as well). -jQuery.each( { - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mouseenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -} ); - -jQuery.fn.extend( { - - on: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn ); - }, - one: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? - handleObj.origType + "." + handleObj.namespace : - handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each( function() { - jQuery.event.remove( this, types, fn, selector ); - } ); - } -} ); - - -var - - // Support: IE <=10 - 11, Edge 12 - 13 only - // In IE/Edge using regex groups here causes severe slowdowns. - // See https://connect.microsoft.com/IE/feedback/details/1736512/ - rnoInnerhtml = /\s*$/g; - -// Prefer a tbody over its parent table for containing new rows -function manipulationTarget( elem, content ) { - if ( nodeName( elem, "table" ) && - nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - - return jQuery( elem ).children( "tbody" )[ 0 ] || elem; - } - - return elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { - elem.type = elem.type.slice( 5 ); - } else { - elem.removeAttribute( "type" ); - } - - return elem; -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( dataPriv.hasData( src ) ) { - pdataOld = dataPriv.get( src ); - events = pdataOld.events; - - if ( events ) { - dataPriv.remove( dest, "handle events" ); - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( dataUser.hasData( src ) ) { - udataOld = dataUser.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - dataUser.set( dest, udataCur ); - } -} - -// Fix IE bugs, see support tests -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -function domManip( collection, args, callback, ignored ) { - - // Flatten any nested arrays - args = flat( args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = collection.length, - iNoClone = l - 1, - value = args[ 0 ], - valueIsFunction = isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( valueIsFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return collection.each( function( index ) { - var self = collection.eq( index ); - if ( valueIsFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - domManip( self, args, callback, ignored ); - } ); - } - - if ( l ) { - fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - // Require either new content or an interest in ignored elements to invoke the callback - if ( first || ignored ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item - // instead of the first because it can end up - // being emptied incorrectly in certain situations (trac-8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( collection[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !dataPriv.access( node, "globalEval" ) && - jQuery.contains( doc, node ) ) { - - if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { - - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl && !node.noModule ) { - jQuery._evalUrl( node.src, { - nonce: node.nonce || node.getAttribute( "nonce" ) - }, doc ); - } - } else { - - // Unwrap a CDATA section containing script contents. This shouldn't be - // needed as in XML documents they're already not visible when - // inspecting element contents and in HTML documents they have no - // meaning but we're preserving that logic for backwards compatibility. - // This will be removed completely in 4.0. See gh-4904. - DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); - } - } - } - } - } - } - - return collection; -} - -function remove( elem, selector, keepData ) { - var node, - nodes = selector ? jQuery.filter( selector, elem ) : elem, - i = 0; - - for ( ; ( node = nodes[ i ] ) != null; i++ ) { - if ( !keepData && node.nodeType === 1 ) { - jQuery.cleanData( getAll( node ) ); - } - - if ( node.parentNode ) { - if ( keepData && isAttached( node ) ) { - setGlobalEval( getAll( node, "script" ) ); - } - node.parentNode.removeChild( node ); - } - } - - return elem; -} - -jQuery.extend( { - htmlPrefilter: function( html ) { - return html; - }, - - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = isAttached( elem ); - - // Fix IE cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - cleanData: function( elems ) { - var data, elem, type, - special = jQuery.event.special, - i = 0; - - for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { - if ( acceptData( elem ) ) { - if ( ( data = elem[ dataPriv.expando ] ) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataPriv.expando ] = undefined; - } - if ( elem[ dataUser.expando ] ) { - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataUser.expando ] = undefined; - } - } - } - } -} ); - -jQuery.fn.extend( { - detach: function( selector ) { - return remove( this, selector, true ); - }, - - remove: function( selector ) { - return remove( this, selector ); - }, - - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each( function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - } ); - }, null, value, arguments.length ); - }, - - append: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - } ); - }, - - prepend: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - } ); - }, - - before: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - } ); - }, - - after: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - } ); - }, - - empty: function() { - var elem, - i = 0; - - for ( ; ( elem = this[ i ] ) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - } ); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = jQuery.htmlPrefilter( value ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch ( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var ignored = []; - - // Make the changes, replacing each non-ignored context element with the new content - return domManip( this, arguments, function( elem ) { - var parent = this.parentNode; - - if ( jQuery.inArray( this, ignored ) < 0 ) { - jQuery.cleanData( getAll( this ) ); - if ( parent ) { - parent.replaceChild( elem, this ); - } - } - - // Force callback invocation - }, ignored ); - } -} ); - -jQuery.each( { - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: Android <=4.0 only, PhantomJS 1 only - // .get() because push.apply(_, arraylike) throws on ancient WebKit - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -} ); -var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); - -var rcustomProp = /^--/; - - -var getStyles = function( elem ) { - - // Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150) - // IE throws on elements created in popups - // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" - var view = elem.ownerDocument.defaultView; - - if ( !view || !view.opener ) { - view = window; - } - - return view.getComputedStyle( elem ); - }; - -var swap = function( elem, options, callback ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.call( elem ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; -}; - - -var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); - -var whitespace = "[\\x20\\t\\r\\n\\f]"; - - -var rtrimCSS = new RegExp( - "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", - "g" -); - - - - -( function() { - - // Executing both pixelPosition & boxSizingReliable tests require only one layout - // so they're executed at the same time to save the second computation. - function computeStyleTests() { - - // This is a singleton, we need to execute it only once - if ( !div ) { - return; - } - - container.style.cssText = "position:absolute;left:-11111px;width:60px;" + - "margin-top:1px;padding:0;border:0"; - div.style.cssText = - "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + - "margin:auto;border:1px;padding:1px;" + - "width:60%;top:1%"; - documentElement.appendChild( container ).appendChild( div ); - - var divStyle = window.getComputedStyle( div ); - pixelPositionVal = divStyle.top !== "1%"; - - // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 - reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; - - // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 - // Some styles come back with percentage values, even though they shouldn't - div.style.right = "60%"; - pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; - - // Support: IE 9 - 11 only - // Detect misreporting of content dimensions for box-sizing:border-box elements - boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; - - // Support: IE 9 only - // Detect overflow:scroll screwiness (gh-3699) - // Support: Chrome <=64 - // Don't get tricked when zoom affects offsetWidth (gh-4029) - div.style.position = "absolute"; - scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; - - documentElement.removeChild( container ); - - // Nullify the div so it wouldn't be stored in the memory and - // it will also be a sign that checks already performed - div = null; - } - - function roundPixelMeasures( measure ) { - return Math.round( parseFloat( measure ) ); - } - - var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, - reliableTrDimensionsVal, reliableMarginLeftVal, - container = document.createElement( "div" ), - div = document.createElement( "div" ); - - // Finish early in limited (non-browser) environments - if ( !div.style ) { - return; - } - - // Support: IE <=9 - 11 only - // Style of cloned element affects source element cloned (trac-8908) - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - jQuery.extend( support, { - boxSizingReliable: function() { - computeStyleTests(); - return boxSizingReliableVal; - }, - pixelBoxStyles: function() { - computeStyleTests(); - return pixelBoxStylesVal; - }, - pixelPosition: function() { - computeStyleTests(); - return pixelPositionVal; - }, - reliableMarginLeft: function() { - computeStyleTests(); - return reliableMarginLeftVal; - }, - scrollboxSize: function() { - computeStyleTests(); - return scrollboxSizeVal; - }, - - // Support: IE 9 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Behavior in IE 9 is more subtle than in newer versions & it passes - // some versions of this test; make sure not to make it pass there! - // - // Support: Firefox 70+ - // Only Firefox includes border widths - // in computed dimensions. (gh-4529) - reliableTrDimensions: function() { - var table, tr, trChild, trStyle; - if ( reliableTrDimensionsVal == null ) { - table = document.createElement( "table" ); - tr = document.createElement( "tr" ); - trChild = document.createElement( "div" ); - - table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; - tr.style.cssText = "border:1px solid"; - - // Support: Chrome 86+ - // Height set through cssText does not get applied. - // Computed height then comes back as 0. - tr.style.height = "1px"; - trChild.style.height = "9px"; - - // Support: Android 8 Chrome 86+ - // In our bodyBackground.html iframe, - // display for all div elements is set to "inline", - // which causes a problem only in Android 8 Chrome 86. - // Ensuring the div is display: block - // gets around this issue. - trChild.style.display = "block"; - - documentElement - .appendChild( table ) - .appendChild( tr ) - .appendChild( trChild ); - - trStyle = window.getComputedStyle( tr ); - reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + - parseInt( trStyle.borderTopWidth, 10 ) + - parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; - - documentElement.removeChild( table ); - } - return reliableTrDimensionsVal; - } - } ); -} )(); - - -function curCSS( elem, name, computed ) { - var width, minWidth, maxWidth, ret, - isCustomProp = rcustomProp.test( name ), - - // Support: Firefox 51+ - // Retrieving style before computed somehow - // fixes an issue with getting wrong values - // on detached elements - style = elem.style; - - computed = computed || getStyles( elem ); - - // getPropertyValue is needed for: - // .css('filter') (IE 9 only, trac-12537) - // .css('--customProperty) (gh-3144) - if ( computed ) { - ret = computed.getPropertyValue( name ) || computed[ name ]; - - // trim whitespace for custom property (issue gh-4926) - if ( isCustomProp ) { - - // rtrim treats U+000D CARRIAGE RETURN and U+000C FORM FEED - // as whitespace while CSS does not, but this is not a problem - // because CSS preprocessing replaces them with U+000A LINE FEED - // (which *is* CSS whitespace) - // https://www.w3.org/TR/css-syntax-3/#input-preprocessing - ret = ret.replace( rtrimCSS, "$1" ); - } - - if ( ret === "" && !isAttached( elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Android Browser returns percentage for some values, - // but width seems to be reliably pixels. - // This is against the CSSOM draft spec: - // https://drafts.csswg.org/cssom/#resolved-values - if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret !== undefined ? - - // Support: IE <=9 - 11 only - // IE returns zIndex value as an integer. - ret + "" : - ret; -} - - -function addGetHookIf( conditionFn, hookFn ) { - - // Define the hook, we'll check on the first run if it's really needed. - return { - get: function() { - if ( conditionFn() ) { - - // Hook not needed (or it's not possible to use it due - // to missing dependency), remove it. - delete this.get; - return; - } - - // Hook needed; redefine it so that the support test is not executed again. - return ( this.get = hookFn ).apply( this, arguments ); - } - }; -} - - -var cssPrefixes = [ "Webkit", "Moz", "ms" ], - emptyStyle = document.createElement( "div" ).style, - vendorProps = {}; - -// Return a vendor-prefixed property or undefined -function vendorPropName( name ) { - - // Check for vendor prefixed names - var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in emptyStyle ) { - return name; - } - } -} - -// Return a potentially-mapped jQuery.cssProps or vendor prefixed property -function finalPropName( name ) { - var final = jQuery.cssProps[ name ] || vendorProps[ name ]; - - if ( final ) { - return final; - } - if ( name in emptyStyle ) { - return name; - } - return vendorProps[ name ] = vendorPropName( name ) || name; -} - - -var - - // Swappable if display is none or starts with table - // except "table", "table-cell", or "table-caption" - // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: "0", - fontWeight: "400" - }; - -function setPositiveNumber( _elem, value, subtract ) { - - // Any relative (+/-) values have already been - // normalized at this point - var matches = rcssNum.exec( value ); - return matches ? - - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : - value; -} - -function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { - var i = dimension === "width" ? 1 : 0, - extra = 0, - delta = 0; - - // Adjustment may not be necessary - if ( box === ( isBorderBox ? "border" : "content" ) ) { - return 0; - } - - for ( ; i < 4; i += 2 ) { - - // Both box models exclude margin - if ( box === "margin" ) { - delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); - } - - // If we get here with a content-box, we're seeking "padding" or "border" or "margin" - if ( !isBorderBox ) { - - // Add padding - delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // For "border" or "margin", add border - if ( box !== "padding" ) { - delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - - // But still keep track of it otherwise - } else { - extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - - // If we get here with a border-box (content + padding + border), we're seeking "content" or - // "padding" or "margin" - } else { - - // For "content", subtract padding - if ( box === "content" ) { - delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // For "content" or "padding", subtract border - if ( box !== "margin" ) { - delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - // Account for positive content-box scroll gutter when requested by providing computedVal - if ( !isBorderBox && computedVal >= 0 ) { - - // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border - // Assuming integer scroll gutter, subtract the rest and round down - delta += Math.max( 0, Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - computedVal - - delta - - extra - - 0.5 - - // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter - // Use an explicit zero to avoid NaN (gh-3964) - ) ) || 0; - } - - return delta; -} - -function getWidthOrHeight( elem, dimension, extra ) { - - // Start with computed style - var styles = getStyles( elem ), - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). - // Fake content-box until we know it's needed to know the true value. - boxSizingNeeded = !support.boxSizingReliable() || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - valueIsBorderBox = isBorderBox, - - val = curCSS( elem, dimension, styles ), - offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); - - // Support: Firefox <=54 - // Return a confounding non-pixel value or feign ignorance, as appropriate. - if ( rnumnonpx.test( val ) ) { - if ( !extra ) { - return val; - } - val = "auto"; - } - - - // Support: IE 9 - 11 only - // Use offsetWidth/offsetHeight for when box sizing is unreliable. - // In those cases, the computed value can be trusted to be border-box. - if ( ( !support.boxSizingReliable() && isBorderBox || - - // Support: IE 10 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Interestingly, in some cases IE 9 doesn't suffer from this issue. - !support.reliableTrDimensions() && nodeName( elem, "tr" ) || - - // Fall back to offsetWidth/offsetHeight when value is "auto" - // This happens for inline elements with no explicit setting (gh-3571) - val === "auto" || - - // Support: Android <=4.1 - 4.3 only - // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) - !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && - - // Make sure the element is visible & connected - elem.getClientRects().length ) { - - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // Where available, offsetWidth/offsetHeight approximate border box dimensions. - // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the - // retrieved value as a content box dimension. - valueIsBorderBox = offsetProp in elem; - if ( valueIsBorderBox ) { - val = elem[ offsetProp ]; - } - } - - // Normalize "" and auto - val = parseFloat( val ) || 0; - - // Adjust for the element's box model - return ( val + - boxModelAdjustment( - elem, - dimension, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles, - - // Provide the current computed size to request scroll gutter calculation (gh-3589) - val - ) - ) + "px"; -} - -jQuery.extend( { - - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Don't automatically add "px" to these possibly-unitless properties - cssNumber: { - "animationIterationCount": true, - "columnCount": true, - "fillOpacity": true, - "flexGrow": true, - "flexShrink": true, - "fontWeight": true, - "gridArea": true, - "gridColumn": true, - "gridColumnEnd": true, - "gridColumnStart": true, - "gridRow": true, - "gridRowEnd": true, - "gridRowStart": true, - "lineHeight": true, - "opacity": true, - "order": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: {}, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ), - style = elem.style; - - // Make sure that we're working with the right name. We don't - // want to query the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Gets hook for the prefixed version, then unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // Convert "+=" or "-=" to relative numbers (trac-7345) - if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { - value = adjustCSS( elem, name, ret ); - - // Fixes bug trac-9237 - type = "number"; - } - - // Make sure that null and NaN values aren't set (trac-7116) - if ( value == null || value !== value ) { - return; - } - - // If a number was passed in, add the unit (except for certain CSS properties) - // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append - // "px" to a few hardcoded values. - if ( type === "number" && !isCustomProp ) { - value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); - } - - // background-* props affect original clone's values - if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !( "set" in hooks ) || - ( value = hooks.set( elem, value, extra ) ) !== undefined ) { - - if ( isCustomProp ) { - style.setProperty( name, value ); - } else { - style[ name ] = value; - } - } - - } else { - - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && - ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { - - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ); - - // Make sure that we're working with the right name. We don't - // want to modify the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Try prefixed name followed by the unprefixed name - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - // Convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Make numeric if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || isFinite( num ) ? num || 0 : val; - } - - return val; - } -} ); - -jQuery.each( [ "height", "width" ], function( _i, dimension ) { - jQuery.cssHooks[ dimension ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - - // Certain elements can have dimension info if we invisibly show them - // but it must have a current display style that would benefit - return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - - // Support: Safari 8+ - // Table columns in Safari have non-zero offsetWidth & zero - // getBoundingClientRect().width unless display is changed. - // Support: IE <=11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? - swap( elem, cssShow, function() { - return getWidthOrHeight( elem, dimension, extra ); - } ) : - getWidthOrHeight( elem, dimension, extra ); - } - }, - - set: function( elem, value, extra ) { - var matches, - styles = getStyles( elem ), - - // Only read styles.position if the test has a chance to fail - // to avoid forcing a reflow. - scrollboxSizeBuggy = !support.scrollboxSize() && - styles.position === "absolute", - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) - boxSizingNeeded = scrollboxSizeBuggy || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - subtract = extra ? - boxModelAdjustment( - elem, - dimension, - extra, - isBorderBox, - styles - ) : - 0; - - // Account for unreliable border-box dimensions by comparing offset* to computed and - // faking a content-box to get border and padding (gh-3699) - if ( isBorderBox && scrollboxSizeBuggy ) { - subtract -= Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - parseFloat( styles[ dimension ] ) - - boxModelAdjustment( elem, dimension, "border", false, styles ) - - 0.5 - ); - } - - // Convert to pixels if value adjustment is needed - if ( subtract && ( matches = rcssNum.exec( value ) ) && - ( matches[ 3 ] || "px" ) !== "px" ) { - - elem.style[ dimension ] = value; - value = jQuery.css( elem, dimension ); - } - - return setPositiveNumber( elem, value, subtract ); - } - }; -} ); - -jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, - function( elem, computed ) { - if ( computed ) { - return ( parseFloat( curCSS( elem, "marginLeft" ) ) || - elem.getBoundingClientRect().left - - swap( elem, { marginLeft: 0 }, function() { - return elem.getBoundingClientRect().left; - } ) - ) + "px"; - } - } -); - -// These hooks are used by animate to expand properties -jQuery.each( { - margin: "", - padding: "", - border: "Width" -}, function( prefix, suffix ) { - jQuery.cssHooks[ prefix + suffix ] = { - expand: function( value ) { - var i = 0, - expanded = {}, - - // Assumes a single number if not a string - parts = typeof value === "string" ? value.split( " " ) : [ value ]; - - for ( ; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( prefix !== "margin" ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } -} ); - -jQuery.fn.extend( { - css: function( name, value ) { - return access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( Array.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - } -} ); - - -function Tween( elem, options, prop, end, easing ) { - return new Tween.prototype.init( elem, options, prop, end, easing ); -} -jQuery.Tween = Tween; - -Tween.prototype = { - constructor: Tween, - init: function( elem, options, prop, end, easing, unit ) { - this.elem = elem; - this.prop = prop; - this.easing = easing || jQuery.easing._default; - this.options = options; - this.start = this.now = this.cur(); - this.end = end; - this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - }, - cur: function() { - var hooks = Tween.propHooks[ this.prop ]; - - return hooks && hooks.get ? - hooks.get( this ) : - Tween.propHooks._default.get( this ); - }, - run: function( percent ) { - var eased, - hooks = Tween.propHooks[ this.prop ]; - - if ( this.options.duration ) { - this.pos = eased = jQuery.easing[ this.easing ]( - percent, this.options.duration * percent, 0, 1, this.options.duration - ); - } else { - this.pos = eased = percent; - } - this.now = ( this.end - this.start ) * eased + this.start; - - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - if ( hooks && hooks.set ) { - hooks.set( this ); - } else { - Tween.propHooks._default.set( this ); - } - return this; - } -}; - -Tween.prototype.init.prototype = Tween.prototype; - -Tween.propHooks = { - _default: { - get: function( tween ) { - var result; - - // Use a property on the element directly when it is not a DOM element, - // or when there is no matching style property that exists. - if ( tween.elem.nodeType !== 1 || - tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { - return tween.elem[ tween.prop ]; - } - - // Passing an empty string as a 3rd parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails. - // Simple values such as "10px" are parsed to Float; - // complex values such as "rotate(1rad)" are returned as-is. - result = jQuery.css( tween.elem, tween.prop, "" ); - - // Empty strings, null, undefined and "auto" are converted to 0. - return !result || result === "auto" ? 0 : result; - }, - set: function( tween ) { - - // Use step hook for back compat. - // Use cssHook if its there. - // Use .style if available and use plain properties where available. - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.nodeType === 1 && ( - jQuery.cssHooks[ tween.prop ] || - tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { - jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); - } else { - tween.elem[ tween.prop ] = tween.now; - } - } - } -}; - -// Support: IE <=9 only -// Panic based approach to setting things on disconnected nodes -Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { - set: function( tween ) { - if ( tween.elem.nodeType && tween.elem.parentNode ) { - tween.elem[ tween.prop ] = tween.now; - } - } -}; - -jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p * Math.PI ) / 2; - }, - _default: "swing" -}; - -jQuery.fx = Tween.prototype.init; - -// Back compat <1.8 extension point -jQuery.fx.step = {}; - - - - -var - fxNow, inProgress, - rfxtypes = /^(?:toggle|show|hide)$/, - rrun = /queueHooks$/; - -function schedule() { - if ( inProgress ) { - if ( document.hidden === false && window.requestAnimationFrame ) { - window.requestAnimationFrame( schedule ); - } else { - window.setTimeout( schedule, jQuery.fx.interval ); - } - - jQuery.fx.tick(); - } -} - -// Animations created synchronously will run synchronously -function createFxNow() { - window.setTimeout( function() { - fxNow = undefined; - } ); - return ( fxNow = Date.now() ); -} - -// Generate parameters to create a standard animation -function genFx( type, includeWidth ) { - var which, - i = 0, - attrs = { height: type }; - - // If we include width, step value is 1 to do all cssExpand values, - // otherwise step value is 2 to skip over Left and Right - includeWidth = includeWidth ? 1 : 0; - for ( ; i < 4; i += 2 - includeWidth ) { - which = cssExpand[ i ]; - attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; - } - - if ( includeWidth ) { - attrs.opacity = attrs.width = type; - } - - return attrs; -} - -function createTween( value, prop, animation ) { - var tween, - collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { - - // We're done with this property - return tween; - } - } -} - -function defaultPrefilter( elem, props, opts ) { - var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, - isBox = "width" in props || "height" in props, - anim = this, - orig = {}, - style = elem.style, - hidden = elem.nodeType && isHiddenWithinTree( elem ), - dataShow = dataPriv.get( elem, "fxshow" ); - - // Queue-skipping animations hijack the fx hooks - if ( !opts.queue ) { - hooks = jQuery._queueHooks( elem, "fx" ); - if ( hooks.unqueued == null ) { - hooks.unqueued = 0; - oldfire = hooks.empty.fire; - hooks.empty.fire = function() { - if ( !hooks.unqueued ) { - oldfire(); - } - }; - } - hooks.unqueued++; - - anim.always( function() { - - // Ensure the complete handler is called before this completes - anim.always( function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - } ); - } ); - } - - // Detect show/hide animations - for ( prop in props ) { - value = props[ prop ]; - if ( rfxtypes.test( value ) ) { - delete props[ prop ]; - toggle = toggle || value === "toggle"; - if ( value === ( hidden ? "hide" : "show" ) ) { - - // Pretend to be hidden if this is a "show" and - // there is still data from a stopped show/hide - if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { - hidden = true; - - // Ignore all other no-op show/hide data - } else { - continue; - } - } - orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); - } - } - - // Bail out if this is a no-op like .hide().hide() - propTween = !jQuery.isEmptyObject( props ); - if ( !propTween && jQuery.isEmptyObject( orig ) ) { - return; - } - - // Restrict "overflow" and "display" styles during box animations - if ( isBox && elem.nodeType === 1 ) { - - // Support: IE <=9 - 11, Edge 12 - 15 - // Record all 3 overflow attributes because IE does not infer the shorthand - // from identically-valued overflowX and overflowY and Edge just mirrors - // the overflowX value there. - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Identify a display type, preferring old show/hide data over the CSS cascade - restoreDisplay = dataShow && dataShow.display; - if ( restoreDisplay == null ) { - restoreDisplay = dataPriv.get( elem, "display" ); - } - display = jQuery.css( elem, "display" ); - if ( display === "none" ) { - if ( restoreDisplay ) { - display = restoreDisplay; - } else { - - // Get nonempty value(s) by temporarily forcing visibility - showHide( [ elem ], true ); - restoreDisplay = elem.style.display || restoreDisplay; - display = jQuery.css( elem, "display" ); - showHide( [ elem ] ); - } - } - - // Animate inline elements as inline-block - if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { - if ( jQuery.css( elem, "float" ) === "none" ) { - - // Restore the original display value at the end of pure show/hide animations - if ( !propTween ) { - anim.done( function() { - style.display = restoreDisplay; - } ); - if ( restoreDisplay == null ) { - display = style.display; - restoreDisplay = display === "none" ? "" : display; - } - } - style.display = "inline-block"; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - anim.always( function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - } ); - } - - // Implement show/hide animations - propTween = false; - for ( prop in orig ) { - - // General show/hide setup for this element animation - if ( !propTween ) { - if ( dataShow ) { - if ( "hidden" in dataShow ) { - hidden = dataShow.hidden; - } - } else { - dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); - } - - // Store hidden/visible for toggle so `.stop().toggle()` "reverses" - if ( toggle ) { - dataShow.hidden = !hidden; - } - - // Show elements before animating them - if ( hidden ) { - showHide( [ elem ], true ); - } - - /* eslint-disable no-loop-func */ - - anim.done( function() { - - /* eslint-enable no-loop-func */ - - // The final step of a "hide" animation is actually hiding the element - if ( !hidden ) { - showHide( [ elem ] ); - } - dataPriv.remove( elem, "fxshow" ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - } ); - } - - // Per-property setup - propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = propTween.start; - if ( hidden ) { - propTween.end = propTween.start; - propTween.start = 0; - } - } - } -} - -function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( Array.isArray( value ) ) { - easing = value[ 1 ]; - value = props[ index ] = value[ 0 ]; - } - - if ( index !== name ) { - props[ name ] = value; - delete props[ index ]; - } - - hooks = jQuery.cssHooks[ name ]; - if ( hooks && "expand" in hooks ) { - value = hooks.expand( value ); - delete props[ name ]; - - // Not quite $.extend, this won't overwrite existing keys. - // Reusing 'index' because we have the correct "name" - for ( index in value ) { - if ( !( index in props ) ) { - props[ index ] = value[ index ]; - specialEasing[ index ] = easing; - } - } - } else { - specialEasing[ name ] = easing; - } - } -} - -function Animation( elem, properties, options ) { - var result, - stopped, - index = 0, - length = Animation.prefilters.length, - deferred = jQuery.Deferred().always( function() { - - // Don't match elem in the :animated selector - delete tick.elem; - } ), - tick = function() { - if ( stopped ) { - return false; - } - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - - // Support: Android 2.3 only - // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (trac-12497) - temp = remaining / animation.duration || 0, - percent = 1 - temp, - index = 0, - length = animation.tweens.length; - - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ] ); - - // If there's more to do, yield - if ( percent < 1 && length ) { - return remaining; - } - - // If this was an empty animation, synthesize a final progress notification - if ( !length ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - } - - // Resolve the animation and report its conclusion - deferred.resolveWith( elem, [ animation ] ); - return false; - }, - animation = deferred.promise( { - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { - specialEasing: {}, - easing: jQuery.easing._default - }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end ) { - var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); - animation.tweens.push( tween ); - return tween; - }, - stop: function( gotoEnd ) { - var index = 0, - - // If we are going to the end, we want to run all the tweens - // otherwise we skip this part - length = gotoEnd ? animation.tweens.length : 0; - if ( stopped ) { - return this; - } - stopped = true; - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // Resolve when we played the last frame; otherwise, reject - if ( gotoEnd ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - deferred.resolveWith( elem, [ animation, gotoEnd ] ); - } else { - deferred.rejectWith( elem, [ animation, gotoEnd ] ); - } - return this; - } - } ), - props = animation.props; - - propFilter( props, animation.opts.specialEasing ); - - for ( ; index < length; index++ ) { - result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - if ( isFunction( result.stop ) ) { - jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = - result.stop.bind( result ); - } - return result; - } - } - - jQuery.map( props, createTween, animation ); - - if ( isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - // Attach callbacks from options - animation - .progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); - - jQuery.fx.timer( - jQuery.extend( tick, { - elem: elem, - anim: animation, - queue: animation.opts.queue - } ) - ); - - return animation; -} - -jQuery.Animation = jQuery.extend( Animation, { - - tweeners: { - "*": [ function( prop, value ) { - var tween = this.createTween( prop, value ); - adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); - return tween; - } ] - }, - - tweener: function( props, callback ) { - if ( isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.match( rnothtmlwhite ); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length; index++ ) { - prop = props[ index ]; - Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; - Animation.tweeners[ prop ].unshift( callback ); - } - }, - - prefilters: [ defaultPrefilter ], - - prefilter: function( callback, prepend ) { - if ( prepend ) { - Animation.prefilters.unshift( callback ); - } else { - Animation.prefilters.push( callback ); - } - } -} ); - -jQuery.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !isFunction( easing ) && easing - }; - - // Go to the end state if fx are off - if ( jQuery.fx.off ) { - opt.duration = 0; - - } else { - if ( typeof opt.duration !== "number" ) { - if ( opt.duration in jQuery.fx.speeds ) { - opt.duration = jQuery.fx.speeds[ opt.duration ]; - - } else { - opt.duration = jQuery.fx.speeds._default; - } - } - } - - // Normalize opt.queue - true/undefined/null -> "fx" - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } - - // Queueing - opt.old = opt.complete; - - opt.complete = function() { - if ( isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; -}; - -jQuery.fn.extend( { - fadeTo: function( speed, to, easing, callback ) { - - // Show any hidden elements after setting opacity to 0 - return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() - - // Animate to the value specified - .end().animate( { opacity: to }, speed, easing, callback ); - }, - animate: function( prop, speed, easing, callback ) { - var empty = jQuery.isEmptyObject( prop ), - optall = jQuery.speed( speed, easing, callback ), - doAnimation = function() { - - // Operate on a copy of prop so per-property easing won't be lost - var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations, or finishing resolves immediately - if ( empty || dataPriv.get( this, "finish" ) ) { - anim.stop( true ); - } - }; - - doAnimation.finish = doAnimation; - - return empty || optall.queue === false ? - this.each( doAnimation ) : - this.queue( optall.queue, doAnimation ); - }, - stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( hooks ) { - var stop = hooks.stop; - delete hooks.stop; - stop( gotoEnd ); - }; - - if ( typeof type !== "string" ) { - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue ) { - this.queue( type || "fx", [] ); - } - - return this.each( function() { - var dequeue = true, - index = type != null && type + "queueHooks", - timers = jQuery.timers, - data = dataPriv.get( this ); - - if ( index ) { - if ( data[ index ] && data[ index ].stop ) { - stopQueue( data[ index ] ); - } - } else { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( data[ index ] ); - } - } - } - - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && - ( type == null || timers[ index ].queue === type ) ) { - - timers[ index ].anim.stop( gotoEnd ); - dequeue = false; - timers.splice( index, 1 ); - } - } - - // Start the next in the queue if the last step wasn't forced. - // Timers currently will call their complete callbacks, which - // will dequeue but only if they were gotoEnd. - if ( dequeue || !gotoEnd ) { - jQuery.dequeue( this, type ); - } - } ); - }, - finish: function( type ) { - if ( type !== false ) { - type = type || "fx"; - } - return this.each( function() { - var index, - data = dataPriv.get( this ), - queue = data[ type + "queue" ], - hooks = data[ type + "queueHooks" ], - timers = jQuery.timers, - length = queue ? queue.length : 0; - - // Enable finishing flag on private data - data.finish = true; - - // Empty the queue first - jQuery.queue( this, type, [] ); - - if ( hooks && hooks.stop ) { - hooks.stop.call( this, true ); - } - - // Look for any active animations, and finish them - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && timers[ index ].queue === type ) { - timers[ index ].anim.stop( true ); - timers.splice( index, 1 ); - } - } - - // Look for any animations in the old queue and finish them - for ( index = 0; index < length; index++ ) { - if ( queue[ index ] && queue[ index ].finish ) { - queue[ index ].finish.call( this ); - } - } - - // Turn off finishing flag - delete data.finish; - } ); - } -} ); - -jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { - var cssFn = jQuery.fn[ name ]; - jQuery.fn[ name ] = function( speed, easing, callback ) { - return speed == null || typeof speed === "boolean" ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; -} ); - -// Generate shortcuts for custom animations -jQuery.each( { - slideDown: genFx( "show" ), - slideUp: genFx( "hide" ), - slideToggle: genFx( "toggle" ), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; -} ); - -jQuery.timers = []; -jQuery.fx.tick = function() { - var timer, - i = 0, - timers = jQuery.timers; - - fxNow = Date.now(); - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - - // Run the timer and safely remove it when done (allowing for external removal) - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } - fxNow = undefined; -}; - -jQuery.fx.timer = function( timer ) { - jQuery.timers.push( timer ); - jQuery.fx.start(); -}; - -jQuery.fx.interval = 13; -jQuery.fx.start = function() { - if ( inProgress ) { - return; - } - - inProgress = true; - schedule(); -}; - -jQuery.fx.stop = function() { - inProgress = null; -}; - -jQuery.fx.speeds = { - slow: 600, - fast: 200, - - // Default speed - _default: 400 -}; - - -// Based off of the plugin by Clint Helfers, with permission. -jQuery.fn.delay = function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = window.setTimeout( next, time ); - hooks.stop = function() { - window.clearTimeout( timeout ); - }; - } ); -}; - - -( function() { - var input = document.createElement( "input" ), - select = document.createElement( "select" ), - opt = select.appendChild( document.createElement( "option" ) ); - - input.type = "checkbox"; - - // Support: Android <=4.3 only - // Default value for a checkbox should be "on" - support.checkOn = input.value !== ""; - - // Support: IE <=11 only - // Must access selectedIndex to make default options select - support.optSelected = opt.selected; - - // Support: IE <=11 only - // An input loses its value after becoming a radio - input = document.createElement( "input" ); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; -} )(); - - -var boolHook, - attrHandle = jQuery.expr.attrHandle; - -jQuery.fn.extend( { - attr: function( name, value ) { - return access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each( function() { - jQuery.removeAttr( this, name ); - } ); - } -} ); - -jQuery.extend( { - attr: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set attributes on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - // Attribute hooks are determined by the lowercase version - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - hooks = jQuery.attrHooks[ name.toLowerCase() ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); - } - - if ( value !== undefined ) { - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - } - - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - elem.setAttribute( name, value + "" ); - return value; - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? undefined : ret; - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !support.radioValue && value === "radio" && - nodeName( elem, "input" ) ) { - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - removeAttr: function( elem, value ) { - var name, - i = 0, - - // Attribute names can contain non-HTML whitespace characters - // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 - attrNames = value && value.match( rnothtmlwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( ( name = attrNames[ i++ ] ) ) { - elem.removeAttribute( name ); - } - } - } -} ); - -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; - -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { - var getter = attrHandle[ name ] || jQuery.find.attr; - - attrHandle[ name ] = function( elem, name, isXML ) { - var ret, handle, - lowercaseName = name.toLowerCase(); - - if ( !isXML ) { - - // Avoid an infinite loop by temporarily removing this function from the getter - handle = attrHandle[ lowercaseName ]; - attrHandle[ lowercaseName ] = ret; - ret = getter( elem, name, isXML ) != null ? - lowercaseName : - null; - attrHandle[ lowercaseName ] = handle; - } - return ret; - }; -} ); - - - - -var rfocusable = /^(?:input|select|textarea|button)$/i, - rclickable = /^(?:a|area)$/i; - -jQuery.fn.extend( { - prop: function( name, value ) { - return access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each( function() { - delete this[ jQuery.propFix[ name ] || name ]; - } ); - } -} ); - -jQuery.extend( { - prop: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set properties on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - return ( elem[ name ] = value ); - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - return elem[ name ]; - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - - // Support: IE <=9 - 11 only - // elem.tabIndex doesn't always return the - // correct value when it hasn't been explicitly set - // Use proper attribute retrieval (trac-12072) - var tabindex = jQuery.find.attr( elem, "tabindex" ); - - if ( tabindex ) { - return parseInt( tabindex, 10 ); - } - - if ( - rfocusable.test( elem.nodeName ) || - rclickable.test( elem.nodeName ) && - elem.href - ) { - return 0; - } - - return -1; - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - } -} ); - -// Support: IE <=11 only -// Accessing the selectedIndex property -// forces the browser to respect setting selected -// on the option -// The getter ensures a default option is selected -// when in an optgroup -// eslint rule "no-unused-expressions" is disabled for this code -// since it considers such accessions noop -if ( !support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - }, - set: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - }; -} - -jQuery.each( [ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -} ); - - - - - // Strip and collapse whitespace according to HTML spec - // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace - function stripAndCollapse( value ) { - var tokens = value.match( rnothtmlwhite ) || []; - return tokens.join( " " ); - } - - -function getClass( elem ) { - return elem.getAttribute && elem.getAttribute( "class" ) || ""; -} - -function classesToArray( value ) { - if ( Array.isArray( value ) ) { - return value; - } - if ( typeof value === "string" ) { - return value.match( rnothtmlwhite ) || []; - } - return []; -} - -jQuery.fn.extend( { - addClass: function( value ) { - var classNames, cur, curValue, className, i, finalValue; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - classNames = classesToArray( value ); - - if ( classNames.length ) { - return this.each( function() { - curValue = getClass( this ); - cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - if ( cur.indexOf( " " + className + " " ) < 0 ) { - cur += className + " "; - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - this.setAttribute( "class", finalValue ); - } - } - } ); - } - - return this; - }, - - removeClass: function( value ) { - var classNames, cur, curValue, className, i, finalValue; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( !arguments.length ) { - return this.attr( "class", "" ); - } - - classNames = classesToArray( value ); - - if ( classNames.length ) { - return this.each( function() { - curValue = getClass( this ); - - // This expression is here for better compressibility (see addClass) - cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - - // Remove *all* instances - while ( cur.indexOf( " " + className + " " ) > -1 ) { - cur = cur.replace( " " + className + " ", " " ); - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - this.setAttribute( "class", finalValue ); - } - } - } ); - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var classNames, className, i, self, - type = typeof value, - isValidValue = type === "string" || Array.isArray( value ); - - if ( isFunction( value ) ) { - return this.each( function( i ) { - jQuery( this ).toggleClass( - value.call( this, i, getClass( this ), stateVal ), - stateVal - ); - } ); - } - - if ( typeof stateVal === "boolean" && isValidValue ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } - - classNames = classesToArray( value ); - - return this.each( function() { - if ( isValidValue ) { - - // Toggle individual class names - self = jQuery( this ); - - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - - // Check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } - - // Toggle whole class name - } else if ( value === undefined || type === "boolean" ) { - className = getClass( this ); - if ( className ) { - - // Store className if set - dataPriv.set( this, "__className__", className ); - } - - // If the element has a class name or if we're passed `false`, - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - if ( this.setAttribute ) { - this.setAttribute( "class", - className || value === false ? - "" : - dataPriv.get( this, "__className__" ) || "" - ); - } - } - } ); - }, - - hasClass: function( selector ) { - var className, elem, - i = 0; - - className = " " + selector + " "; - while ( ( elem = this[ i++ ] ) ) { - if ( elem.nodeType === 1 && - ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { - return true; - } - } - - return false; - } -} ); - - - - -var rreturn = /\r/g; - -jQuery.fn.extend( { - val: function( value ) { - var hooks, ret, valueIsFunction, - elem = this[ 0 ]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || - jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && - "get" in hooks && - ( ret = hooks.get( elem, "value" ) ) !== undefined - ) { - return ret; - } - - ret = elem.value; - - // Handle most common string cases - if ( typeof ret === "string" ) { - return ret.replace( rreturn, "" ); - } - - // Handle cases where value is null/undef or number - return ret == null ? "" : ret; - } - - return; - } - - valueIsFunction = isFunction( value ); - - return this.each( function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( valueIsFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - - } else if ( typeof val === "number" ) { - val += ""; - - } else if ( Array.isArray( val ) ) { - val = jQuery.map( val, function( value ) { - return value == null ? "" : value + ""; - } ); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - } ); - } -} ); - -jQuery.extend( { - valHooks: { - option: { - get: function( elem ) { - - var val = jQuery.find.attr( elem, "value" ); - return val != null ? - val : - - // Support: IE <=10 - 11 only - // option.text throws exceptions (trac-14686, trac-14858) - // Strip and collapse whitespace - // https://html.spec.whatwg.org/#strip-and-collapse-whitespace - stripAndCollapse( jQuery.text( elem ) ); - } - }, - select: { - get: function( elem ) { - var value, option, i, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one", - values = one ? null : [], - max = one ? index + 1 : options.length; - - if ( index < 0 ) { - i = max; - - } else { - i = one ? index : 0; - } - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Support: IE <=9 only - // IE8-9 doesn't update selected after form reset (trac-2551) - if ( ( option.selected || i === index ) && - - // Don't return options that are disabled or in a disabled optgroup - !option.disabled && - ( !option.parentNode.disabled || - !nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - - /* eslint-disable no-cond-assign */ - - if ( option.selected = - jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 - ) { - optionSet = true; - } - - /* eslint-enable no-cond-assign */ - } - - // Force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - } -} ); - -// Radios and checkboxes getter/setter -jQuery.each( [ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( Array.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); - } - } - }; - if ( !support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - return elem.getAttribute( "value" ) === null ? "on" : elem.value; - }; - } -} ); - - - - -// Return jQuery for attributes-only inclusion - - -support.focusin = "onfocusin" in window; - - -var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - stopPropagationCallback = function( e ) { - e.stopPropagation(); - }; - -jQuery.extend( jQuery.event, { - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; - - cur = lastElement = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "." ) > -1 ) { - - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split( "." ); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf( ":" ) < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join( "." ); - event.rnamespace = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (trac-9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (trac-9724) - if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === ( elem.ownerDocument || document ) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { - lastElement = cur; - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && - dataPriv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( ( !special._default || - special._default.apply( eventPath.pop(), data ) === false ) && - acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name as the event. - // Don't do default actions on window, that's where global variables be (trac-6170) - if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - - if ( event.isPropagationStopped() ) { - lastElement.addEventListener( type, stopPropagationCallback ); - } - - elem[ type ](); - - if ( event.isPropagationStopped() ) { - lastElement.removeEventListener( type, stopPropagationCallback ); - } - - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - // Piggyback on a donor event to simulate a different one - // Used only for `focus(in | out)` events - simulate: function( type, elem, event ) { - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true - } - ); - - jQuery.event.trigger( e, null, elem ); - } - -} ); - -jQuery.fn.extend( { - - trigger: function( type, data ) { - return this.each( function() { - jQuery.event.trigger( type, data, this ); - } ); - }, - triggerHandler: function( type, data ) { - var elem = this[ 0 ]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -} ); - - -// Support: Firefox <=44 -// Firefox doesn't have focus(in | out) events -// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 -// -// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 -// focus(in | out) events fire after focus & blur events, -// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order -// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 -if ( !support.focusin ) { - jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - - // Handle: regular nodes (via `this.ownerDocument`), window - // (via `this.document`) & document (via `this`). - var doc = this.ownerDocument || this.document || this, - attaches = dataPriv.access( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this.document || this, - attaches = dataPriv.access( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - dataPriv.remove( doc, fix ); - - } else { - dataPriv.access( doc, fix, attaches ); - } - } - }; - } ); -} -var location = window.location; - -var nonce = { guid: Date.now() }; - -var rquery = ( /\?/ ); - - - -// Cross-browser xml parsing -jQuery.parseXML = function( data ) { - var xml, parserErrorElem; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE 9 - 11 only - // IE throws on parseFromString with invalid input. - try { - xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) {} - - parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; - if ( !xml || parserErrorElem ) { - jQuery.error( "Invalid XML: " + ( - parserErrorElem ? - jQuery.map( parserErrorElem.childNodes, function( el ) { - return el.textContent; - } ).join( "\n" ) : - data - ) ); - } - return xml; -}; - - -var - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, - rsubmittable = /^(?:input|select|textarea|keygen)/i; - -function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( Array.isArray( obj ) ) { - - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - - // Item is non-scalar (array or object), encode its numeric index. - buildParams( - prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", - v, - traditional, - add - ); - } - } ); - - } else if ( !traditional && toType( obj ) === "object" ) { - - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - - // Serialize scalar item. - add( prefix, obj ); - } -} - -// Serialize an array of form elements or a set of -// key/values into a query string -jQuery.param = function( a, traditional ) { - var prefix, - s = [], - add = function( key, valueOrFunction ) { - - // If value is a function, invoke it and use its return value - var value = isFunction( valueOrFunction ) ? - valueOrFunction() : - valueOrFunction; - - s[ s.length ] = encodeURIComponent( key ) + "=" + - encodeURIComponent( value == null ? "" : value ); - }; - - if ( a == null ) { - return ""; - } - - // If an array was passed in, assume that it is an array of form elements. - if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - } ); - - } else { - - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ); -}; - -jQuery.fn.extend( { - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map( function() { - - // Can add propHook for "elements" to filter or add form elements - var elements = jQuery.prop( this, "elements" ); - return elements ? jQuery.makeArray( elements ) : this; - } ).filter( function() { - var type = this.type; - - // Use .is( ":disabled" ) so that fieldset[disabled] works - return this.name && !jQuery( this ).is( ":disabled" ) && - rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && - ( this.checked || !rcheckableType.test( type ) ); - } ).map( function( _i, elem ) { - var val = jQuery( this ).val(); - - if ( val == null ) { - return null; - } - - if ( Array.isArray( val ) ) { - return jQuery.map( val, function( val ) { - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ); - } - - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ).get(); - } -} ); - - -var - r20 = /%20/g, - rhash = /#.*$/, - rantiCache = /([?&])_=[^&]*/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, - - // trac-7653, trac-8125, trac-8152: local protocol detection - rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Avoid comment-prolog char sequence (trac-10098); must appease lint and evade compression - allTypes = "*/".concat( "*" ), - - // Anchor tag for parsing the document origin - originAnchor = document.createElement( "a" ); - -originAnchor.href = location.href; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport -function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - var dataType, - i = 0, - dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; - - if ( isFunction( func ) ) { - - // For each dataType in the dataTypeExpression - while ( ( dataType = dataTypes[ i++ ] ) ) { - - // Prepend if requested - if ( dataType[ 0 ] === "+" ) { - dataType = dataType.slice( 1 ) || "*"; - ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); - - // Otherwise append - } else { - ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); - } - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { - - var inspected = {}, - seekingTransport = ( structure === transports ); - - function inspect( dataType ) { - var selected; - inspected[ dataType ] = true; - jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { - var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); - if ( typeof dataTypeOrTransport === "string" && - !seekingTransport && !inspected[ dataTypeOrTransport ] ) { - - options.dataTypes.unshift( dataTypeOrTransport ); - inspect( dataTypeOrTransport ); - return false; - } else if ( seekingTransport ) { - return !( selected = dataTypeOrTransport ); - } - } ); - return selected; - } - - return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); -} - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes trac-9887 -function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - - for ( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } - - return target; -} - -/* Handles responses to an ajax request: - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes; - - // Remove auto dataType and get content-type in the process - while ( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } -} - -/* Chain conversions given the request and the original response - * Also sets the responseXXX fields on the jqXHR instance - */ -function ajaxConvert( s, response, jqXHR, isSuccess ) { - var conv2, current, conv, tmp, prev, - converters = {}, - - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(); - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - current = dataTypes.shift(); - - // Convert to each sequential dataType - while ( current ) { - - if ( s.responseFields[ current ] ) { - jqXHR[ s.responseFields[ current ] ] = response; - } - - // Apply the dataFilter if provided - if ( !prev && isSuccess && s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - prev = current; - current = dataTypes.shift(); - - if ( current ) { - - // There's only work to do if current dataType is non-auto - if ( current === "*" ) { - - current = prev; - - // Convert response if prev dataType is non-auto and differs from current - } else if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split( " " ); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.unshift( tmp[ 1 ] ); - } - break; - } - } - } - } - - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s.throws ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { - state: "parsererror", - error: conv ? e : "No conversion from " + prev + " to " + current - }; - } - } - } - } - } - } - - return { state: "success", data: response }; -} - -jQuery.extend( { - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {}, - - ajaxSettings: { - url: location.href, - type: "GET", - isLocal: rlocalProtocol.test( location.protocol ), - global: true, - processData: true, - async: true, - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - "*": allTypes, - text: "text/plain", - html: "text/html", - xml: "application/xml, text/xml", - json: "application/json, text/javascript" - }, - - contents: { - xml: /\bxml\b/, - html: /\bhtml/, - json: /\bjson\b/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText", - json: "responseJSON" - }, - - // Data converters - // Keys separate source (or catchall "*") and destination types with a single space - converters: { - - // Convert anything to text - "* text": String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": JSON.parse, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - url: true, - context: true - } - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - return settings ? - - // Building a settings object - ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : - - // Extending ajaxSettings - ajaxExtend( jQuery.ajaxSettings, target ); - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var transport, - - // URL without anti-cache param - cacheURL, - - // Response headers - responseHeadersString, - responseHeaders, - - // timeout handle - timeoutTimer, - - // Url cleanup var - urlAnchor, - - // Request state (becomes false upon send and true upon completion) - completed, - - // To know if global events are to be dispatched - fireGlobals, - - // Loop variable - i, - - // uncached part of the url - uncached, - - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - - // Callbacks context - callbackContext = s.context || s, - - // Context for global events is callbackContext if it is a DOM node or jQuery collection - globalEventContext = s.context && - ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, - - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks( "once memory" ), - - // Status-dependent callbacks - statusCode = s.statusCode || {}, - - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - - // Default abort message - strAbort = "canceled", - - // Fake xhr - jqXHR = { - readyState: 0, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( completed ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while ( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() + " " ] = - ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) - .concat( match[ 2 ] ); - } - } - match = responseHeaders[ key.toLowerCase() + " " ]; - } - return match == null ? null : match.join( ", " ); - }, - - // Raw string - getAllResponseHeaders: function() { - return completed ? responseHeadersString : null; - }, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( completed == null ) { - name = requestHeadersNames[ name.toLowerCase() ] = - requestHeadersNames[ name.toLowerCase() ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( completed == null ) { - s.mimeType = type; - } - return this; - }, - - // Status-dependent callbacks - statusCode: function( map ) { - var code; - if ( map ) { - if ( completed ) { - - // Execute the appropriate callbacks - jqXHR.always( map[ jqXHR.status ] ); - } else { - - // Lazy-add the new callbacks in a way that preserves old ones - for ( code in map ) { - statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; - } - } - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - var finalText = statusText || strAbort; - if ( transport ) { - transport.abort( finalText ); - } - done( 0, finalText ); - return this; - } - }; - - // Attach deferreds - deferred.promise( jqXHR ); - - // Add protocol if not provided (prefilters might expect it) - // Handle falsy url in the settings object (trac-10093: consistency with old signature) - // We also use the url parameter if available - s.url = ( ( url || s.url || location.href ) + "" ) - .replace( rprotocol, location.protocol + "//" ); - - // Alias method option to type as per ticket trac-12004 - s.type = options.method || options.type || s.method || s.type; - - // Extract dataTypes list - s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; - - // A cross-domain request is in order when the origin doesn't match the current origin. - if ( s.crossDomain == null ) { - urlAnchor = document.createElement( "a" ); - - // Support: IE <=8 - 11, Edge 12 - 15 - // IE throws exception on accessing the href property if url is malformed, - // e.g. http://example.com:80x/ - try { - urlAnchor.href = s.url; - - // Support: IE <=8 - 11 only - // Anchor's host property isn't correctly set when s.url is relative - urlAnchor.href = urlAnchor.href; - s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== - urlAnchor.protocol + "//" + urlAnchor.host; - } catch ( e ) { - - // If there is an error parsing the URL, assume it is crossDomain, - // it can be rejected by the transport if it is invalid - s.crossDomain = true; - } - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefilter, stop there - if ( completed ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (trac-15118) - fireGlobals = jQuery.event && s.global; - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Save the URL in case we're toying with the If-Modified-Since - // and/or If-None-Match header later on - // Remove hash to simplify url manipulation - cacheURL = s.url.replace( rhash, "" ); - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // Remember the hash so we can put it back - uncached = s.url.slice( cacheURL.length ); - - // If data is available and should be processed, append data to url - if ( s.data && ( s.processData || typeof s.data === "string" ) ) { - cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; - - // trac-9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Add or update anti-cache param if needed - if ( s.cache === false ) { - cacheURL = cacheURL.replace( rantiCache, "$1" ); - uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + - uncached; - } - - // Put hash and anti-cache on the URL that will be requested (gh-1732) - s.url = cacheURL + uncached; - - // Change '%20' to '+' if this is encoded form body content (gh-2658) - } else if ( s.data && s.processData && - ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { - s.data = s.data.replace( r20, "+" ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( jQuery.lastModified[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); - } - if ( jQuery.etag[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? - s.accepts[ s.dataTypes[ 0 ] ] + - ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && - ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { - - // Abort if not done already and return - return jqXHR.abort(); - } - - // Aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - completeDeferred.add( s.complete ); - jqXHR.done( s.success ); - jqXHR.fail( s.error ); - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - - // If request was aborted inside ajaxSend, stop there - if ( completed ) { - return jqXHR; - } - - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = window.setTimeout( function() { - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - completed = false; - transport.send( requestHeaders, done ); - } catch ( e ) { - - // Rethrow post-completion exceptions - if ( completed ) { - throw e; - } - - // Propagate others as results - done( -1, e ); - } - } - - // Callback for when everything is done - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Ignore repeat invocations - if ( completed ) { - return; - } - - completed = true; - - // Clear timeout if it exists - if ( timeoutTimer ) { - window.clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - // Determine if successful - isSuccess = status >= 200 && status < 300 || status === 304; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // Use a noop converter for missing script but not if jsonp - if ( !isSuccess && - jQuery.inArray( "script", s.dataTypes ) > -1 && - jQuery.inArray( "json", s.dataTypes ) < 0 ) { - s.converters[ "text script" ] = function() {}; - } - - // Convert no matter what (that way responseXXX fields are always set) - response = ajaxConvert( s, response, jqXHR, isSuccess ); - - // If successful, handle type chaining - if ( isSuccess ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - modified = jqXHR.getResponseHeader( "Last-Modified" ); - if ( modified ) { - jQuery.lastModified[ cacheURL ] = modified; - } - modified = jqXHR.getResponseHeader( "etag" ); - if ( modified ) { - jQuery.etag[ cacheURL ] = modified; - } - } - - // if no content - if ( status === 204 || s.type === "HEAD" ) { - statusText = "nocontent"; - - // if not modified - } else if ( status === 304 ) { - statusText = "notmodified"; - - // If we have data, let's convert it - } else { - statusText = response.state; - success = response.data; - error = response.error; - isSuccess = !error; - } - } else { - - // Extract error from statusText and normalize for non-aborts - error = statusText; - if ( status || !statusText ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = ( nativeStatusText || statusText ) + ""; - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - return jqXHR; - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - } -} ); - -jQuery.each( [ "get", "post" ], function( _i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - - // Shift arguments if data argument was omitted - if ( isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - // The url can be an options object (which then must have .url) - return jQuery.ajax( jQuery.extend( { - url: url, - type: method, - dataType: type, - data: data, - success: callback - }, jQuery.isPlainObject( url ) && url ) ); - }; -} ); - -jQuery.ajaxPrefilter( function( s ) { - var i; - for ( i in s.headers ) { - if ( i.toLowerCase() === "content-type" ) { - s.contentType = s.headers[ i ] || ""; - } - } -} ); - - -jQuery._evalUrl = function( url, options, doc ) { - return jQuery.ajax( { - url: url, - - // Make this explicit, since user can override this through ajaxSetup (trac-11264) - type: "GET", - dataType: "script", - cache: true, - async: false, - global: false, - - // Only evaluate the response if it is successful (gh-4126) - // dataFilter is not invoked for failure responses, so using it instead - // of the default converter is kludgy but it works. - converters: { - "text script": function() {} - }, - dataFilter: function( response ) { - jQuery.globalEval( response, options, doc ); - } - } ); -}; - - -jQuery.fn.extend( { - wrapAll: function( html ) { - var wrap; - - if ( this[ 0 ] ) { - if ( isFunction( html ) ) { - html = html.call( this[ 0 ] ); - } - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map( function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - } ).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( isFunction( html ) ) { - return this.each( function( i ) { - jQuery( this ).wrapInner( html.call( this, i ) ); - } ); - } - - return this.each( function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - } ); - }, - - wrap: function( html ) { - var htmlIsFunction = isFunction( html ); - - return this.each( function( i ) { - jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); - } ); - }, - - unwrap: function( selector ) { - this.parent( selector ).not( "body" ).each( function() { - jQuery( this ).replaceWith( this.childNodes ); - } ); - return this; - } -} ); - - -jQuery.expr.pseudos.hidden = function( elem ) { - return !jQuery.expr.pseudos.visible( elem ); -}; -jQuery.expr.pseudos.visible = function( elem ) { - return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); -}; - - - - -jQuery.ajaxSettings.xhr = function() { - try { - return new window.XMLHttpRequest(); - } catch ( e ) {} -}; - -var xhrSuccessStatus = { - - // File protocol always yields status code 0, assume 200 - 0: 200, - - // Support: IE <=9 only - // trac-1450: sometimes IE returns 1223 when it should be 204 - 1223: 204 - }, - xhrSupported = jQuery.ajaxSettings.xhr(); - -support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); -support.ajax = xhrSupported = !!xhrSupported; - -jQuery.ajaxTransport( function( options ) { - var callback, errorCallback; - - // Cross domain only allowed if supported through XMLHttpRequest - if ( support.cors || xhrSupported && !options.crossDomain ) { - return { - send: function( headers, complete ) { - var i, - xhr = options.xhr(); - - xhr.open( - options.type, - options.url, - options.async, - options.username, - options.password - ); - - // Apply custom fields if provided - if ( options.xhrFields ) { - for ( i in options.xhrFields ) { - xhr[ i ] = options.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( options.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( options.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Set headers - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - - // Callback - callback = function( type ) { - return function() { - if ( callback ) { - callback = errorCallback = xhr.onload = - xhr.onerror = xhr.onabort = xhr.ontimeout = - xhr.onreadystatechange = null; - - if ( type === "abort" ) { - xhr.abort(); - } else if ( type === "error" ) { - - // Support: IE <=9 only - // On a manual native abort, IE9 throws - // errors on any property access that is not readyState - if ( typeof xhr.status !== "number" ) { - complete( 0, "error" ); - } else { - complete( - - // File: protocol always yields status 0; see trac-8605, trac-14207 - xhr.status, - xhr.statusText - ); - } - } else { - complete( - xhrSuccessStatus[ xhr.status ] || xhr.status, - xhr.statusText, - - // Support: IE <=9 only - // IE9 has no XHR2 but throws on binary (trac-11426) - // For XHR2 non-text, let the caller handle it (gh-2498) - ( xhr.responseType || "text" ) !== "text" || - typeof xhr.responseText !== "string" ? - { binary: xhr.response } : - { text: xhr.responseText }, - xhr.getAllResponseHeaders() - ); - } - } - }; - }; - - // Listen to events - xhr.onload = callback(); - errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); - - // Support: IE 9 only - // Use onreadystatechange to replace onabort - // to handle uncaught aborts - if ( xhr.onabort !== undefined ) { - xhr.onabort = errorCallback; - } else { - xhr.onreadystatechange = function() { - - // Check readyState before timeout as it changes - if ( xhr.readyState === 4 ) { - - // Allow onerror to be called first, - // but that will not handle a native abort - // Also, save errorCallback to a variable - // as xhr.onerror cannot be accessed - window.setTimeout( function() { - if ( callback ) { - errorCallback(); - } - } ); - } - }; - } - - // Create the abort callback - callback = callback( "abort" ); - - try { - - // Do send the request (this may raise an exception) - xhr.send( options.hasContent && options.data || null ); - } catch ( e ) { - - // trac-14683: Only rethrow if this hasn't been notified as an error yet - if ( callback ) { - throw e; - } - } - }, - - abort: function() { - if ( callback ) { - callback(); - } - } - }; - } -} ); - - - - -// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) -jQuery.ajaxPrefilter( function( s ) { - if ( s.crossDomain ) { - s.contents.script = false; - } -} ); - -// Install script dataType -jQuery.ajaxSetup( { - accepts: { - script: "text/javascript, application/javascript, " + - "application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /\b(?:java|ecma)script\b/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -} ); - -// Handle cache's special case and crossDomain -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - } -} ); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function( s ) { - - // This transport only deals with cross domain or forced-by-attrs requests - if ( s.crossDomain || s.scriptAttrs ) { - var script, callback; - return { - send: function( _, complete ) { - script = jQuery( " + """, """ """, @@ -684,7 +684,7 @@ void checkJqueryAndImageFiles(boolean expectedOutput) { checkFiles(expectedOutput, "search.js", "jquery-ui.overrides.css", - "script-dir/jquery-3.6.1.min.js", + "script-dir/jquery-3.7.1.min.js", "script-dir/jquery-ui.min.js", "script-dir/jquery-ui.min.css", "resources/x.png", diff --git a/test/langtools/jdk/javadoc/doclet/testSearchScript/javadoc-search.js b/test/langtools/jdk/javadoc/doclet/testSearchScript/javadoc-search.js index 52c1b136035b1..660dc2b1e1b8e 100644 --- a/test/langtools/jdk/javadoc/doclet/testSearchScript/javadoc-search.js +++ b/test/langtools/jdk/javadoc/doclet/testSearchScript/javadoc-search.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ function loadIndexFiles(docsPath) { tryLoad(docsPath, "type-search-index.js"); tryLoad(docsPath, "member-search-index.js"); tryLoad(docsPath, "tag-search-index.js"); - load(docsPath + "/search.js"); + load(docsPath + "/script-files/search.js"); } function tryLoad(docsPath, file) { @@ -62,6 +62,12 @@ var $ = function(f) { f(); } else { return { + attr: function() { + return this; + }, + css: function() { + return this; + }, val: function() { return this; }, diff --git a/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java b/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java index fb46f7b2e9e94..af2da0969f169 100644 --- a/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java +++ b/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,15 +23,19 @@ /* * @test - * @bug 8017191 8182765 8200432 8239804 8250766 8262992 + * @bug 8017191 8182765 8200432 8239804 8250766 8262992 8281944 * @summary Javadoc is confused by at-link to imported classes outside of the set of generated packages - * @library ../../lib + * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool - * @build javadoc.tester.* + * @build toolbox.ToolBox javadoc.tester.* * @run main TestSeeTag */ import javadoc.tester.JavadocTester; +import toolbox.ToolBox; + +import java.io.IOException; +import java.nio.file.Path; public class TestSeeTag extends JavadocTester { @@ -105,4 +109,40 @@ public void testBadReference() { """); } + + ToolBox tb = new ToolBox(); + + @Test + public void testErroneous() throws IOException { + Path src = Path.of("erroneous", "src"); + tb.writeJavaFiles(src, """ + package erroneous; + /** + * Comment. + * @see +
      See Also:
      +
      +
        +
      • invalid input: '<a href="'
      • +
      +
      + + """); + + } } diff --git a/test/langtools/jdk/javadoc/lib/javadoc/tester/JavadocTester.java b/test/langtools/jdk/javadoc/lib/javadoc/tester/JavadocTester.java index 1dcedf87208ec..38ba81b61012e 100644 --- a/test/langtools/jdk/javadoc/lib/javadoc/tester/JavadocTester.java +++ b/test/langtools/jdk/javadoc/lib/javadoc/tester/JavadocTester.java @@ -422,12 +422,14 @@ public void javadoc(String... args) { String charsetArg = null; String docencodingArg = null; String encodingArg = null; + boolean haveSourcePath = false; for (int i = 0; i < args.length - 2; i++) { switch (args[i]) { case "-d" -> outputDir = Path.of(args[++i]); case "-charset" -> charsetArg = args[++i]; case "-docencoding" -> docencodingArg = args[++i]; case "-encoding" -> encodingArg = args[++i]; + case "-sourcepath", "--source-path", "--module-source-path" -> haveSourcePath = true; } } @@ -449,6 +451,16 @@ public void javadoc(String... args) { charset = Charset.defaultCharset(); } + // explicitly set the source path if none specified + // to override the javadoc tool default to use the classpath + if (!haveSourcePath) { + var newArgs = new String[args.length + 2]; + newArgs[0] = "-sourcepath"; + newArgs[1] = testSrc; + System.arraycopy(args, 0, newArgs, 2, args.length); + args = newArgs; + } + out.println("args: " + Arrays.toString(args)); // log.setOutDir(outputDir); diff --git a/test/langtools/jdk/javadoc/tool/CheckManPageOptions.java b/test/langtools/jdk/javadoc/tool/CheckManPageOptions.java index f876d92b7dd27..b9abd3e6d7dc9 100644 --- a/test/langtools/jdk/javadoc/tool/CheckManPageOptions.java +++ b/test/langtools/jdk/javadoc/tool/CheckManPageOptions.java @@ -54,8 +54,14 @@ * of the javadoc man page against the set of options declared in the source code. */ public class CheckManPageOptions { + static class SourceDirNotFound extends Error { } + public static void main(String... args) throws Exception { - new CheckManPageOptions().run(args); + try { + new CheckManPageOptions().run(args); + } catch (SourceDirNotFound e) { + System.err.println("NOTE: Cannot find src directory; test skipped"); + } } static final PrintStream out = System.err; @@ -150,7 +156,7 @@ Path findRootDir() { } dir = dir.getParent(); } - throw new IllegalStateException("cannot find root dir"); + throw new SourceDirNotFound(); } List getToolOptions() throws Error { diff --git a/test/langtools/jdk/javadoc/tool/api/basic/APITest.java b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java index 0e2641ca04e25..f7a5ba4fb80c5 100644 --- a/test/langtools/jdk/javadoc/tool/api/basic/APITest.java +++ b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java @@ -200,7 +200,7 @@ protected void error(String msg) { "help-doc.html", "index-all.html", "index.html", - "script-dir/jquery-3.6.1.min.js", + "script-dir/jquery-3.7.1.min.js", "script-dir/jquery-ui.min.js", "script-dir/jquery-ui.min.css", "member-search-index.js", diff --git a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/SampleApi.java b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/SampleApi.java index 84b0e2e93be30..c3ec71e3fa735 100644 --- a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/SampleApi.java +++ b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/SampleApi.java @@ -22,10 +22,6 @@ */ package sampleapi; -import com.sun.source.util.JavacTask; -import com.sun.tools.javac.api.JavacTaskImpl; -import com.sun.tools.javac.api.JavacTool; -import com.sun.tools.javac.util.Context; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; @@ -34,17 +30,22 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import java.util.Optional; -import static java.util.stream.Collectors.toList; + import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; + +import com.sun.source.util.JavacTask; +import com.sun.tools.javac.api.JavacTaskImpl; +import com.sun.tools.javac.api.JavacTool; +import com.sun.tools.javac.util.Context; + import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; -import sampleapi.generator.ModuleGenerator; +import sampleapi.generator.ModuleGenerator; import sampleapi.generator.PackageGenerator; public class SampleApi { diff --git a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/DocCommentGenerator.java b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/DocCommentGenerator.java index d13785607494d..0a97c0b752492 100644 --- a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/DocCommentGenerator.java +++ b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/DocCommentGenerator.java @@ -23,17 +23,23 @@ package sampleapi.generator; -import java.io.File; -import java.io.BufferedInputStream; -import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.Set; + import javax.lang.model.element.Modifier; import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCLiteral; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCNewArray; +import com.sun.tools.javac.tree.JCTree.JCNewClass; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; -import java.util.HashMap; -import java.util.Map; class DocCommentGenerator { diff --git a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/Documentifier.java b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/Documentifier.java index 2f7a974bb06b4..12f902cf1b856 100644 --- a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/Documentifier.java +++ b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/Documentifier.java @@ -23,22 +23,25 @@ package sampleapi.generator; -import java.util.ArrayList; import java.util.Set; + import javax.lang.model.element.Modifier; -import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.*; -import com.sun.tools.javac.tree.DocCommentTable; -import com.sun.tools.javac.parser.ScannerFactory; +import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.parser.Scanner; -import com.sun.tools.javac.parser.Tokens.Token; +import com.sun.tools.javac.parser.ScannerFactory; import com.sun.tools.javac.parser.Tokens.Comment; import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle; -import com.sun.source.tree.Tree.Kind; +import com.sun.tools.javac.parser.Tokens.Token; +import com.sun.tools.javac.tree.DocCommentTable; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.util.Context; -import sampleapi.util.*; +import sampleapi.util.PoorDocCommentTable; class Documentifier { diff --git a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/ModuleGenerator.java b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/ModuleGenerator.java index 76345638fa139..ccc32720f6ad5 100644 --- a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/ModuleGenerator.java +++ b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/ModuleGenerator.java @@ -22,31 +22,26 @@ */ package sampleapi.generator; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + import com.sun.source.tree.ModuleTree.ModuleKind; import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.parser.Scanner; -import com.sun.tools.javac.parser.ScannerFactory; -import com.sun.tools.javac.parser.Tokens; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCDirective; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.TreeMaker; -import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.ListBuffer; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.ArrayList; +import com.sun.tools.javac.util.Names; + import org.w3c.dom.Element; import org.w3c.dom.NodeList; import sampleapi.SampleApi; -import sampleapi.util.PoorDocCommentTable; - -import static com.sun.tools.javac.parser.Tokens.Comment.CommentStyle.JAVADOC; /** * diff --git a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/PackageGenerator.java b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/PackageGenerator.java index a0fcd71804f77..6576467aef8fa 100644 --- a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/PackageGenerator.java +++ b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/generator/PackageGenerator.java @@ -26,33 +26,45 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; + import javax.xml.parsers.DocumentBuilderFactory; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; import com.sun.source.util.JavacTask; -import com.sun.tools.javac.api.JavacTool; import com.sun.tools.javac.api.JavacTaskImpl; -import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.Name; -import com.sun.tools.javac.util.Names; -import com.sun.tools.javac.util.List; -import com.sun.tools.javac.util.ListBuffer; -import com.sun.tools.javac.tree.TreeMaker; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.api.JavacTool; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.code.TypeTag; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symtab; -import java.nio.file.Path; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.TypeTag; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCPackageDecl; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Name; +import com.sun.tools.javac.util.Names; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; -import sampleapi.util.*; +import sampleapi.util.SimpleMultiplier; public class PackageGenerator { diff --git a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/util/SimpleMultiplier.java b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/util/SimpleMultiplier.java index 545e671c64008..711644c061402 100644 --- a/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/util/SimpleMultiplier.java +++ b/test/langtools/jdk/javadoc/tool/sampleapi/lib/sampleapi/util/SimpleMultiplier.java @@ -23,8 +23,8 @@ package sampleapi.util; -import java.util.StringTokenizer; import java.util.ArrayList; +import java.util.StringTokenizer; /* * The class implements unknown number of nested loops. The number of modifiers diff --git a/test/langtools/jdk/jshell/AnalyzeSnippetTest.java b/test/langtools/jdk/jshell/AnalyzeSnippetTest.java index b566a023caff0..3e2e1a839e2bd 100644 --- a/test/langtools/jdk/jshell/AnalyzeSnippetTest.java +++ b/test/langtools/jdk/jshell/AnalyzeSnippetTest.java @@ -64,6 +64,7 @@ public void setUp() { state = JShell.builder() .out(new PrintStream(new ByteArrayOutputStream())) .err(new PrintStream(new ByteArrayOutputStream())) + .executionEngine(Presets.TEST_DEFAULT_EXECUTION) .build(); sca = state.sourceCodeAnalysis(); } diff --git a/test/langtools/jdk/jshell/CustomInputToolBuilder.java b/test/langtools/jdk/jshell/CustomInputToolBuilder.java index 3b3d5616a9468..523981b3d915c 100644 --- a/test/langtools/jdk/jshell/CustomInputToolBuilder.java +++ b/test/langtools/jdk/jshell/CustomInputToolBuilder.java @@ -90,7 +90,8 @@ private void doTest(boolean interactiveTerminal, String code, String... expected .interactiveTerminal(interactiveTerminal) .promptCapture(true) .persistence(new HashMap<>()) - .start("--no-startup"); + .start("--no-startup", + "--execution", Presets.TEST_DEFAULT_EXECUTION); String actual = new String(out.toByteArray()); List actualLines = Arrays.asList(actual.split("\\R")); diff --git a/test/langtools/jdk/jshell/ExceptionMessageTest.java b/test/langtools/jdk/jshell/ExceptionMessageTest.java index 9f8b53f1563b1..fe8eec5773920 100644 --- a/test/langtools/jdk/jshell/ExceptionMessageTest.java +++ b/test/langtools/jdk/jshell/ExceptionMessageTest.java @@ -26,6 +26,7 @@ * @bug 8185108 * @summary Test exception().getMessage() in events returned by eval() * @run testng ExceptionMessageTest + * @key intermittent */ import java.util.HashMap; diff --git a/test/langtools/jdk/jshell/ExecPtyGetFlagsToSetTest.java b/test/langtools/jdk/jshell/ExecPtyGetFlagsToSetTest.java index 36f966449612e..efd1cd010cdc0 100644 --- a/test/langtools/jdk/jshell/ExecPtyGetFlagsToSetTest.java +++ b/test/langtools/jdk/jshell/ExecPtyGetFlagsToSetTest.java @@ -26,7 +26,7 @@ * @bug 8224184 * @summary Control Char check for pty * @modules jdk.internal.le/jdk.internal.org.jline.terminal - * jdk.internal.le/jdk.internal.org.jline.terminal.impl + * jdk.internal.le/jdk.internal.org.jline.terminal.impl.exec * jdk.internal.le/jdk.internal.org.jline.terminal.spi * @requires (os.family == "linux") | (os.family == "aix") */ @@ -35,12 +35,13 @@ import jdk.internal.org.jline.terminal.Attributes; import jdk.internal.org.jline.terminal.Attributes.ControlChar; import jdk.internal.org.jline.terminal.Attributes.LocalFlag; -import jdk.internal.org.jline.terminal.impl.ExecPty; +import jdk.internal.org.jline.terminal.impl.exec.ExecPty; +import jdk.internal.org.jline.terminal.spi.SystemStream; import jdk.internal.org.jline.terminal.spi.TerminalProvider; public class ExecPtyGetFlagsToSetTest extends ExecPty { - public ExecPtyGetFlagsToSetTest(String name, TerminalProvider.Stream stream) { - super(name, stream); + public ExecPtyGetFlagsToSetTest(TerminalProvider provider, SystemStream stream, String name) { + super(provider, stream, name); } @Override @@ -50,7 +51,7 @@ protected List getFlagsToSet(Attributes attr, Attributes current) { public static void main(String[] args) { ExecPtyGetFlagsToSetTest testPty = - new ExecPtyGetFlagsToSetTest("stty", TerminalProvider.Stream.Output); + new ExecPtyGetFlagsToSetTest(null, SystemStream.Output, "stty"); Attributes attr = new Attributes(); Attributes current = new Attributes(); diff --git a/test/langtools/jdk/jshell/ExecutionControlTestBase.java b/test/langtools/jdk/jshell/ExecutionControlTestBase.java index 9035d84d4a56f..20336c902cffc 100644 --- a/test/langtools/jdk/jshell/ExecutionControlTestBase.java +++ b/test/langtools/jdk/jshell/ExecutionControlTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,28 +25,14 @@ import org.testng.annotations.Test; import jdk.jshell.VarSnippet; -import java.net.InetAddress; import static jdk.jshell.Snippet.Status.VALID; import static jdk.jshell.Snippet.SubKind.*; public class ExecutionControlTestBase extends KullaTesting { - String standardListenSpec() { - String loopback = InetAddress.getLoopbackAddress().getHostAddress(); - return "jdi:hostname(" + loopback + ")"; - } - - String standardLaunchSpec() { - return "jdi:launch(true)"; - } - - String standardJdiSpec() { - return "jdi"; - } - - String standardSpecs() { - return "5(" + standardListenSpec() + "), 6(" + standardLaunchSpec() + "), 7(" + standardJdiSpec() + ")"; + String alwaysPassingSpec() { + return "5(local)"; } @Test diff --git a/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java b/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java index da838798f8ee3..a094ed4a86f3d 100644 --- a/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java +++ b/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -129,9 +129,7 @@ public void setUp() { Map pm = provider.defaultParameters(); pm.put("0", "alwaysFailing"); pm.put("1", "alwaysFailing"); - pm.put("2", standardListenSpec()); - pm.put("3", standardLaunchSpec()); - pm.put("4", standardJdiSpec()); + pm.put("2", "local"); setUp(builder -> builder.executionEngine(provider, pm)); } @@ -159,9 +157,7 @@ public void variables() { assertTrue(log.contains("This operation intentionally broken"), log); log = logged.get(Level.FINEST).get(0); assertTrue( - log.contains("Success failover -- 2 = " + standardListenSpec()) - || log.contains("Success failover -- 3 = " + standardLaunchSpec()) - || log.contains("Success failover -- 4 = " + standardJdiSpec()), + log.contains("Success failover -- 2 = local"), log); } } diff --git a/test/langtools/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java b/test/langtools/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java index f3218fab7c76d..31011960880dd 100644 --- a/test/langtools/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java +++ b/test/langtools/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,6 +43,6 @@ public class FailOverExecutionControlDyingLaunchTest extends ExecutionControlTes public void setUp() { setUp(builder -> builder.executionEngine( "failover:0(jdi:remoteAgent(DyingRemoteAgent),launch(true)), " - + standardSpecs())); + + alwaysPassingSpec())); } } diff --git a/test/langtools/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java b/test/langtools/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java index 778d004915c20..9958b7a3284e6 100644 --- a/test/langtools/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java +++ b/test/langtools/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,6 @@ public class FailOverExecutionControlHangingLaunchTest extends ExecutionControlT public void setUp() { setUp(builder -> builder.executionEngine( "failover:0(jdi:remoteAgent(HangingRemoteAgent),launch(true)), " - + standardSpecs())); + + alwaysPassingSpec())); } } diff --git a/test/langtools/jdk/jshell/FailOverExecutionControlHangingListenTest.java b/test/langtools/jdk/jshell/FailOverExecutionControlHangingListenTest.java index f22dd821f4049..4f29bfe9c7a82 100644 --- a/test/langtools/jdk/jshell/FailOverExecutionControlHangingListenTest.java +++ b/test/langtools/jdk/jshell/FailOverExecutionControlHangingListenTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,6 +45,6 @@ public void setUp() { String loopback = InetAddress.getLoopbackAddress().getHostAddress(); setUp(builder -> builder.executionEngine( "failover:0(jdi:remoteAgent(HangingRemoteAgent),hostname(" + loopback + "))," - + standardSpecs())); + + alwaysPassingSpec())); } } diff --git a/test/langtools/jdk/jshell/FailOverExecutionControlTest.java b/test/langtools/jdk/jshell/FailOverExecutionControlTest.java index 0843351815f87..80dc56d72c484 100644 --- a/test/langtools/jdk/jshell/FailOverExecutionControlTest.java +++ b/test/langtools/jdk/jshell/FailOverExecutionControlTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ public class FailOverExecutionControlTest extends ExecutionControlTestBase { @Override public void setUp() { setUp(builder -> builder.executionEngine("failover:0(expectedFailureNonExistent1), 1(expectedFailureNonExistent2), " - + standardSpecs())); + + alwaysPassingSpec())); } } diff --git a/test/langtools/jdk/jshell/IdGeneratorTest.java b/test/langtools/jdk/jshell/IdGeneratorTest.java index 23727aef643a8..e8a38dfe7f05f 100644 --- a/test/langtools/jdk/jshell/IdGeneratorTest.java +++ b/test/langtools/jdk/jshell/IdGeneratorTest.java @@ -53,7 +53,8 @@ public JShell.Builder getBuilder() { return JShell.builder() .in(inStream) .out(new PrintStream(outStream)) - .err(new PrintStream(errStream)); + .err(new PrintStream(errStream)) + .executionEngine(Presets.TEST_DEFAULT_EXECUTION); } public void testTempNameGenerator() { diff --git a/test/langtools/jdk/jshell/KullaTesting.java b/test/langtools/jdk/jshell/KullaTesting.java index 369b2ed1f44a2..d74f3484f4bd8 100644 --- a/test/langtools/jdk/jshell/KullaTesting.java +++ b/test/langtools/jdk/jshell/KullaTesting.java @@ -100,7 +100,9 @@ public class KullaTesting { private Set allSnippets = new LinkedHashSet<>(); static { - JShell js = JShell.create(); + JShell js = JShell.builder() + .executionEngine(Presets.TEST_DEFAULT_EXECUTION) + .build(); MAIN_SNIPPET = js.eval("MAIN_SNIPPET").get(0).snippet(); js.close(); assertTrue(MAIN_SNIPPET != null, "Bad MAIN_SNIPPET set-up -- must not be null"); @@ -192,7 +194,8 @@ public int read(byte[] b, int off, int len) throws IOException { JShell.Builder builder = JShell.builder() .in(in) .out(new PrintStream(outStream)) - .err(new PrintStream(errStream)); + .err(new PrintStream(errStream)) + .executionEngine(Presets.TEST_DEFAULT_EXECUTION); bc.accept(builder); state = builder.build(); allSnippets = new LinkedHashSet<>(); diff --git a/test/langtools/jdk/jshell/Presets.java b/test/langtools/jdk/jshell/Presets.java new file mode 100644 index 0000000000000..b9a93c967dc71 --- /dev/null +++ b/test/langtools/jdk/jshell/Presets.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.net.InetAddress; +import java.util.*; + +public class Presets { + public static final String TEST_DEFAULT_EXECUTION; + public static final String TEST_STANDARD_EXECUTION; + + static { + String loopback = InetAddress.getLoopbackAddress().getHostAddress(); + + TEST_DEFAULT_EXECUTION = "failover:0(jdi:hostname(" + loopback + "))," + + "1(jdi:launch(true)), 2(jdi), 3(local)"; + TEST_STANDARD_EXECUTION = "failover:0(jdi:hostname(" + loopback + "))," + + "1(jdi:launch(true)), 2(jdi)"; + } + + public static String[] addExecutionIfMissing(String[] args) { + if (Arrays.stream(args).noneMatch(Presets::remoteRelatedOption)) { + List augmentedArgs = new ArrayList<>(); + + augmentedArgs.add("--execution"); + augmentedArgs.add(Presets.TEST_DEFAULT_EXECUTION); + augmentedArgs.addAll(List.of(args)); + + return augmentedArgs.toArray(s -> new String[s]); + } + + return args; + } + + private static boolean remoteRelatedOption(String option) { + return "--execution".equals(option) || + "--add-modules".equals(option) || + option.startsWith("-R"); + } +} diff --git a/test/langtools/jdk/jshell/ReplToolTesting.java b/test/langtools/jdk/jshell/ReplToolTesting.java index 6e0192e3aa74e..1e9f9942f4241 100644 --- a/test/langtools/jdk/jshell/ReplToolTesting.java +++ b/test/langtools/jdk/jshell/ReplToolTesting.java @@ -293,7 +293,7 @@ protected JavaShellToolBuilder builder(Locale locale) { private void testRaw(Locale locale, String[] args, ReplTest... tests) { testRawInit(tests); - testRawRun(locale, args); + testRawRun(locale, Presets.addExecutionIfMissing(args)); testRawCheck(locale); } diff --git a/test/langtools/jdk/jshell/StartOptionTest.java b/test/langtools/jdk/jshell/StartOptionTest.java index df445d49750a8..aa8d9be03a9ef 100644 --- a/test/langtools/jdk/jshell/StartOptionTest.java +++ b/test/langtools/jdk/jshell/StartOptionTest.java @@ -81,7 +81,7 @@ private JavaShellToolBuilder builder() { protected int runShell(String... args) { try { return builder() - .start(args); + .start(Presets.addExecutionIfMissing(args)); } catch (Exception ex) { fail("Repl tool died with exception", ex); } diff --git a/test/langtools/jdk/jshell/ToolReloadTest.java b/test/langtools/jdk/jshell/ToolReloadTest.java index 13d583e51f5c1..4709584cd1268 100644 --- a/test/langtools/jdk/jshell/ToolReloadTest.java +++ b/test/langtools/jdk/jshell/ToolReloadTest.java @@ -201,7 +201,7 @@ public void testReloadCrashRestore() { } public void testEnvBadModule() { - test( + test(new String[] {"--execution", Presets.TEST_STANDARD_EXECUTION}, (a) -> assertVariable(a, "int", "x", "5", "5"), (a) -> assertMethod(a, "int m(int z) { return z * z; }", "(int)int", "m"), diff --git a/test/langtools/jdk/jshell/UITesting.java b/test/langtools/jdk/jshell/UITesting.java index 848c0d52797a9..477e540e71632 100644 --- a/test/langtools/jdk/jshell/UITesting.java +++ b/test/langtools/jdk/jshell/UITesting.java @@ -93,7 +93,8 @@ protected void doRunTest(Test test) throws Exception { .promptCapture(true) .persistence(new HashMap<>()) .locale(Locale.US) - .run("--no-startup"); + .run("--no-startup", + "--execution", Presets.TEST_DEFAULT_EXECUTION); } catch (Exception ex) { throw new IllegalStateException(ex); } diff --git a/test/langtools/tools/javac/Paths/Class-Path.sh b/test/langtools/tools/javac/Paths/Class-Path.sh deleted file mode 100644 index db4957366daba..0000000000000 --- a/test/langtools/tools/javac/Paths/Class-Path.sh +++ /dev/null @@ -1,198 +0,0 @@ -#!/bin/sh - -# -# Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - - -# @test -# @bug 4212732 -# @summary Test handling of the Class-Path attribute in jar file manifests -# @author Martin Buchholz -# -# @run shell Class-Path.sh - -# To run this test manually, simply do ./Class-Path.sh - -. ${TESTSRC-.}/Util.sh - -set -u - -Cleanup() { - Sys rm -rf pkg Main.java Main.class Main.jar jars - Sys rm -rf MANIFEST.MF A.jar B.zip -} - -Cleanup -Sys mkdir pkg - -#---------------------------------------------------------------- -# Create mutually referential jar files -#---------------------------------------------------------------- -cat >pkg/A.java <pkg/B.java <Main.java <> MANIFEST.MF -Sys "$jar" cmf MANIFEST.MF Main.jar Main.class - -Success "$java" ${TESTVMOPTS} -jar Main.jar - -MkManifestWithClassPath "." -Sys "$jar" cmf MANIFEST.MF A.jar pkg/A.class - -Success "$javac" ${TESTTOOLVMOPTS} -cp "A.jar" Main.java -Success "$java" ${TESTVMOPTS} -jar Main.jar - -MkManifestWithClassPath "" -Sys "$jar" cmf MANIFEST.MF A.jar pkg/A.class - -Failure "$javac" ${TESTTOOLVMOPTS} -cp "A.jar" Main.java -Failure "$java" ${TESTVMOPTS} -jar Main.jar - -#---------------------------------------------------------------- -# Test new flag -e (application entry point) -#---------------------------------------------------------------- - -cat > Hello.java < Bye.java <> MANIFEST.MF - -# test for error: " 'e' flag and manifest with the 'Main-Class' -# attribute cannot be specified together, during creation -Failure "$jar" cmfe MANIFEST.MF "Bye.jar" "Bye" Bye.class - -# test for overriding the manifest when updating the jar -# -Success "$jar" cfe "greetings.jar" "Hello" Hello.class -Success "$jar" ufe "greetings.jar" "Bye" Bye.class -Success "$java" ${TESTVMOPTS} -jar greetings.jar - -# test for error: " 'e' flag and manifest with the 'Main-Class' -# attribute cannot be specified together, during update -Failure "$jar" umfe MANIFEST.MF "greetings.jar" "Hello" - -# test jar updation when there are no inputfiles -# -Success "$jar" ufe "Hello.jar" "Bye" -Failure "$java" ${TESTVMOPTS} -jar Hello.jar -Success "$jar" umf MANIFEST.MF "Hello.jar" - -# test creating jar when the to-be-archived files -# do not contain the specified main class, there is no check done -# for the presence of the main class, so the test will pass -# -Success "$jar" cfe "Hello.jar" "Hello" Bye.class - -# Jar creation and update when there is no manifest and inputfiles -# specified -Failure "$jar" cvf "A.jar" -Failure "$jar" uvf "A.jar" - -# error: no such file or directory -Failure "$jar" cvf "A.jar" non-existing.file -Failure "$jar" uvf "A.jar" non-existing.file - -Cleanup - -Bottom Line diff --git a/test/langtools/tools/javac/Paths/Class-Path2.sh b/test/langtools/tools/javac/Paths/Class-Path2.sh deleted file mode 100644 index fc37c6fdbfc57..0000000000000 --- a/test/langtools/tools/javac/Paths/Class-Path2.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# @test -# @bug 4212732 6485027 -# @summary Test handling of the Class-Path attribute in jar file manifests -# @author Martin Buchholz -# -# @run shell Class-Path2.sh - -# To run this test manually, simply do ./Class-Path2.sh - -. ${TESTSRC-.}/Util.sh - -set -u - -Cleanup() { - Sys rm -rf pkg Main.java Main.class Main.jar jars - Sys rm -rf MANIFEST.MF A.jar B.zip -} - -Cleanup -Sys mkdir pkg - -#---------------------------------------------------------------- -# Create mutually referential jar files -#---------------------------------------------------------------- -cat >pkg/A.java <pkg/B.java <Main.java <&1`; rc="$?" - output2=`echo "$output" | grep -v "bootstrap class path not set in conjunction with -source"` - test -n "$output2" && echo "$output" - test $rc -eq 0 || Fail "Command \"$*\" failed with exitValue $rc"; - case "$output2" in *warning:*) gotwarning="yes";; *) gotwarning="no";; esac - - if test "$gotwarning" = "yes" -a "$NO" = "no"; then - Fail "Command \"$*\" printed an unexpected warning" - elif test "$gotwarning" = "no" -a "$NO" != "no"; then - Fail "Command \"$*\" did not generate the expected warning" - fi -} - -Error() { - HorizontalRule - echo "$@" - output=`"$@" 2>&1`; rc="$?" - test -n "$output" && echo "$output" - case "$output" in *error:*) goterror="yes";; *) goterror="no";; esac - - if test "$NO" = "no"; then - test "$rc" -ne 0 && \ - Fail "Command \"$*\" failed with return code $rc" - test "$goterror" = "yes" && \ - Fail "Command \"$*\" did not generate any error message" - else - test "$rc" -eq 0 && \ - Fail "Command \"$*\" was supposed to Die with fatal error"; - test "$goterror" = "no" && \ - Fail "Command \"$*\" printed an unexpected error message" - fi -} - -Cleanup() { - Sys rm -rf Main.java Main.class - Sys rm -rf classes classes.foo classes.jar classes.war classes.zip - Sys rm -rf MANIFEST.MF classesRef.jar classesRefRef.jar jars -} - -Cleanup -echo "public class Main{public static void main(String[]a){}}" > Main.java - -# We need to set -source 8 -target 8 for those cases where the option is -# not legal in 9 and later. However, that triggers an additional warning -# about not setting bootclasspath, which is filtered out in Warning. -# The alternative would be to extract a minimal rt.jar from JDK and -# specify that with -bootclasspath. -SRCTRG8="-source 8 -target 8" - -#---------------------------------------------------------------- -# No warnings unless -Xlint:path is used -#---------------------------------------------------------------- -No Warning "$javac" ${TESTTOOLVMOPTS} Main.java -No Warning "$javac" ${TESTTOOLVMOPTS} -cp ".${PS}classes" Main.java - -#---------------------------------------------------------------- -# Warn for missing elts in user-specified paths -#---------------------------------------------------------------- -Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path -cp ".${PS}classes" Main.java -Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint:path "-Xbootclasspath/p:classes" Main.java -Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint "-Xbootclasspath/a:classes" Main.java - -Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint:path "-endorseddirs" "classes" Main.java -Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint "-extdirs" "classes" Main.java -#Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint:path "-Xbootclasspath:classes${PS}${BCP}" Main.java - -#---------------------------------------------------------------- -# No warning for missing elts in "system" paths -#---------------------------------------------------------------- -# No Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path "-J-Djava.endorsed.dirs=classes" Main.java -# No Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path "-J-Djava.ext.dirs=classes" Main.java -# No Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path "-J-Xbootclasspath/p:classes" Main.java -# No Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path "-J-Xbootclasspath/a:classes" Main.java -# No Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path "-J-Xbootclasspath:classes${PS}${BCP}" Main.java - -#---------------------------------------------------------------- -# No warning if class path element exists -#---------------------------------------------------------------- -Sys mkdir classes -No Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path -cp ".${PS}classes" Main.java -No Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint:path "-endorseddirs" "classes" Main.java -No Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint:path "-extdirs" "classes" Main.java -No Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint:path "-Xbootclasspath/p:classes" Main.java -No Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint:path "-Xbootclasspath/a:classes" Main.java -#No Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint:path "-Xbootclasspath:classes${PS}${BCP}" Main.java - -Sys "$jar" cf classes.jar Main.class -Sys cp classes.jar classes.war -Sys cp classes.war classes.zip -No Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path -cp ".${PS}classes.jar" Main.java - Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path -cp ".${PS}classes.war" Main.java -No Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path -cp ".${PS}classes.zip" Main.java - -#---------------------------------------------------------------- -# Warn if -Xlint is used and if class path element refers to -# regular file which doesn't look like a zip file, but is -#---------------------------------------------------------------- -Sys cp classes.war classes.foo - Warning "$javac" ${TESTTOOLVMOPTS} -Xlint:path -cp ".${PS}classes.foo" Main.java - - -#---------------------------------------------------------------- -# No error if class path element refers to regular file which is -# not a zip file -#---------------------------------------------------------------- -No Error "$javac" ${TESTTOOLVMOPTS} -cp Main.java Main.java # Main.java is NOT a jar file -No Error "$javac" ${TESTTOOLVMOPTS} Main.java - -#---------------------------------------------------------------- -# Warn if -Xlint is used and if class path element refers to -# regular file which is not a zip file -#---------------------------------------------------------------- -Warning "$javac" ${TESTTOOLVMOPTS} -Xlint -cp Main.java Main.java # Main.java is NOT a jar file - -#---------------------------------------------------------------- -# Test jar file class path reference recursion -#---------------------------------------------------------------- -MkManifestWithClassPath classesRef.jar -Sys "$jar" cmf MANIFEST.MF classesRefRef.jar Main.class - -#---------------------------------------------------------------- -# Non-existent recursive Class-Path reference gives warning -#---------------------------------------------------------------- -No Warning "$javac" ${TESTTOOLVMOPTS} -classpath classesRefRef.jar Main.java - Warning "$javac" ${TESTTOOLVMOPTS} -Xlint -classpath classesRefRef.jar Main.java -No Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint -Xbootclasspath/p:classesRefRef.jar Main.java - -BadJarFile classesRef.jar - -#---------------------------------------------------------------- -# Non-jar file recursive Class-Path reference gives error -#---------------------------------------------------------------- - Error "$javac" ${TESTTOOLVMOPTS} -classpath classesRefRef.jar Main.java -No Error "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xbootclasspath/a:classesRefRef.jar Main.java - -MkManifestWithClassPath classes -Sys "$jar" cmf MANIFEST.MF classesRef.jar Main.class - -#---------------------------------------------------------------- -# Jar file recursive Class-Path reference is OK -#---------------------------------------------------------------- -No Warning "$javac" ${TESTTOOLVMOPTS} -Xlint -classpath classesRefRef.jar Main.java -No Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint -Xbootclasspath/p:classesRefRef.jar Main.java - -#---------------------------------------------------------------- -# Class-Path attribute followed in extdirs or endorseddirs -#---------------------------------------------------------------- -Sys mkdir jars -Sys cp classesRefRef.jar jars/. - Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint -extdirs jars Main.java - Warning "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint -endorseddirs jars Main.java - -#---------------------------------------------------------------- -# Bad Jar file in extdirs and endorseddirs should not be ignored -#---------------------------------------------------------------- -BadJarFile jars/classesRef.jar - Error "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint -extdirs jars Main.java - Error "$javac" ${TESTTOOLVMOPTS} ${SRCTRG8} -Xlint -endorseddirs jars Main.java - -Cleanup - -Bottom Line diff --git a/test/langtools/tools/javac/Paths/Help.java b/test/langtools/tools/javac/Paths/Help.java new file mode 100644 index 0000000000000..af1e0ff32dd7c --- /dev/null +++ b/test/langtools/tools/javac/Paths/Help.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * @test + * @bug 4940642 8293877 + * @summary Check for -help and -X flags + */ + +/* + * Converted from Help.sh, originally written by Martin Buchholz + * + * For the last version of the original, Help.sh, see + * https://git.openjdk.org/jdk/blob/jdk-19%2B36/test/langtools/tools/javac/Paths/Help.sh + * + * This class provides rudimentary tests of the javac command-line help. + */ + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.spi.ToolProvider; + +public class Help { + public static void main(String... args) throws Exception { + new Help().run(args); + } + + void run(String... args) throws Exception { + String helpText = javac("-help"); + check(helpText, + "-X ", "-J", "-classpath ", "-cp ", "-bootclasspath ", "-sourcepath "); + + String xText = javac("-X"); + check(xText, "-Xbootclasspath/p:"); + } + + void check(String text, String... options) throws Exception { + for (String opt : options) { + System.err.println("Checking '" + opt + "'"); + if (!text.contains(opt)) { + text.lines().forEach(System.err::println); + throw new Exception("Bad help output"); + } + } + } + + String javac(String... args) throws Exception { + var javac = ToolProvider.findFirst("javac") + .orElseThrow(() -> new Exception("cannot find javac")); + try (StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw)) { + int rc = javac.run(pw, pw, args); + if (rc != 0) { + throw new Error("unexpected exit from javac: " + rc); + } + pw.flush(); + return sw.toString(); + } + } +} + diff --git a/test/langtools/tools/javac/Paths/Help.sh b/test/langtools/tools/javac/Paths/Help.sh deleted file mode 100644 index 9453622a116ef..0000000000000 --- a/test/langtools/tools/javac/Paths/Help.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/sh - -# -# Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# @test -# @bug 4940642 -# @summary Check for -help and -X flags -# @author Martin Buchholz -# -# @run shell Help.sh - -# To run this test manually, simply do ./MineField.sh - - -. ${TESTSRC-.}/Util.sh - -set -u - -DiagnosticsInEnglishPlease - -HELP="`\"$javac\" ${TESTTOOLVMOPTS} -help 2>&1`" -XHELP="`\"$javac\" ${TESTTOOLVMOPTS} -X 2>&1`" - -#---------------------------------------------------------------- -# Standard options -#---------------------------------------------------------------- -for opt in \ - "-X " \ - "-J" \ - "-classpath " \ - "-cp " \ - "-bootclasspath " \ - "-sourcepath "; -do - case "$HELP" in *"$opt"*) ;; *) Fail "Bad help output" ;; esac -done - -#---------------------------------------------------------------- -# Non-standard options -#---------------------------------------------------------------- -for opt in \ - "-Xbootclasspath/p:"; -do - case "$XHELP" in *"$opt"*) ;; *) Fail "Bad help output" ;; esac -done - -Bottom Line diff --git a/test/langtools/tools/javac/Paths/MineField.java b/test/langtools/tools/javac/Paths/MineField.java new file mode 100644 index 0000000000000..ee8ead0002743 --- /dev/null +++ b/test/langtools/tools/javac/Paths/MineField.java @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * @test + * @bug 4758537 4809833 8149599 8293877 + * @summary Test that javac and java find files in similar ways + * @library /tools/lib + * @build toolbox.ToolBox Util MineField + * @run main MineField + */ + +/* + * Converted from MineField.sh, originally written by Martin Buchholz. + * + * For the last version of the original, MineField.sh, see + * https://git.openjdk.org/jdk/blob/jdk-19%2B36/test/langtools/tools/javac/Paths/MineField.sh + * + * This class primarily tests that javac and the java launcher provide + * equivalent handling of all path-related options, like {@code -classpath}. + */ + +/* +#---------------------------------------------------------------- +# The search order for classes used by both java and javac is: +# +# -Xbootclasspath/p: +# -endorseddirs or -Djava.endorsed.dirs= (search for jar/zip only) +# -bootclasspath or -Xbootclasspath: +# -Xbootclasspath/a: +# -extdirs or -Djava.ext.dirs= (search for jar/zip only) +# -classpath , -cp , env CLASSPATH= +# +# Peculiarities of the class file search: +# - Empty elements of the (user) classpath default to ".", +# while empty elements of other paths are ignored. +# - Only for the user classpath is an empty string value equivalent to "." +# - Specifying a bootclasspath on the command line obliterates any +# previous -Xbootclasspath/p: or -Xbootclasspath/a: command line flags. +# +# JDK 9 update: +# java: The java launcher does not support any of the following: +# * -Xbootclasspath/p: -Xbootclasspath: +# * -endorseddirs -Djava.endorsed.dirs +# * -extdirs -Djava.ext.dirs +# All test cases exercising these features have been removed. +# javac: The following features are only supported when compiling +# for older releases: +# * -Xbootclasspath/p: -Xbootclasspath: -bootclasspath -Xbootclasspath/a: +# * -endorseddirs -Djava.endorsed.dirs +# * -extdirs -Djava.ext.dirs +# All test cases exercising these features have been modified to +# use -source 8 -target 8. In addition, javac test cases involving +# use of the runtime properties java.endorsed.dirs and java.extdirs +# (by means of -J-Dname=value) have been removed. +# Although the primary purpose of the test cases in this file is to +# compare javac and java behavior, some tests remain for javac for +# which there is no java equivalent. However, the cases remain as useful +# test cases for javac handling of the paths involved. + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class MineField extends Util { + public static void main(String... args) throws Exception { + new MineField().run(args); + } + + void run(String... args) throws Exception{ + setup(); + tests(); + cleanup(); + bottomLine(); + } + + void cleanup() throws IOException { + deleteFiles("GooSrc", "GooJar", "GooZip", "GooClass"); + deleteFiles("BadSrc", "BadJar", "BadZip", "BadClass"); + deleteFiles("OneDir", "Main.java", "MANIFEST.MF"); + deleteFiles(listFiles(Path.of("."), "*.class")); + deleteFiles("java-lang.jar"); + } + + /** + * "Prepare the minefield". + */ + void setup() throws Exception { + cleanup(); + + tb.createDirectories("GooSrc", "GooJar", "GooZip", "GooClass"); + tb.createDirectories("BadSrc", "BadJar", "BadZip", "BadClass"); + + Files.writeString(Path.of("Lib.java"), + "public class Lib {public static void f(){}}"); + javac("Lib.java"); + jar("cf", "GooJar/Lib.jar", "Lib.class"); + jar("cf", "GooZip/Lib.zip", "Lib.class"); + tb.moveFile("Lib.class", "GooClass/."); + tb.moveFile("Lib.java", "GooSrc/."); + checkFiles("GooZip/Lib.zip", "GooJar/Lib.jar", "GooSrc/Lib.java"); + + Files.writeString(Path.of("Lib.java"), + "public class Lib {/* Bad */}"); + javac("Lib.java"); + jar("cf", "BadJar/Lib.jar", "Lib.class"); + jar("cf", "BadZip/Lib.zip", "Lib.class"); + tb.moveFile("Lib.class", "BadClass/."); + tb.moveFile("Lib.java", "BadSrc/."); + checkFiles("BadZip/Lib.zip", "BadJar/Lib.jar", "BadSrc/Lib.java"); + + Files.writeString(Path.of("Main.java"), + "public class Main {public static void main(String[] a) {Lib.f();}}"); + Path libModules = javaHome.resolve("lib").resolve("modules"); + if (Files.isReadable(libModules)) { + jimage("extract", "--dir", "modules", libModules.toString()); + jar("cf", "java-lang.jar", "-C", "modules/java.base", "java/lang"); + deleteFiles("modules"); + } else { + Path modules = javaHome.resolve("modules"); + if (Files.isDirectory(modules)) { + jar("cf", "java-lang.jar", "-C", modules.resolve("java.base").toString(), "java/lang"); + } else { + throw new Exception("Cannot create java-lang.jar"); + } + } + } + + void tests() throws Exception { + + //---------------------------------------------------------------- + // Verify that javac class search order is the same as java's + //---------------------------------------------------------------- + + expectFail(JAVAC, """ + -source 8 -target 8 + -Xbootclasspath/p:GooClass + -bootclasspath java-lang.jar${PS}BadZip/Lib.zip + Main.java"""); + + expectPass(JAVAC, """ + -source 8 -target 8 + -Xbootclasspath/p:BadClass${PS}GooClass + -bootclasspath java-lang.jar${PS}GooZip/Lib.zip${PS}BadClass + Main.java"""); + + expectPass(JAVAC, """ + -source 8 -target 8 + -Xbootclasspath/p:BadJar/Lib.jar + -Xbootclasspath:java-lang.jar${PS}GooClass + Main.java"""); + + //---------------------------------------------------------------- + + expectFail(JAVAC, """ + -source 8 -target 8 + -bootclasspath java-lang.jar${PS}GooZip/Lib.zip + -Xbootclasspath/p:BadClass + Main.java"""); + + expectPass(JAVAC, """ + -source 8 -target 8 + -bootclasspath java-lang.jar${PS}BadZip/Lib.zip + -Xbootclasspath/p:GooClass${PS}BadJar/Lib.jar + Main.java"""); + + //---------------------------------------------------------------- + + expectFail(JAVAC, """ + -source 8 -target 8 + -Xbootclasspath/p:BadClass + -Xbootclasspath/a:GooClass + Main.java"""); + + expectPass(JAVAC, """ + -source 8 -target 8 + -Xbootclasspath/p:GooClass${PS}BadClass + -Xbootclasspath/a:BadClass + Main.java"""); + + expectPass(JAVA, """ + -Xbootclasspath/a:GooClass + Main"""); + + //---------------------------------------------------------------- + + expectFail(JAVAC, """ + -source 8 -target 8 + -Xbootclasspath/p:GooClass + -Xbootclasspath:BadClass${PS}java-lang.jar + -Xbootclasspath/a:GooClass + Main.java"""); + + expectPass(JAVAC, """ + -source 8 -target 8 + -Xbootclasspath/p:BadClass + -Xbootclasspath:GooClass${PS}BadClass${PS}java-lang.jar + -Xbootclasspath/a:BadClass + Main.java"""); + + //---------------------------------------------------------------- + + expectPass(JAVAC, """ + -source 8 -target 8 + -endorseddirs BadClass${PS}GooZip${PS}BadJar + -Xbootclasspath:"BadClass${PS}java-lang.jar + Main.java"""); + + expectPass(JAVAC, """ + -source 8 -target 8 + -Djava.endorsed.dirs=BadClass${PS}GooZip${PS}BadJar + -Xbootclasspath:BadClass${PS}java-lang.jar + Main.java"""); + + //---------------------------------------------------------------- + + expectFail(JAVAC, """ + -source 8 -target 8 + -Xbootclasspath/a:BadClass + -extdirs GooZip + Main.java"""); + + expectPass(JAVAC, """ + -source 8 -target 8 + -Xbootclasspath/a:GooClass${PS}BadClass + -extdirs BadZip + Main.java"""); + + //---------------------------------------------------------------- + + expectFail(JAVAC, """ + -source 8 -target 8 + -extdirs GooClass${PS}BadZip + -cp GooZip/Lib.zip + Main.java"""); + + expectPass(JAVAC, """ + -source 8 -target 8 + -extdirs BadClass${PS}GooZip${PS}BadJar + -cp BadZip/Lib.zip + Main.java"""); + + expectPass(JAVAC, """ + -source 8 -target 8 + -Djava.ext.dirs=GooZip${PS}BadJar + -classpath BadZip/Lib.zip + Main.java"""); + + //---------------------------------------------------------------- + + expectFail(JAVAC, "-classpath BadClass${PS}GooClass Main.java"); + expectPass(JAVAC, "-classpath GooClass${PS}BadClass Main.java"); + expectFail(JAVA, "-classpath BadClass${PS}GooClass${PS}. Main"); + expectPass(JAVA, "-classpath GooClass${PS}BadClass${PS}. Main"); + + expectFail(JAVAC, "-cp BadJar/Lib.jar${PS}GooZip/Lib.zip Main.java"); + expectPass(JAVAC, "-cp GooJar/Lib.jar${PS}BadZip/Lib.zip Main.java"); + expectFail(JAVA, "-cp BadJar/Lib.jar${PS}${PS}GooZip/Lib.zip Main"); + expectPass(JAVA, "-cp GooJar/Lib.jar${PS}${PS}BadZip/Lib.zip Main"); + + //---------------------------------------------------------------- + + expectFail(classpath("BadZip/Lib.zip${PS}GooJar/Lib.jar"), JAVAC,"Main.java"); + expectPass(classpath("GooZip/Lib.zip${PS}BadJar/Lib.jar"), JAVAC, "Main.java"); + expectFail(classpath("${PS}BadZip/Lib.zip${PS}GooJar/Lib.jar"), JAVA, "Main"); + expectPass(classpath("${PS}GooZip/Lib.zip${PS}BadJar/Lib.jar"), JAVA, "Main"); + + //---------------------------------------------------------------- + // Check behavior of empty paths and empty path elements + //---------------------------------------------------------------- + + Path GooClass = Path.of("GooClass"); + Path GooJar = Path.of("GooJar"); + + expectFail(GooClass, JAVAC, "-cp .. ../Main.java"); + expectFail(GooClass, JAVA, "-cp .. Main"); + + // Unspecified classpath defaults to "." + Path OneDir = Path.of("OneDir"); + tb.createDirectories(OneDir); + tb.copyFile(Path.of("Main.java"), OneDir); + tb.copyFile(GooClass.resolve("Lib.class"), OneDir); + expectPass(OneDir, JAVAC, "Main.java"); + expectPass(OneDir, JAVA, "Main"); + + // Empty classpath elements mean "." + expectPass(GooClass, JAVAC, "-cp ${PS}.. ../Main.java"); + expectPass(GooClass, JAVA, "-cp ${PS}.. Main"); + + expectPass(GooClass, JAVAC, "-cp ..${PS} ../Main.java"); + expectPass(GooClass, JAVA, "-cp ..${PS} Main"); + + expectPass(GooClass, JAVAC, "-cp ..${PS}${PS}/xyzzy ../Main.java"); + expectPass(GooClass, JAVA, "-cp ..${PS}${PS}/xyzzy Main"); + + // All other empty path elements are ignored. + + // note presence of empty arg in this invocation + expectFail(GooJar, null, JAVAC, "-source", "8", "-target", "8", "-extdirs", "", "-cp", "..", "../Main.java"); + + expectFail(GooJar, JAVAC, "-source 8 -target 8 -extdirs ${PS} -cp .. ../Main.java"); + expectFail(GooJar, JAVAC, "-source 8 -target 8 -Djava.ext.dirs=${PS} -cp .. ../Main.java"); + + expectPass(GooJar, JAVAC, "-source 8 -target 8 -extdirs . -cp .. ../Main.java"); + expectPass(GooJar, JAVAC, "-source 8 -target 8 -Djava.ext.dirs=. -cp .. ../Main.java"); + + expectFail(GooJar, JAVAC, "-source 8 -target 8 -Djava.endorsed.dirs= -cp .. ../Main.java"); + + expectFail(GooJar, JAVAC, "-source 8 -target 8 -endorseddirs ${PS} -cp .. ../Main.java"); + + expectPass(GooJar, JAVAC, "-source 8 -target 8 -Djava.endorsed.dirs=. -cp .. ../Main.java"); + + expectFail(GooClass, JAVAC, "-source 8 -target 8 -Xbootclasspath/p: -cp .. ../Main.java"); + + expectPass(GooClass, JAVAC, "-source 8 -target 8 -Xbootclasspath/p:. -cp .. ../Main.java"); + + expectFail(GooClass, JAVAC, "-source 8 -target 8 -Xbootclasspath:../java-lang.jar -cp .. ../Main.java"); + + expectPass(GooClass, JAVAC, "-source 8 -target 8 -Xbootclasspath:../java-lang.jar${PS}. -cp .. ../Main.java"); + + expectFail(GooClass, JAVAC, "-source 8 -target 8 -Xbootclasspath/a: -cp .. ../Main.java"); + expectFail(GooClass, JAVA, "-Xbootclasspath/a: -cp .. Main"); + + expectPass(GooClass, JAVAC, "-source 8 -target 8 -Xbootclasspath/a:. -cp .. ../Main.java"); + expectPass(GooClass, JAVA, "-Xbootclasspath/a:. -cp .. Main"); + + } + + +} diff --git a/test/langtools/tools/javac/Paths/MineField.sh b/test/langtools/tools/javac/Paths/MineField.sh deleted file mode 100644 index cfacc28fbcba4..0000000000000 --- a/test/langtools/tools/javac/Paths/MineField.sh +++ /dev/null @@ -1,277 +0,0 @@ -#!/bin/sh - -# -# Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# @test -# @bug 4758537 4809833 8149599 -# @summary Test that javac and java find files in similar ways -# @author Martin Buchholz -# -# @run shell/timeout=600 MineField.sh - -# To run this test manually, simply do ./MineField.sh - -#---------------------------------------------------------------- -# The search order for classes used by both java and javac is: -# -# -Xbootclasspath/p: -# -endorseddirs or -Djava.endorsed.dirs= (search for jar/zip only) -# -bootclasspath or -Xbootclasspath: -# -Xbootclasspath/a: -# -extdirs or -Djava.ext.dirs= (search for jar/zip only) -# -classpath , -cp , env CLASSPATH= -# -# Peculiarities of the class file search: -# - Empty elements of the (user) classpath default to ".", -# while empty elements of other paths are ignored. -# - Only for the user classpath is an empty string value equivalent to "." -# - Specifying a bootclasspath on the command line obliterates any -# previous -Xbootclasspath/p: or -Xbootclasspath/a: command line flags. -# -# JDK 9 update: -# java: The java launcher does not support any of the following: -# * -Xbootclasspath/p: -Xbootclasspath: -# * -endorseddirs -Djava.endorsed.dirs -# * -extdirs -Djava.ext.dirs -# All test cases exercising these features have been removed. -# javac: The following features are only supported when compiling -# for older releases: -# * -Xbootclasspath/p: -Xbootclasspath: -bootclasspath -Xbootclasspath/a: -# * -endorseddirs -Djava.endorsed.dirs -# * -extdirs -Djava.ext.dirs -# All test cases exercising these features have been modified to -# use -source 8 -target 8. In addition, javac test cases involving -# use of the runtime properties java.endorsed.dirs and java.extdirs -# (by means of -J-Dname=value) have been removed. -# Although the primary purpose of the test cases in this file is to -# compare javac and java behavior, some tests remain for javac for -# which there is no java equivalent. However, the cases remain as useful -# test cases for javac handling of the paths involved. - -#---------------------------------------------------------------- - -. ${TESTSRC-.}/Util.sh - -set -u - -#---------------------------------------------------------------- -# Prepare the "Minefield" -#---------------------------------------------------------------- -Cleanup() { - Sys rm -rf GooSrc GooJar GooZip GooClass - Sys rm -rf BadSrc BadJar BadZip BadClass - Sys rm -rf OneDir *.class Main.java MANIFEST.MF - Sys rm -f java-lang.jar -} - -Cleanup -Sys mkdir GooSrc GooJar GooZip GooClass -Sys mkdir BadSrc BadJar BadZip BadClass - -echo 'public class Lib {public static void f(){}}' > Lib.java -Sys "$javac" ${TESTTOOLVMOPTS} Lib.java -Sys "$jar" cf GooJar/Lib.jar Lib.class -Sys "$jar" cf GooZip/Lib.zip Lib.class -Sys mv Lib.class GooClass/. -Sys mv Lib.java GooSrc/. -CheckFiles GooZip/Lib.zip GooJar/Lib.jar GooSrc/Lib.java - -echo 'public class Lib {/* Bad */}' > Lib.java -Sys "$javac" ${TESTTOOLVMOPTS} Lib.java -Sys "$jar" cf BadJar/Lib.jar Lib.class -Sys "$jar" cf BadZip/Lib.zip Lib.class -Sys mv Lib.class BadClass/. -Sys mv Lib.java BadSrc/. -CheckFiles BadZip/Lib.zip BadJar/Lib.jar BadSrc/Lib.java - -echo 'public class Main {public static void main(String[] a) {Lib.f();}}' > Main.java - -# Create a jar file that is good enough to put on the javac boot class path (i.e. contains java.lang.**) -if [ -r ${TESTJAVA}/lib/modules ]; then - Sys "$jimage" extract --dir modules ${TESTJAVA}/lib/modules - Sys "$jar" cf java-lang.jar -C modules/java.base java/lang - Sys rm -rf modules -elif [ -d ${TESTJAVA}/modules ]; then - Sys "$jar" cf java-lang.jar -C ${TESTJAVA}/modules/java.base java/lang -else - echo 'cannot create java-lang.jar' ; exit 1 -fi - -#---------------------------------------------------------------- -# Verify that javac class search order is the same as java's -#---------------------------------------------------------------- -Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Xbootclasspath/p:"GooClass" \ - -bootclasspath "java-lang.jar${PS}BadZip/Lib.zip" \ - Main.java -Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Xbootclasspath/p:"BadClass${PS}GooClass" \ - -bootclasspath "java-lang.jar${PS}GooZip/Lib.zip${PS}BadClass" \ - Main.java -Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Xbootclasspath/p:"BadJar/Lib.jar" \ - -Xbootclasspath:"java-lang.jar${PS}GooClass" \ - Main.java - -#---------------------------------------------------------------- -Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -bootclasspath "java-lang.jar${PS}GooZip/Lib.zip" \ - -Xbootclasspath/p:"BadClass" \ - Main.java -Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -bootclasspath "java-lang.jar${PS}BadZip/Lib.zip" \ - -Xbootclasspath/p:"GooClass${PS}BadJar/Lib.jar" \ - Main.java - -#---------------------------------------------------------------- -Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Xbootclasspath/p:"BadClass" \ - -Xbootclasspath/a:"GooClass" \ - Main.java -Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Xbootclasspath/p:"GooClass${PS}BadClass" \ - -Xbootclasspath/a:"BadClass" \ - Main.java -Success "$java" ${TESTVMOPTS} \ - -Xbootclasspath/a:"GooClass" \ - Main - -#---------------------------------------------------------------- -Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Xbootclasspath/p:"GooClass" \ - -Xbootclasspath:"BadClass${PS}java-lang.jar" \ - -Xbootclasspath/a:"GooClass" \ - Main.java -Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Xbootclasspath/p:"BadClass" \ - -Xbootclasspath:"GooClass${PS}BadClass${PS}java-lang.jar" \ - -Xbootclasspath/a:"BadClass" \ - Main.java - -#---------------------------------------------------------------- -Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -endorseddirs "BadClass${PS}GooZip${PS}BadJar" \ - -Xbootclasspath:"BadClass${PS}java-lang.jar" \ - Main.java -Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Djava.endorsed.dirs="BadClass${PS}GooZip${PS}BadJar" \ - -Xbootclasspath:"BadClass${PS}java-lang.jar" \ - Main.java - -#---------------------------------------------------------------- -Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Xbootclasspath/a:"BadClass" \ - -extdirs "GooZip" \ - Main.java -Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Xbootclasspath/a:"GooClass${PS}BadClass" \ - -extdirs "BadZip" \ - Main.java - -#---------------------------------------------------------------- -Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -extdirs "GooClass${PS}BadZip" \ - -cp "GooZip/Lib.zip" \ - Main.java -Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -extdirs "BadClass${PS}GooZip${PS}BadJar" \ - -cp "BadZip/Lib.zip" \ - Main.java -Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \ - -Djava.ext.dirs="GooZip${PS}BadJar" \ - -classpath "BadZip/Lib.zip" \ - Main.java - -#---------------------------------------------------------------- -Failure "$javac" ${TESTTOOLVMOPTS} -classpath "BadClass${PS}GooClass" Main.java -Success "$javac" ${TESTTOOLVMOPTS} -classpath "GooClass${PS}BadClass" Main.java -Failure "$java" ${TESTVMOPTS} -classpath "BadClass${PS}GooClass${PS}." Main -Success "$java" ${TESTVMOPTS} -classpath "GooClass${PS}BadClass${PS}." Main - -Failure "$javac" ${TESTTOOLVMOPTS} -cp "BadJar/Lib.jar${PS}GooZip/Lib.zip" Main.java -Success "$javac" ${TESTTOOLVMOPTS} -cp "GooJar/Lib.jar${PS}BadZip/Lib.zip" Main.java -Failure "$java" ${TESTVMOPTS} -cp "BadJar/Lib.jar${PS}${PS}GooZip/Lib.zip" Main -Success "$java" ${TESTVMOPTS} -cp "GooJar/Lib.jar${PS}${PS}BadZip/Lib.zip" Main - -Failure env CLASSPATH="BadZip/Lib.zip${PS}GooJar/Lib.jar" "$javac" ${TESTTOOLVMOPTS} Main.java -Success env CLASSPATH="GooZip/Lib.zip${PS}BadJar/Lib.jar" "$javac" ${TESTTOOLVMOPTS} Main.java -Failure env CLASSPATH="${PS}BadZip/Lib.zip${PS}GooJar/Lib.jar" "$java" ${TESTVMOPTS} Main -Success env CLASSPATH="${PS}GooZip/Lib.zip${PS}BadJar/Lib.jar" "$java" ${TESTVMOPTS} Main - -#---------------------------------------------------------------- -# Check behavior of empty paths and empty path elements -#---------------------------------------------------------------- -In() { cd "$1"; shift; "$@"; cd ..; } - -In GooClass Failure "$javac" ${TESTTOOLVMOPTS} -cp ".." ../Main.java -In GooClass Failure "$java" ${TESTVMOPTS} -cp ".." Main - -# Unspecified classpath defaults to "." -Sys mkdir OneDir; Sys cp Main.java GooClass/Lib.class OneDir/. -In OneDir Success "$javac" ${TESTTOOLVMOPTS} Main.java -In OneDir Success "$java" ${TESTVMOPTS} Main - -# Empty classpath elements mean "." -In GooClass Success "$javac" ${TESTTOOLVMOPTS} -cp "${PS}.." ../Main.java -In GooClass Success "$java" ${TESTVMOPTS} -cp "${PS}.." Main - -In GooClass Success "$javac" ${TESTTOOLVMOPTS} -cp "..${PS}" ../Main.java -In GooClass Success "$java" ${TESTVMOPTS} -cp "..${PS}" Main - -In GooClass Success "$javac" ${TESTTOOLVMOPTS} -cp "..${PS}${PS}/xyzzy" ../Main.java -In GooClass Success "$java" ${TESTVMOPTS} -cp "..${PS}${PS}/xyzzy" Main - -# All other empty path elements are ignored. -In GooJar Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -extdirs "" -cp ".." ../Main.java - -In GooJar Failure "$javac" -source 8 -targt 8 ${TESTTOOLVMOPTS} -extdirs "${PS}" -cp ".." ../Main.java -In GooJar Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Djava.ext.dirs="${PS}" -cp ".." ../Main.java - -In GooJar Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -extdirs "." -cp ".." ../Main.java -In GooJar Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Djava.ext.dirs="." -cp ".." ../Main.java - -In GooJar Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Djava.endorsed.dirs="" -cp ".." ../Main.java - -In GooJar Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -endorseddirs "${PS}" -cp ".." ../Main.java - -In GooJar Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Djava.endorsed.dirs="." -cp ".." ../Main.java - -In GooClass Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath/p: -cp ".." ../Main.java - -In GooClass Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath/p:. -cp ".." ../Main.java - -In GooClass Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath:"../java-lang.jar" -cp ".." ../Main.java - -In GooClass Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath:"../java-lang.jar${PS}." -cp ".." ../Main.java - -In GooClass Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath/a: -cp ".." ../Main.java -In GooClass Failure "$java" ${TESTVMOPTS} -Xbootclasspath/a: -cp ".." Main - -In GooClass Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath/a:. -cp ".." ../Main.java -In GooClass Success "$java" ${TESTVMOPTS} -Xbootclasspath/a:. -cp ".." Main - -Cleanup - -Bottom Line diff --git a/test/langtools/tools/javac/Paths/Util.java b/test/langtools/tools/javac/Paths/Util.java new file mode 100644 index 0000000000000..ea0d0adc98b61 --- /dev/null +++ b/test/langtools/tools/javac/Paths/Util.java @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.spi.ToolProvider; + +import toolbox.ToolBox; + +/** + * Utility methods for use by tests in the `Paths` directory. + */ +class Util { + + ToolBox tb = new ToolBox(); + PrintStream out = tb.out; + + Path javaHome = Path.of(System.getProperty("java.home")); + String PS = File.pathSeparator; + Path curDir = Path.of("."); + + static final String JAR = "jar"; + static final String JAVA = "java"; + static final String JAVAC = "javac"; + static final String JIMAGE = "jimage"; + + /** The number of test-case failures. */ + int failCount = 0; + /** The number of test-case passes. */ + int passCount = 0; + /** A map recording how often each tool is executed in a separate process. */ + Map execCounts = new TreeMap<>(); + /** A map recording how often each tool is invoked via its ToolProvider API. */ + Map toolCounts = new TreeMap<>(); + + /** + * Reports a summary of the overall test statistics, and throws an exception + * if any test cases failed. + * + * @throws Exception if any test cases failed + */ + void bottomLine() throws Exception { + out.println(); + out.println("-- Summary --"); + out.println("Passed: " + passCount); + out.println("Failed: " + failCount); + out.println("exec: " + execCounts); + out.println("tool: " + toolCounts); + + if (failCount > 0) { + throw new Exception(failCount + " tests failed"); + } + } + + /** + * The result of executing a tool, either in a separate process, or via its ToolProvider API. + * + * @param exitCode the exit code from the tool: 0 for success + * @param out the output from the tool + */ + record Result(int exitCode, String out) { } + + /** + * Executes a tool with given arguments and verifies that it passes. + * + * @param command the name of a JDK tool: java, javac or jar + * @param args a string containing whitespace separated arguments + * @throws Exception if there was an issue trying to execute the tool + * @see #passCount + * @see #failCount + * @see #splitArgs(String) + */ + void expectPass(String command, String args) throws Exception { + expectPass(null, null, command, splitArgs(args)); + } + + /** + * Executes a tool in a specific directory with given arguments and verifies that it passes. + * In order to set the directory, the tool will be executed in a separate process. + * + * @param dir the directory + * @param command the name of a JDK tool: java, javac or jar + * @param args a string containing whitespace separated arguments + * @throws Exception if there was an issue trying to execute the tool + * @see #passCount + * @see #failCount + * @see #splitArgs(String) + */ + void expectPass(Path dir, String command, String args) throws Exception { + expectPass(dir, null, command, splitArgs(args)); + } + + /** + * Executes a tool with additional env variables with given arguments and verifies that it passes. + * In order to set the env variables, the tool will be executed in a separate process. + * Note that any value of {@code CLASSPATH} inherited from this process will always be removed. + * + * @param env the additional env variables + * @param command the name of a JDK tool: java, javac or jar + * @param args a string containing whitespace separated arguments + * @throws Exception if there was an issue trying to execute the tool + * @see #passCount + * @see #failCount + * @see #splitArgs(String) + */ + void expectPass(Map env, String command, String args) throws Exception { + expectPass(null, env, command, splitArgs(args)); + } + + /** + * Executes a tool in a given directory with additional env variables with given arguments + * and verifies that it passes. + * In order to set any directory and env variables, the tool will be executed in a separate process. + * Note that any value of {@code CLASSPATH} inherited from this process will always be removed. + * + * @param dir the directory, or {@code null} + * @param env the additional env variables, or {@code null} + * @param command the name of a JDK tool: java, javac or jar + * @param args the arguments + * @throws Exception if there was an issue trying to execute the tool + * @see #passCount + * @see #failCount + */ + void expectPass(Path dir, Map env, String command, String... args) throws Exception { + Result result = switch (command) { + case JAR -> jar(args); + case JAVAC -> javac(dir, env, args); + case JAVA -> java(dir, env, args); + default -> throw new Exception("unknown command: " + command); + }; + + if (result.exitCode == 0) { + out.println("PASS: test passed as expected"); + passCount++; + } else { + out.println("FAIL: test failed unexpectedly"); + failCount++; + } + } + + /** + * Executes a tool with given arguments and verifies that it fails. + * + * @param command the name of a JDK tool: java, javac or jar + * @param args a string containing whitespace separated arguments + * @throws Exception if there was an issue trying to execute the tool + * @see #passCount + * @see #failCount + * @see #splitArgs(String) + */ + void expectFail(String command, String args) throws Exception { + expectFail(null, null, command, splitArgs(args)); + } + + /** + * Executes a tool in a specific directory with given arguments and verifies that it fails. + * In order to set the directory, the tool will be executed in a separate process. + * + * @param dir the directory + * @param command the name of a JDK tool: java, javac or jar + * @param args a string containing whitespace separated arguments + * @throws Exception if there was an issue trying to execute the tool + * @see #passCount + * @see #failCount + * @see #splitArgs(String) + */ + void expectFail(Path dir, String command, String args) throws Exception { + expectFail(dir, null, command, splitArgs(args)); + } + + /** + * Executes a tool with additional env variables with given arguments and verifies that it passes. + * In order to set the env variables, the tool will be executed in a separate process. + * Note that any value of {@code CLASSPATH} inherited from this process will always be removed. + * + * @param env the additional env variables + * @param command the name of a JDK tool: java, javac or jar + * @param args a string containing whitespace separated arguments + * @throws Exception if there was an issue trying to execute the tool + * @see #passCount + * @see #failCount + * @see #splitArgs(String) + */ + void expectFail(Map env, String command, String args) throws Exception { + expectFail(null, env, command, splitArgs(args)); + } + + /** + * Executes a tool in a given directory with additional env variables with given arguments + * and verifies that it passes. + * In order to set any directory and env variables, the tool will be executed in a separate process. + * Note that any value of {@code CLASSPATH} inherited from this process will always be removed. + * + * @param dir the directory, or {@code null} + * @param env the additional env variables, or {@code null} + * @param command the name of a JDK tool: java, javac or jar + * @param args the arguments + * @throws Exception if there was an issue trying to execute the tool + * @see #passCount + * @see #failCount + */ + void expectFail(Path dir, Map env, String command, String... args) throws Exception { + Result result = switch (command) { + case JAR -> jar(args); + case JAVAC -> javac(dir, env, args); + case JAVA -> java(dir, env, args); + default -> throw new Exception("unknown command: " + command); + }; + + if (result.exitCode == 0) { + out.println("FAIL: test passed unexpectedly"); + failCount++; + } else { + out.println("PASS: failed as expected"); + passCount++; + } + } + + /** + * Splits a string into a list of strings that were separated by whitespace. + * Leading and trailing whitespace is removed. + * The character sequence {@code ${PS}} is replaced by the platform path separator. + * Note, quotes are not supported, and so there is no support for embedded whitespace + * or empty strings in the output. + * + * @param args a string of tokens separated by whitespace + * @return an array of the tokens that were separated by whitespace + */ + String[] splitArgs(String args) { + return args.trim() + .replace("${PS}", PS) + .split("\\s+"); + } + + /** + * Executes {@code javac} using its ToolProvider API. + * + * @param args the arguments + * @return an object containing the output and exit code from the tool + * @throws Exception if there is an issue executing the tool + */ + Result javac(String... args) throws Exception { + return runTool(JAVAC, args); + } + + /** + * Executes {@code javac} in either a separate process or using its ToolProvider API. + * The ToolProvider API is used if the directory and env parameters are {@code null}, + * and if the arguments definitely do not use "classpath wildcards", which are + * only supported when the tool is invoked by the launcher. + * + * @param dir the directory, or {@code null} + * @param env any additional environment variables, or {@code null} + * @param args the arguments + * @return an object containing the output and exit code from the tool + * @throws Exception if there is an issue executing the tool + */ + Result javac(Path dir, Map env, String... args) throws Exception { + return (env != null || dir != null || hasWildcardClassPath(args)) + ? execTool(dir, env, JAVAC, args) + : runTool(JAVAC, args); + } + + /** + * {@return true if the arguments may contain a classpath option using a "classpath wildcard"} + * + * The result is {@code true} if there is any form of a classpath option whose value contains {@code *}. + * Note: this may include "false positives", where the {@code *} is not at the end of + * any element in the path, such as when the character is part of the filename. + * However, in context, the approximation is safe, and just means that we may sometimes + * execute javac in a separate process when it would be sufficient to use its ToolProvider API. + * + * A more refined implementation could split apart the path elements and looking for + * an element that is {@code *} or which ends in {@code *}. + * + * @param args the arguments to be checked + */ + private boolean hasWildcardClassPath(String... args) { + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + switch (arg) { + case "-classpath", "--class-path", "-cp" -> { + if (i + 1 < args.length && args[i + 1].contains("*")) { + return true; + } + } + default -> { + if (arg.startsWith("--class-path=") && arg.contains("*")) { + return true; + } + } + } + } + return false; + } + + /** + * Executes {@code jar} using its ToolProvider API. + * + * @param args the arguments + * @return an object containing the output and exit code from the tool + * @throws Exception if there is an issue executing the tool + */ + Result jar(String... args) throws Exception { + return runTool(JAR, args); + } + + /** + * Executes {@code jimage} using its ToolProvider API. + * + * @param args the arguments + * @return an object containing the output and exit code from the tool + * @throws Exception if there is an issue executing the tool + */ + Result jimage(String... args) throws Exception { + return execTool(null, null, JIMAGE, args); + } + + /** + * Executes {@code java} in a separate process. + * + * @param dir the directory, or {@code null} + * @param env any additional environment variables, or {@code null} + * @param args the arguments + * @return an object containing the output and exit code from the launcher + * @throws Exception if there is an issue executing the tool + */ + Result java(Path dir, Map env, String... args) throws Exception { + return execTool(dir, env, JAVA, args); + } + + /** + * Runs a tool using its ToolProvider API. + * + * @param args the arguments + * @return an object containing the output and exit code from the launcher + * @throws Exception if there is an issue executing the tool + */ + Result runTool(String name, String... args) throws Exception { + out.println(name + ": " + String.join(" ", args)); + var tool = ToolProvider.findFirst(name) + .orElseThrow(() -> new Exception("cannot find " + name)); + try (StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw)) { + int rc = tool.run(pw, pw, args); + pw.flush(); + String output = sw.toString(); + output.lines() + .forEach(l -> out.println(name + ": " + l)); + if (rc != 0) { + out.println(name + ": exit code " + rc); + } + toolCounts.put(name, toolCounts.computeIfAbsent(name, n -> 0) + 1); + return new Result(rc, output); + } + } + + /** + * Executes a tool in a separate process. + * + * Note that any value of {@code CLASSPATH} inherited from this process will always be removed. + * + * @param dir the directory, or {@code null} + * @param env any additional environment variables, or {@code null} + * @param args the arguments + * @return an object containing the output and exit code from the launcher + * @throws Exception if there is an issue executing the tool + */ + Result execTool(Path dir, Map env, String name, String... args) throws Exception { + out.print(name + ":"); + if (env != null) { + out.print(" " + env); + } + if (dir != null) { + out.print(" (" + dir + ")"); + } + out.println(" " + String.join(" ", args)); + + Path tool = javaHome.resolve("bin").resolve(name + (ToolBox.isWindows() ? ".exe" : "")); + if (!Files.exists(tool)) { + throw new Exception("cannot find " + name); + } + var cmd = new ArrayList(); + cmd.add(tool.toString()); + cmd.addAll(List.of(args)); + ProcessBuilder pb = new ProcessBuilder(cmd) + .redirectErrorStream(true); + pb.environment().remove("CLASSPATH"); // always remove default value set by jtreg + if (env != null) { + pb.environment().putAll(env); + } + if (dir != null) { + pb.directory(dir.toFile()); + } + Process p = pb.start(); + StringBuilder sb = new StringBuilder(); + try (var in = p.inputReader()) { + in.lines().forEach(l -> { + sb.append(l).append("\n"); + out.println(name + ": " + l); + }); + } + p.waitFor(); + int rc = p.exitValue(); + if (rc != 0) { + out.println(name + ": exit code " + rc); + } + execCounts.put(name, execCounts.computeIfAbsent(name, n -> 0) + 1); + return new Result(rc, sb.toString()); + } + + /** + * Checks that a series of files exist and are readable. + * + * @param paths the files + * @throws Exception if any of the files are not found or are not readable + */ + void checkFiles(String... paths) throws Exception { + for (String p : paths) { + Path path = Path.of(p); + if (!Files.isReadable(path) ) { + throw new Exception("file not found: " + path); + } + } + } + + /** + * List the files in a directory that match a "glob" pattern. + * + * @param dir the directory + * @param glob the pattern + * @return the list of files + * @throws IOException if there is a problem listing the contents of the directory + */ + List listFiles(Path dir, String glob) throws IOException { + var files = new ArrayList(); + try (DirectoryStream ds = Files.newDirectoryStream(dir, glob)) { + for (Path p : ds) { + files.add(p); + } + } + return files; + } + + /** + * Deletes a series of files. + * The files are deleted using {@link ToolBox#cleanDirectory(Path)} and + * {@code ToolBox#deleteFiles}, which together try hard to delete the files, + * even on Windows. + * + * @param paths the paths + * @throws IOException if there is a problem deleting any of the files + * @see #deleteFiles(List) + */ + void deleteFiles(String... paths) throws IOException { + deleteFiles(Arrays.stream(paths) + .map(Path::of) + .toList()); + } + + /** + * Deletes a series of files. + * The files are deleted using {@link ToolBox#cleanDirectory(Path)} and + * {@code ToolBox#deleteFiles}, which together try hard to delete the files, + * even on Windows. + * + * @param paths the paths + * @throws IOException if there is a problem deleting any of the files + */ + void deleteFiles(List paths) throws IOException { + for (Path path : paths) { + if (Files.exists(path)) { + if (Files.isDirectory(path)) { + tb.cleanDirectory(path); + } + tb.deleteFiles(path); + } + } + } + + /** + * Moves a series of files into a given directory. + * + * @param files the files + * @param dir the target directory + * @throws IOException if there is a problem moving any of the files + */ + void moveFiles(List files, Path dir) throws IOException { + for (Path p : files) { + tb.moveFile(p, dir); + } + } + + /** + * Moves a series of files into a given directory. + * + * @param files the files + * @param dir the target directory + * @throws IOException if there is a problem moving any of the files + */ + void moveFiles(List files, String dir) throws IOException { + for (String p : files) { + tb.moveFile(p, dir); + } + } + + /** + * {@return a map containing a setting for the {@code CLASSPATH} env variable} + * + * @param classpath the value for the env variable + */ + Map classpath(String classpath) { + return Map.of("CLASSPATH", classpath.replace("${PS}", PS)); + } + + /** + * Writes a file called {@code MANIFEST.MF} containing a given value for + * the {@code Class-Path} entry. + * + * @param path the value for the {@code Class-Path} entry + * @throws IOException if there is a problem writing the file + */ + void makeManifestWithClassPath(String path) throws IOException { + Files.writeString(Path.of("MANIFEST.MF"), + "Manifest-Version: 1.0\n" + + "Class-Path: " + path + "\n"); + } + +} diff --git a/test/langtools/tools/javac/Paths/Util.sh b/test/langtools/tools/javac/Paths/Util.sh deleted file mode 100644 index fa96732fea17e..0000000000000 --- a/test/langtools/tools/javac/Paths/Util.sh +++ /dev/null @@ -1,115 +0,0 @@ -# -# Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# Utilities for shell tests - -: ${TESTSRC=.} ${TESTCLASSES=.} - java="${TESTJAVA+${TESTJAVA}/bin/}java${EXE_SUFFIX}" - javac="${TESTJAVA+${TESTJAVA}/bin/}javac${EXE_SUFFIX}" - jar="${TESTJAVA+${TESTJAVA}/bin/}jar${EXE_SUFFIX}" -jimage="${TESTJAVA+${TESTJAVA}/bin/}jimage${EXE_SUFFIX}" - -case `uname -s` in - Windows*|CYGWIN*|MSYS*|MINGW*) - WindowsOnly() { "$@"; } - UnixOnly() { :; } - PS=";" ;; - *) - UnixOnly() { "$@"; } - WindowsOnly() { :; } - PS=":";; -esac - -failed="" -Fail() { echo "FAIL: $1"; failed="${failed}."; } - -Die() { printf "%s\n" "$*"; exit 1; } - -Sys() { - printf "%s\n" "$*"; "$@"; rc="$?"; - test "$rc" -eq 0 || Die "Command \"$*\" failed with exitValue $rc"; -} - -CheckFiles() { - for f in "$@"; do test -r "$f" || Die "File $f not found"; done -} - -Report() { - test "$#" != 2 && Die "Usage: Report success|failure rc" - - if test "$1" = "success" -a "$2" = 0; then - echo "PASS: succeeded as expected" - elif test "$1" = "failure" -a "$2" != 0; then - echo "PASS: failed as expected" - elif test "$1" = "success" -a "$2" != 0; then - Fail "test failed unexpectedly" - elif test "$1" = "failure" -a "$2" = 0; then - Fail "test succeeded unexpectedly" - else - Die "Usage: Report success|failure rc" - fi -} - -MkManifestWithClassPath() { - (echo "Manifest-Version: 1.0"; echo "Class-Path: $*") > MANIFEST.MF -} - -HorizontalRule() { - echo "-----------------------------------------------------------------" -} - -Test() { - HorizontalRule - expectedResult="$1"; shift - printf "%s\n" "$*" - "$@" - Report "$expectedResult" "$?" -} - -Failure() { Test failure "$@"; } -Success() { Test success "$@"; } - -Bottom() { - test "$#" = 1 -a "$1" = "Line" || Die "Usage: Bottom Line" - - if test -n "$failed"; then - count=`printf "%s" "$failed" | wc -c | tr -d ' '` - echo "FAIL: $count tests failed" - exit 1 - else - echo "PASS: all tests gave expected results" - exit 0 - fi -} - -BadJarFile() { - for jarfilename in "$@"; do pwd > "$jarfilename"; done -} - - -#---------------------------------------------------------------- -# Foil message localization -#---------------------------------------------------------------- -DiagnosticsInEnglishPlease() { - LANG="C" LC_ALL="C" LC_MESSAGES="C"; export LANG LC_ALL LC_MESSAGES -} diff --git a/test/langtools/tools/javac/Paths/WildcardMineField.java b/test/langtools/tools/javac/Paths/WildcardMineField.java new file mode 100644 index 0000000000000..ad1362080b19b --- /dev/null +++ b/test/langtools/tools/javac/Paths/WildcardMineField.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6268383 8172309 8293877 + * @summary Test classpath wildcards for javac and java -classpath option. + * @library /tools/lib + * @build toolbox.ToolBox Util WildcardMineField + * @run main WildcardMineField + */ + +/* + * Converted from wcMineField.sh, originally written by Martin Buchholz. + * + * For the last version of the original, wcMineField.sh, see + * https://git.openjdk.org/jdk/blob/jdk-19%2B36/test/langtools/tools/javac/Paths/wcMineField.sh + * + * This class primarily tests support for "classpath wildcards", which is a feature + * by which elements of a classpath option ending in {@code *} are expanded into + * the set of jar files found in the directory preceding the {@code *}. + * + * Note that this feature is only implemented in the launcher, even for javac, + * and so is only available when running javac via its launcher, in a separate process. + * + * Note that this feature does not affect the use of {@code *} elsewhere in any path, + * classpath or otherwise, and so this class also tests the use of {@code *} and other special + * characters (like {@code ,} and {@code ;}) in filenames. Some of these tests, + * labelled in the original code as "UnixOnly", do not apply to Windows. + * + * For information on the launcher support for the {@code -classpath} option, + * see the java man page. As of September 2022, there is no equivalent documentation + * for javac, except to say that the support is only in the native launcher for javac, + * and not in the main javac source code. + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import toolbox.ToolBox; + +public class WildcardMineField extends Util { + public static void main(String... args) throws Exception { + new WildcardMineField().run(args); + } + + void run(String... args) throws Exception { + setup(); + tests(); + cleanup(); + bottomLine(); + } + + void setup() throws Exception { + cleanup(); + tb.createDirectories("GooSrc", "GooJar", "GooZip", "GooClass", "GooJar/SubDir"); + tb.createDirectories("BadSrc", "BadJar", "BadZip", "BadClass"); + tb.createDirectories("SpeSrc", "SpeJar", "SpeZip", "SpeClass"); + tb.createDirectories("JarNClass", "StarJar", "MixJar"); + Files.writeString(Path.of("Lib.java"), "public class Lib {public static void f(){}}"); + Files.writeString(Path.of("Lib2.java"), "public class Lib2 {public static void g(){}}"); + Files.writeString(Path.of("Lib3.java"), "public class Lib3 {public static void h(){}}"); + javac("Lib.java", "Lib2.java", "Lib3.java"); + tb.copyFile("Lib.class", "JarNClass/."); + jar("cf", "GooJar/Lib.jar", "Lib.class"); + jar("cf", "GooJar/SubDir/Lib2.jar", "Lib2.class"); + jar("cf", "JarNClass/Lib.jar", "Lib.class"); + + jar("cf", "GooZip/Lib.zip", "Lib.class"); + tb.moveFile("Lib.class", "GooClass/."); + tb.moveFile("Lib2.class", "GooClass/."); + tb.moveFile("Lib3.class", "GooClass/."); + tb.moveFile("Lib.java", "GooSrc/."); + tb.moveFile("Lib2.java", "GooSrc/."); + tb.moveFile("Lib3.java", "GooSrc/."); + + checkFiles("GooZip/Lib.zip", "GooJar/Lib.jar", "GooSrc/Lib.java"); + checkFiles("GooSrc/Lib2.java", "GooSrc/Lib3.java", "GooJar/SubDir/Lib2.jar"); + + Files.writeString(Path.of("Spe1.java"), "public class Spe1 {public static void f(){}}"); + Files.writeString(Path.of("Spe2.java"), "public class Spe2 {public static void f(){}}"); + Files.writeString(Path.of("Spe3.java"), "public class Spe3 {public static void f(){}}"); + Files.writeString(Path.of("Spe4.java"), "public class Spe4 {public static void f(){}}"); + javac("Spe1.java", "Spe2.java", "Spe3.java", "Spe4.java"); + + if (!ToolBox.isWindows()) { + jar("cf", "SpeJar/Spe:Colon.jar", "Spe1.class"); + jar("cf", "SpeJar/Spe*wc.jar", "Spe4.class"); + checkFiles("SpeJar/Spe*wc.jar"); + + jar("cf", "StarJar/*jar.jar", "Spe2.class"); + jar("cf", "StarJar/jar*.jar", "Spe3.class"); + jar("cf", "StarJar/*jar*.jar", "Spe4.class"); + checkFiles("StarJar/*jar.jar", "StarJar/jar*.jar", "StarJar/*jar*.jar"); + } + + jar("cf", "SpeJar/Spe,Comma.jar", "Spe2.class"); + jar("cf", "SpeJar/Spe;Semi.jar", "Spe3.class"); + + jar("cf", "MixJar/mix.jAr", "Spe1.class"); + jar("cf", "MixJar/mix2.JAR", "Spe2.class"); + jar("cf", "MixJar/mix3.zip", "Spe3.class"); + jar("cf", "MixJar/.hiddenjar.jar", "Spe4.class"); + + moveFiles(listFiles(curDir, "Spe*.class"), Path.of("SpeClass/.")); + moveFiles(listFiles(curDir, "Spe*.java"), Path.of("SpeSrc/.")); + checkFiles("SpeJar/Spe,Comma.jar", "SpeJar/Spe;Semi.jar", "SpeSrc/Spe2.java", "SpeSrc/Spe3." + + "java", "SpeSrc/Spe4.java"); + checkFiles("MixJar/mix.jAr", "MixJar/mix2.JAR", "MixJar/mix3.zip", "MixJar/.hiddenjar.jar"); + + Files.writeString(Path.of("Main.java"), "public class Main {public static void main(String[] a) {Lib.f();}}"); + Files.writeString(Path.of("Main1.java"), "public class Main1 {public static void main(String[] a) {Lib2.g();}}"); + Files.writeString(Path.of("Main1b.java"), "public class Main1b {public static void main(String[] a) {Spe1.f();}}"); + Files.writeString(Path.of("Main2.java"), "public class Main2 {public static void main(String[] a) {Spe2.f();}}"); + Files.writeString(Path.of("Main3.java"), "public class Main3 {public static void main(String[] a) {Spe3.f();}}"); + Files.writeString(Path.of("Main4.java"), "public class Main4 {public static void main(String[] a) {Spe4.f();}}"); + Files.writeString(Path.of("Main5.java"), "public class Main5 {public static void main(String[] a) {Spe2.f(); Lib.f();}}"); + Files.writeString(Path.of("Main6.java"), "public class Main6 {public static void main(String[] a) {Lib3.h();}}"); + } + + void cleanup() throws IOException { + deleteFiles("GooSrc", "GooJar", "GooZip", "GooClass"); + deleteFiles("SpeSrc", "SpeJar", "SpeZip", "SpeClass"); + deleteFiles("BadSrc", "BadJar", "BadZip", "BadClass"); + deleteFiles("JarNClass", "StarJar", "MixJar", "StarDir"); + deleteFiles("OneDir", "MANIFEST.MF"); + deleteFiles(listFiles(curDir, "*.class")); + deleteFiles(listFiles(curDir, "Main*.java")); + } + + void tests() throws Exception { + if (!ToolBox.isWindows()) { + starDirTests(); + } + + /*---------------------------------------------------------------- + * Verify the basic jar file works + *----------------------------------------------------------------*/ + + // baseline test to verify it works. + expectPass(JAVAC, "-cp GooJar/Lib.jar Main.java"); + expectPass(JAVAC, "-classpath GooJar/Lib.jar Main.java"); + expectPass(JAVA, "-classpath GooJar/Lib.jar${PS}. Main"); + expectPass(JAVA, "-cp GooJar/Lib.jar${PS}. Main"); + + // basic test of one jar to be loaded + if (!ToolBox.isWindows()) { + expectPass(JAVAC, "-classpath GooJar/* Main.java"); + } + expectPass(JAVAC, "-classpath GooJar/*${PS}. Main.java"); + expectPass(JAVA, "-classpath GooJar/*${PS}. Main"); + + // in a subdir. First * should not load jars in subdirectories unless specified + expectFail(JAVAC, "-classpath GooJar/* Main1.java"); + expectFail(JAVAC, " -classpath GooJar/*${PS}. Main1.java"); + expectPass(JAVAC, "-cp GooJar/SubDir/* Main1.java"); + expectPass(JAVAC, "-classpath GooJar/SubDir/* Main1.java"); + expectPass(JAVAC, "--class-path GooJar/SubDir/* Main1.java"); + expectPass(JAVAC, "--class-path=GooJar/SubDir/* Main1.java"); + + // Same with launcher. Should not load jar in subdirectories unless specified + expectFail(JAVA, "-classpath GooJar/*${PS}. Main1"); + expectPass(JAVA, "-classpath GooJar/SubDir/*${PS}. Main1"); + expectPass(JAVA, "-cp GooJar/SubDir/*${PS}. Main1"); + + expectPass(classpath("GooJar/SubDir/*"), JAVAC, "Main1.java"); + expectPass(classpath("GooJar/SubDir/*${PS}."), JAVA, "Main1"); + + /*---------------------------------------------------------------- + * Verify the jar files in 2 directories + *----------------------------------------------------------------*/ + + expectPass(JAVAC, "-classpath GooJar/Lib.jar${PS}SpeJar/Spe,Comma.jar Main5.java"); + expectPass(JAVA, "-classpath GooJar/Lib.jar${PS}SpeJar/Spe,Comma.jar${PS}. Main5"); + + expectPass(JAVAC, "-classpath GooJar/*${PS}SpeJar/* Main5.java"); + expectPass(JAVA, "-classpath GooJar/*${PS}SpeJar/*${PS}. Main5"); + + /*---------------------------------------------------------------- + * Verify jar file and class file in same directory. + *----------------------------------------------------------------*/ + + expectPass(JAVAC, "-classpath JarNClass/*${PS} Main.java"); + expectPass(JAVA, "-classpath JarNClass/*${PS}. Main"); + + /*---------------------------------------------------------------- + * Verify these odd jar files work explicitly on classpath, kind of + * a baseline. Last one is also a test with * in a jar name. + *----------------------------------------------------------------*/ + + expectFail(JAVAC, "-classpath SpeJar/Spe:Colon.jar Main1.java"); + + expectPass(JAVAC, "-classpath SpeJar/Spe,Comma.jar Main2.java"); + expectPass(JAVA, "-classpath SpeJar/Spe,Comma.jar${PS}. Main2"); + + if (!ToolBox.isWindows()) { + expectPass(JAVAC, "-classpath SpeJar/Spe;Semi.jar Main3.java"); + expectPass(JAVA, "-classpath SpeJar/Spe;Semi.jar${PS}. Main3"); + + expectPass(JAVAC, "-classpath SpeJar/Spe*wc.jar Main4.java"); + expectPass(JAVA, "-classpath SpeJar/Spe*wc.jar${PS}. Main4"); + } + + if (!ToolBox.isWindows()) { + speJar(); + } + + if (!ToolBox.isWindows()) { + starJar(); + } + + /*---------------------------------------------------------------- + * Verify these jar files with varying extensions + *----------------------------------------------------------------*/ + + // Mixed case extensions should not be loaded. + expectFail(JAVAC, "-classpath MixJar/* Main1b.java"); + expectPass(JAVAC, "-classpath MixJar/mix.jAr Main1b.java"); + expectFail(JAVAC, "-classpath MixJar/* Main1b"); + + // upper case, .JAR, extension should be loaded + if (!ToolBox.isWindows()) { + expectPass(JAVAC, "-classpath MixJar/* Main2.java"); + } + expectPass(JAVAC, "-classpath .${PS}MixJar/* Main2.java"); + + expectPass(JAVA, "-classpath MixJar/*${PS}. Main2"); + + // zip extensions should not be loaded + expectFail(JAVAC, "-classpath MixJar/* Main3.java"); + expectPass(JAVAC, "-classpath MixJar/mix3.zip Main3.java"); + expectFail(JAVA, "-classpath MixJar/*${PS}. Main3"); + + // unix "hidden" file + if (!ToolBox.isWindows()) { + expectPass(JAVAC, "-classpath MixJar/* Main4.java"); + expectPass(JAVA, "-classpath MixJar/*${PS}. Main4"); + } + } + + void starDirTests() throws Exception { + out.println("Running tests with directory named \"*\""); + deleteFiles("./StarDir"); + tb.createDirectories("StarDir/*"); + tb.copyFile("GooClass/Lib2.class", "StarDir/*/Lib2.class"); + jar("cf", "StarDir/Lib3.jar", "-C", "GooClass", "Lib3.class"); + jar("cf", "StarDir/*/Lib.jar", "-C", "GooClass", "Lib.class"); + checkFiles("StarDir/*/Lib.jar", "StarDir/*/Lib2.class", "StarDir/Lib3.jar"); + tb.copyFile("Main6.java", "./StarDir/."); + tb.copyFile("Main.java", "./StarDir/*/."); + tb.copyFile("Main1.java", "./StarDir/*/."); + Path StarDir = Path.of("StarDir"); + expectFail(StarDir, JAVAC, "-classpath * Main6.java"); + expectFail(StarDir, JAVAC, "-classpath ./* Main6.java"); + deleteFiles(listFiles(StarDir, "Main6.*")); + Path StarDir_star = StarDir.resolve("*"); + expectPass(StarDir_star, JAVAC, "-classpath * Main.java"); + expectPass(StarDir_star, JAVA, "-classpath .${PS}* Main"); + expectPass(StarDir_star, JAVAC, "Main1.java"); + expectPass(StarDir_star, JAVA, "-classpath . Main1"); + + expectFail(JAVAC, "-classpath StarDir/* Main6.java"); + + expectPass(JAVAC, "-classpath StarDir/* Main1.java"); + expectPass(JAVA, "-classpath StarDir/*:. Main1"); + + expectPass(JAVAC, "-classpath StarDir/* Main1.java"); + expectPass(JAVA, "-classpath .${PS}StarDir/* Main1"); + + expectFail(JAVAC, "-classpath StarDir/\\*/* Main.java"); + expectPass(JAVAC, "-classpath StarDir/*/* Main.java"); + + expectPass(JAVA, "-classpath .${PS}StarDir/*/* Main"); + expectFail(JAVA, "-classpath .${PS}StarDir/\\*/* Main"); + + expectPass(JAVAC, "-classpath StarDir/Lib3.jar Main6.java"); + expectPass(JAVA, "-classpath .${PS}StarDir/Lib3.jar Main6"); + + expectPass(JAVAC, "-classpath StarDir/*/Lib.jar Main.java"); + expectPass(JAVA, "-classpath .${PS}StarDir/*/Lib.jar Main"); + } + + void speJar() throws Exception { + out.println("Running tests with jar file names containing special characters"); + + expectPass(JAVAC, "-classpath SpeJar/* Main2.java"); + expectPass(JAVA, "-classpath SpeJar/*${PS}. Main2"); + + expectPass(JAVAC, "-classpath SpeJar/* Main3.java"); + expectPass(JAVA, "-classpath SpeJar/*${PS}. Main3"); + + expectPass(JAVAC, "-classpath SpeJar/* Main4.java"); + expectPass(JAVA, "-classpath SpeJar/*${PS}. Main4"); + } + + /*---------------------------------------------------------------- + * Verify these jar files with asterisk in jar file name + *----------------------------------------------------------------*/ + void starJar() throws Exception { + out.println("Running tests with jar file names containing \"*\""); + expectPass(JAVAC, "-classpath StarJar/*jar.jar Main2.java"); + expectPass(JAVA, "-classpath StarJar/*jar.jar${PS}. Main2"); + + expectPass(JAVAC, "-classpath StarJar/jar*.jar Main3.java"); + expectPass(JAVA, "-classpath StarJar/jar*.jar${PS}. Main3"); + + expectPass(JAVAC, "-classpath StarJar/*jar*.jar Main4.java"); + expectPass(JAVA, "-classpath StarJar/*jar*.jar${PS}. Main4"); + + expectPass(JAVAC, "-classpath StarJar/* Main2.java"); + expectPass(JAVA, "-classpath StarJar/*${PS}. Main2"); + + expectPass(JAVAC, "-classpath StarJar/* Main3.java"); + expectPass(JAVA, "-classpath StarJar/*${PS}. Main3"); + + expectPass(JAVAC, "-classpath StarJar/* Main4.java"); + expectPass(JAVA, "-classpath StarJar/*${PS}. Main4"); + } +} diff --git a/test/langtools/tools/javac/Paths/wcMineField.sh b/test/langtools/tools/javac/Paths/wcMineField.sh deleted file mode 100644 index 235125ae25436..0000000000000 --- a/test/langtools/tools/javac/Paths/wcMineField.sh +++ /dev/null @@ -1,296 +0,0 @@ -#!/bin/sh - -# -# Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# -# @test -# @summary Test classpath wildcards for javac and java -classpath option. -# @bug 6268383 8172309 -# @run shell/timeout=600 wcMineField.sh - -# To run this test manually, simply do ./wcMineField.sh -#---------------------------------------------------------------- - -. ${TESTSRC-.}/Util.sh - -set -u - -#---------------------------------------------------------------- -# Note that, on Windows only, the launcher also includes another -# kind of command-line wildcard expansion, via setargv.obj -# http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/progs_11.asp -# Therefore, on Windows, -classpath "foo/*" is treated differently -# from -classpath ".${PS}foo/*" -#---------------------------------------------------------------- - -#---------------------------------------------------------------- -# Prepare the "Minefield" -#---------------------------------------------------------------- -Cleanup() { - Sys rm -rf GooSrc GooJar GooZip GooClass - Sys rm -rf SpeSrc SpeJar SpeZip SpeClass - Sys rm -rf BadSrc BadJar BadZip BadClass - Sys rm -rf JarNClass StarJar MixJar StarDir - Sys rm -rf OneDir *.class Main*.java MANIFEST.MF -} - -Cleanup -Sys mkdir GooSrc GooJar GooZip GooClass GooJar/SubDir -Sys mkdir BadSrc BadJar BadZip BadClass -Sys mkdir SpeSrc SpeJar SpeZip SpeClass -Sys mkdir JarNClass StarJar MixJar -echo 'public class Lib {public static void f(){}}' > Lib.java -echo 'public class Lib2 {public static void g(){}}' > Lib2.java -echo 'public class Lib3 {public static void h(){}}' > Lib3.java -Sys "$javac" ${TESTTOOLVMOPTS} Lib.java Lib2.java Lib3.java -Sys cp Lib.class JarNClass/. -Sys "$jar" cf GooJar/Lib.jar Lib.class -Sys "$jar" cf GooJar/SubDir/Lib2.jar Lib2.class -Sys "$jar" cf JarNClass/Lib.jar Lib.class - -Sys "$jar" cf GooZip/Lib.zip Lib.class -Sys mv Lib.class GooClass/. -Sys mv Lib2.class GooClass/. -Sys mv Lib3.class GooClass/. -Sys mv Lib.java GooSrc/. -Sys mv Lib2.java GooSrc/. -Sys mv Lib3.java GooSrc -CheckFiles GooZip/Lib.zip GooJar/Lib.jar GooSrc/Lib.java -CheckFiles GooSrc/Lib2.java GooSrc/Lib3.java GooJar/SubDir/Lib2.jar - -echo 'public class Spe1 {public static void f(){}}' > Spe1.java -echo 'public class Spe2 {public static void f(){}}' > Spe2.java -echo 'public class Spe3 {public static void f(){}}' > Spe3.java -echo 'public class Spe4 {public static void f(){}}' > Spe4.java -Sys "$javac" ${TESTTOOLVMOPTS} Spe1.java -Sys "$javac" ${TESTTOOLVMOPTS} Spe2.java -Sys "$javac" ${TESTTOOLVMOPTS} Spe3.java -Sys "$javac" ${TESTTOOLVMOPTS} Spe4.java - -UnixOnly Sys "$jar" cf "SpeJar/Spe:Colon.jar" Spe1.class -UnixOnly Sys "$jar" cf "SpeJar/Spe*wc.jar" Spe4.class -UnixOnly CheckFiles "SpeJar/Spe*wc.jar" - -UnixOnly Sys "$jar" cf "StarJar/*jar.jar" Spe2.class -UnixOnly Sys "$jar" cf "StarJar/jar*.jar" Spe3.class -UnixOnly Sys "$jar" cf "StarJar/*jar*.jar" Spe4.class -UnixOnly CheckFiles "StarJar/*jar.jar" "StarJar/jar*.jar" "StarJar/*jar*.jar" - -Sys "$jar" cf "SpeJar/Spe,Comma.jar" Spe2.class -Sys "$jar" cf "SpeJar/Spe;Semi.jar" Spe3.class - -Sys "$jar" cf "MixJar/mix.jAr" Spe1.class -Sys "$jar" cf "MixJar/mix2.JAR" Spe2.class -Sys "$jar" cf "MixJar/mix3.zip" Spe3.class -Sys "$jar" cf "MixJar/.hiddenjar.jar" Spe4.class - -Sys mv Spe*.class SpeClass/. -Sys mv Spe*.java SpeSrc/. -CheckFiles "SpeJar/Spe,Comma.jar" "SpeJar/Spe;Semi.jar" "SpeSrc/Spe2.java" "SpeSrc/Spe3.java" "SpeSrc/Spe4.java" -CheckFiles "MixJar/mix.jAr" "MixJar/mix2.JAR" "MixJar/mix3.zip" "MixJar/.hiddenjar.jar" - -echo 'public class Main {public static void main(String[] a) {Lib.f();}}' > Main.java -echo 'public class Main1 {public static void main(String[] a) {Lib2.g();}}' > Main1.java -echo 'public class Main1b {public static void main(String[] a) {Spe1.f();}}' > Main1b.java -echo 'public class Main2 {public static void main(String[] a) {Spe2.f();}}' > Main2.java -echo 'public class Main3 {public static void main(String[] a) {Spe3.f();}}' > Main3.java -echo 'public class Main4 {public static void main(String[] a) {Spe4.f();}}' > Main4.java -echo 'public class Main5 {public static void main(String[] a) {Spe2.f(); Lib.f();}}' > Main5.java -echo 'public class Main6 {public static void main(String[] a) {Lib3.h();}}' > Main6.java - - -#---------------------------------------------------------------- -# Verify expected behaviour with directory named "*" -#---------------------------------------------------------------- -starDir() { - printf "Running tests with directory named \"*\"\n" - Sys rm -rf ./StarDir - Sys mkdir -p StarDir/"*" - Sys cp "GooClass/Lib2.class" "StarDir/*/Lib2.class" - Sys "$jar" cf "StarDir/Lib3.jar" -C GooClass "Lib3.class" - Sys "$jar" cf "StarDir/*/Lib.jar" -C GooClass "Lib.class" - CheckFiles "StarDir/*/Lib.jar" "StarDir/*/Lib2.class" "StarDir/Lib3.jar" - Sys cp Main6.java ./StarDir/. - Sys cp Main.java ./StarDir/"*"/. - Sys cp Main1.java ./StarDir/"*"/. - CPWC_DIR=`pwd` - Sys cd StarDir - Failure "$javac" ${TESTTOOLVMOPTS} -classpath "*" Main6.java - Failure "$javac" ${TESTTOOLVMOPTS} -classpath "./*" Main6.java - Sys rm -f Main6.* - Sys cd "*" - Success "$javac" ${TESTTOOLVMOPTS} -classpath "*" Main.java - Success "$java" ${TESTVMOPTS} -classpath .${PS}"*" Main - Success "$javac" ${TESTTOOLVMOPTS} Main1.java - Success "$java" ${TESTVMOPTS} -classpath "." Main1 - Sys cd $CPWC_DIR - - Failure "$javac" ${TESTTOOLVMOPTS} -classpath "StarDir/*" Main6.java - - Success "$javac" ${TESTTOOLVMOPTS} -classpath StarDir/\* Main1.java - Success "$java" ${TESTVMOPTS} -classpath StarDir/\*:. Main1 - - Success "$javac" ${TESTTOOLVMOPTS} -classpath "StarDir/*" Main1.java - Success "$java" ${TESTVMOPTS} -classpath ".${PS}StarDir/*" Main1 - - Failure "$javac" ${TESTTOOLVMOPTS} -classpath StarDir/"\*/*" Main.java - Success "$javac" ${TESTTOOLVMOPTS} -classpath StarDir/"*/*" Main.java - - Success "$java" ${TESTVMOPTS} -classpath .${PS}StarDir/"*/*" Main - Failure "$java" ${TESTVMOPTS} -classpath .${PS}StarDir/"\*/*" Main - - Success "$javac" ${TESTTOOLVMOPTS} -classpath "StarDir/Lib3.jar" Main6.java - Success "$java" ${TESTVMOPTS} -classpath ".${PS}StarDir/Lib3.jar" Main6 - - Success "$javac" ${TESTTOOLVMOPTS} -classpath StarDir/"*"/Lib.jar Main.java - Success "$java" ${TESTVMOPTS} -classpath .${PS}StarDir/"*"/Lib.jar Main -} -UnixOnly starDir - -#---------------------------------------------------------------- -# Verify the basic jar file works -#---------------------------------------------------------------- -#baseline test to verify it works. -Success "$javac" ${TESTTOOLVMOPTS} -cp "GooJar/Lib.jar" Main.java -Success "$javac" ${TESTTOOLVMOPTS} -classpath "GooJar/Lib.jar" Main.java -Success "$java" ${TESTVMOPTS} -classpath "GooJar/Lib.jar${PS}." Main -Success "$java" ${TESTVMOPTS} -cp "GooJar/Lib.jar${PS}." Main - -#basic test of one jar to be loaded -UnixOnly Success "$javac" ${TESTTOOLVMOPTS} -classpath "GooJar/*" Main.java - Success "$javac" ${TESTTOOLVMOPTS} -classpath "GooJar/*${PS}." Main.java -Success "$java" ${TESTVMOPTS} -classpath "GooJar/*${PS}." Main -#in a subdir. First * should not load jars in subdirectories unless specified -Failure "$javac" ${TESTTOOLVMOPTS} -classpath "GooJar/*" Main1.java -Failure "$javac" ${TESTTOOLVMOPTS} -classpath "GooJar/*${PS}." Main1.java -Success "$javac" ${TESTTOOLVMOPTS} -cp "GooJar/SubDir/*" Main1.java -Success "$javac" ${TESTTOOLVMOPTS} -classpath "GooJar/SubDir/*" Main1.java -Success "$javac" ${TESTTOOLVMOPTS} --class-path "GooJar/SubDir/*" Main1.java -Success "$javac" ${TESTTOOLVMOPTS} --class-path="GooJar/SubDir/*" Main1.java -#Same with launcher. Should not load jar in subdirectories unless specified -Failure "$java" ${TESTVMOPTS} -classpath "GooJar/*${PS}." Main1 -Success "$java" ${TESTVMOPTS} -classpath "GooJar/SubDir/*${PS}." Main1 -Success "$java" ${TESTVMOPTS} -cp "GooJar/SubDir/*${PS}." Main1 - -Success env CLASSPATH="GooJar/SubDir/*" "$javac" ${TESTTOOLVMOPTS} Main1.java -Success env CLASSPATH="GooJar/SubDir/*${PS}." "$java" ${TESTVMOPTS} Main1 -#---------------------------------------------------------------- -# Verify the jar files in 2 directories -#---------------------------------------------------------------- -Success "$javac" ${TESTTOOLVMOPTS} -classpath "GooJar/Lib.jar${PS}SpeJar/Spe,Comma.jar" Main5.java -Success "$java" ${TESTVMOPTS} -classpath "GooJar/Lib.jar${PS}SpeJar/Spe,Comma.jar${PS}." Main5 - -Success "$javac" ${TESTTOOLVMOPTS} -classpath "GooJar/*${PS}SpeJar/*" Main5.java -Success "$java" ${TESTVMOPTS} -classpath "GooJar/*${PS}SpeJar/*${PS}." Main5 - -#---------------------------------------------------------------- -# Verify jar file and class file in same directory. -#---------------------------------------------------------------- -Success "$javac" ${TESTTOOLVMOPTS} -classpath "JarNClass/*${PS}" Main.java -Success "$java" ${TESTVMOPTS} -classpath "JarNClass/*${PS}." Main - -#---------------------------------------------------------------- -# Verify these odd jar files work explicitly on classpath, kind of -# a baseline. Last one is also a test with * in a jar name. -#---------------------------------------------------------------- -Failure "$javac" ${TESTTOOLVMOPTS} -classpath "SpeJar/Spe:Colon.jar" Main1.java - -Success "$javac" ${TESTTOOLVMOPTS} -classpath "SpeJar/Spe,Comma.jar" Main2.java -Success "$java" ${TESTVMOPTS} -classpath "SpeJar/Spe,Comma.jar${PS}." Main2 - -UnixOnly Success "$javac" ${TESTTOOLVMOPTS} -classpath "SpeJar/Spe;Semi.jar" Main3.java -UnixOnly Success "$java" ${TESTVMOPTS} -classpath "SpeJar/Spe;Semi.jar${PS}." Main3 - -UnixOnly Success "$javac" ${TESTTOOLVMOPTS} -classpath "SpeJar/Spe*wc.jar" Main4.java -UnixOnly Success "$java" ${TESTVMOPTS} -classpath "SpeJar/Spe*wc.jar${PS}." Main4 -#---------------------------------------------------------------- -# Verify these odd jar files work with classpath wildcard. -#---------------------------------------------------------------- - -speJar() { - printf "Running tests with jar file names containing special characters\n" -# Failure "$javac" ${TESTTOOLVMOPTS} -classpath "SpeJar/*" Main1.java -# Success "$java" ${TESTVMOPTS} -classpath "SpeJar/*" Main1 - - Success "$javac" ${TESTTOOLVMOPTS} -classpath "SpeJar/*" Main2.java - Success "$java" ${TESTVMOPTS} -classpath "SpeJar/*${PS}." Main2 - - Success "$javac" ${TESTTOOLVMOPTS} -classpath "SpeJar/*" Main3.java - Success "$java" ${TESTVMOPTS} -classpath "SpeJar/*${PS}." Main3 - - Success "$javac" ${TESTTOOLVMOPTS} -classpath "SpeJar/*" Main4.java - Success "$java" ${TESTVMOPTS} -classpath "SpeJar/*${PS}." Main4 -} -UnixOnly speJar - -#---------------------------------------------------------------- -# Verify these jar files with asterisk in jar file name -#---------------------------------------------------------------- -starJar() { - printf "Running tests with jar file names containing \"*\"\n" - Success "$javac" ${TESTTOOLVMOPTS} -classpath "StarJar/*jar.jar" Main2.java - Success "$java" ${TESTVMOPTS} -classpath "StarJar/*jar.jar${PS}." Main2 - - Success "$javac" ${TESTTOOLVMOPTS} -classpath "StarJar/jar*.jar" Main3.java - Success "$java" ${TESTVMOPTS} -classpath "StarJar/jar*.jar${PS}." Main3 - - Success "$javac" ${TESTTOOLVMOPTS} -classpath "StarJar/*jar*.jar" Main4.java - Success "$java" ${TESTVMOPTS} -classpath "StarJar/*jar*.jar${PS}." Main4 - - Success "$javac" ${TESTTOOLVMOPTS} -classpath "StarJar/*" Main2.java - Success "$java" ${TESTVMOPTS} -classpath "StarJar/*${PS}." Main2 - - Success "$javac" ${TESTTOOLVMOPTS} -classpath "StarJar/*" Main3.java - Success "$java" ${TESTVMOPTS} -classpath "StarJar/*${PS}." Main3 - - Success "$javac" ${TESTTOOLVMOPTS} -classpath "StarJar/*" Main4.java - Success "$java" ${TESTVMOPTS} -classpath "StarJar/*${PS}." Main4 -} -UnixOnly starJar - -#---------------------------------------------------------------- -# Verify these jar files with varying extensions -#---------------------------------------------------------------- -# Mixed case extensions should not be loaded. -Failure "$javac" ${TESTTOOLVMOPTS} -classpath "MixJar/*" Main1b.java -Success "$javac" ${TESTTOOLVMOPTS} -classpath "MixJar/mix.jAr" Main1b.java -Failure "$javac" ${TESTTOOLVMOPTS} -classpath "MixJar/*" Main1b - -#upper case, .JAR, extension should be loaded -UnixOnly Success "$javac" ${TESTTOOLVMOPTS} -classpath "MixJar/*" Main2.java - Success "$javac" ${TESTTOOLVMOPTS} -classpath ".${PS}MixJar/*" Main2.java - -Success "$java" ${TESTVMOPTS} -classpath "MixJar/*${PS}." Main2 -# zip extensions should not be loaded -Failure "$javac" ${TESTTOOLVMOPTS} -classpath "MixJar/*" Main3.java -Success "$javac" ${TESTTOOLVMOPTS} -classpath "MixJar/mix3.zip" Main3.java -Failure "$java" ${TESTVMOPTS} -classpath "MixJar/*${PS}." Main3 -# unix "hidden" file -UnixOnly Success "$javac" ${TESTTOOLVMOPTS} -classpath "MixJar/*" Main4.java -UnixOnly Success "$java" ${TESTVMOPTS} -classpath "MixJar/*${PS}." Main4 - -Cleanup - -Bottom Line -#---------------------------------------------------------------- diff --git a/test/langtools/tools/javac/StringConcat/StringAppendEvaluatesInOrder.java b/test/langtools/tools/javac/StringConcat/StringAppendEvaluatesInOrder.java new file mode 100644 index 0000000000000..b2bd47acde000 --- /dev/null +++ b/test/langtools/tools/javac/StringConcat/StringAppendEvaluatesInOrder.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021, Google LLC. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8273914 + * @summary Indy string concat changes order of operations + * + * @clean * + * @compile -XDstringConcat=indy StringAppendEvaluatesInOrder.java + * @run main StringAppendEvaluatesInOrder + * + * @clean * + * @compile -XDstringConcat=indyWithConstants StringAppendEvaluatesInOrder.java + * @run main StringAppendEvaluatesInOrder + * + * @clean * + * @compile -XDstringConcat=inline StringAppendEvaluatesInOrder.java + * @run main StringAppendEvaluatesInOrder + */ + +public class StringAppendEvaluatesInOrder { + static String test() { + StringBuilder builder = new StringBuilder("foo"); + int i = 15; + return "Test: " + i + " " + (++i) + builder + builder.append("bar"); + } + + static String compoundAssignment() { + StringBuilder builder2 = new StringBuilder("foo"); + Object oo = builder2; + oo += "" + builder2.append("bar"); + return oo.toString(); + } + + public static void main(String[] args) throws Exception { + assertEquals(test(), "Test: 15 16foofoobar"); + assertEquals(compoundAssignment(), "foofoobar"); + } + + private static void assertEquals(String actual, String expected) { + if (!actual.equals(expected)) { + throw new AssertionError("expected: " + expected + ", actual: " + actual); + } + } +} diff --git a/test/langtools/tools/javac/StringConcat/WellKnownTypeSignatures.java b/test/langtools/tools/javac/StringConcat/WellKnownTypeSignatures.java new file mode 100644 index 0000000000000..07cdeef495615 --- /dev/null +++ b/test/langtools/tools/javac/StringConcat/WellKnownTypeSignatures.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021, Google LLC. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.tools.classfile.*; +import com.sun.tools.classfile.ConstantPool.*; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @bug 8273914 + * @summary Indy string concat changes order of operations + * @modules jdk.jdeps/com.sun.tools.classfile + * + * @clean * + * @compile -XDstringConcat=indy WellKnownTypeSignatures.java + * @run main WellKnownTypeSignatures + * + * @clean * + * @compile -XDstringConcat=indyWithConstants WellKnownTypeSignatures.java + * @run main WellKnownTypeSignatures + */ + +public class WellKnownTypeSignatures { + static List actualTypes; + + static int idx = 0; + + static boolean z = true; + static char c = (char) 42; + static short s = (short) 42; + static byte b = (byte) 42; + static int i = 42; + static long l = 42L; + static float f = 42.0f; + static double d = 42.0; + + public static void main(String[] argv) throws Exception { + readIndyTypes(); + + test("" + WellKnownTypeSignatures.class, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); + test("" + Boolean.valueOf(z), idx++, "(Ljava/lang/Boolean;)Ljava/lang/String;"); + test("" + Character.valueOf(c), idx++, "(Ljava/lang/Character;)Ljava/lang/String;"); + test("" + Byte.valueOf(b), idx++, "(Ljava/lang/Byte;)Ljava/lang/String;"); + test("" + Short.valueOf(s), idx++, "(Ljava/lang/Short;)Ljava/lang/String;"); + test("" + Integer.valueOf(i), idx++, "(Ljava/lang/Integer;)Ljava/lang/String;"); + test("" + Long.valueOf(l), idx++, "(Ljava/lang/Long;)Ljava/lang/String;"); + test("" + Double.valueOf(d), idx++, "(Ljava/lang/Double;)Ljava/lang/String;"); + test("" + Float.valueOf(f), idx++, "(Ljava/lang/Float;)Ljava/lang/String;"); + test("" + z, idx++, "(Z)Ljava/lang/String;"); + test("" + c, idx++, "(C)Ljava/lang/String;"); + test("" + b, idx++, "(B)Ljava/lang/String;"); + test("" + s, idx++, "(S)Ljava/lang/String;"); + test("" + i, idx++, "(I)Ljava/lang/String;"); + test("" + l, idx++, "(J)Ljava/lang/String;"); + test("" + d, idx++, "(D)Ljava/lang/String;"); + test("" + f, idx++, "(F)Ljava/lang/String;"); + } + + public static void test(String actual, int index, String expectedType) { + String actualType = actualTypes.get(index); + if (!actualType.equals(expectedType)) { + throw new IllegalStateException( + index + + " Unexpected type: expected = " + + expectedType + + ", actual = " + + actualType); + } + } + + public static void readIndyTypes() throws Exception { + actualTypes = new ArrayList(); + + ClassFile classFile = + ClassFile.read( + new File( + System.getProperty("test.classes", "."), + WellKnownTypeSignatures.class.getName() + ".class")); + ConstantPool constantPool = classFile.constant_pool; + + for (Method method : classFile.methods) { + if (method.getName(constantPool).equals("main")) { + Code_attribute code = (Code_attribute) method.attributes.get(Attribute.Code); + for (Instruction i : code.getInstructions()) { + if (i.getOpcode() == Opcode.INVOKEDYNAMIC) { + CONSTANT_InvokeDynamic_info indyInfo = + (CONSTANT_InvokeDynamic_info) + constantPool.get(i.getUnsignedShort(1)); + CONSTANT_NameAndType_info natInfo = indyInfo.getNameAndTypeInfo(); + actualTypes.add(natInfo.getType()); + } + } + } + } + } +} diff --git a/test/langtools/tools/javac/StringConcat/WellKnownTypes.java b/test/langtools/tools/javac/StringConcat/WellKnownTypes.java new file mode 100644 index 0000000000000..39150cfc4da42 --- /dev/null +++ b/test/langtools/tools/javac/StringConcat/WellKnownTypes.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021, Google LLC. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.tools.classfile.*; +import com.sun.tools.classfile.ConstantPool.*; + +/* + * @test + * @bug 8273914 + * @summary Indy string concat changes order of operations + * @modules jdk.jdeps/com.sun.tools.classfile + * + * @compile -XDstringConcat=indy WellKnownTypes.java + * @run main WellKnownTypes + * + * @compile -XDstringConcat=indyWithConstants WellKnownTypes.java + * @run main WellKnownTypes + * + * @compile -XDstringConcat=inline WellKnownTypes.java + * @run main WellKnownTypes + */ + +public class WellKnownTypes { + static int idx = 0; + + static boolean z = true; + static char c = (char) 42; + static byte b = (byte) 43; + static short s = (short) 44; + static int i = 45; + static long l = 46L; + static float f = 47.0f; + static double d = 48.0; + + public static void main(String[] argv) throws Exception { + test("" + WellKnownTypes.class, idx++, "class WellKnownTypes"); + test("" + Boolean.valueOf(z), idx++, "true"); + test("" + Character.valueOf(c), idx++, "*"); + test("" + Byte.valueOf(b), idx++, "43"); + test("" + Short.valueOf(s), idx++, "44"); + test("" + Integer.valueOf(i), idx++, "45"); + test("" + Long.valueOf(l), idx++, "46"); + test("" + Float.valueOf(f), idx++, "47.0"); + test("" + Double.valueOf(d), idx++, "48.0"); + test("" + z, idx++, "true"); + test("" + c, idx++, "*"); + test("" + b, idx++, "43"); + test("" + s, idx++, "44"); + test("" + i, idx++, "45"); + test("" + l, idx++, "46"); + test("" + f, idx++, "47.0"); + test("" + d, idx++, "48.0"); + } + + public static void test(String actual, int index, String expected) { + if (!actual.equals(expected)) { + throw new IllegalStateException( + index + " Unexpected: expected = " + expected + ", actual = " + actual); + } + } +} diff --git a/test/langtools/tools/javac/StringConcat/access/Test.java b/test/langtools/tools/javac/StringConcat/access/Test.java index 72237e8b4ef7b..578bbddb1188c 100644 --- a/test/langtools/tools/javac/StringConcat/access/Test.java +++ b/test/langtools/tools/javac/StringConcat/access/Test.java @@ -56,109 +56,109 @@ public static void main(String[] argv) throws Exception { // ---------------------------------------------------------------------------- // public Private_PublicClass c1 = new Private_PublicClass(); - test("" + holder.c1, idx++, "(Lp1/PublicClass;)Ljava/lang/String;"); + test("" + holder.c1, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Private_PublicInterface c2 = new Private_PublicInterface(); - test("" + holder.c2, idx++, "(Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.c2, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Private_PrivateInterface1 c3 = new Private_PrivateInterface1(); - test("" + holder.c3, idx++, "(Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.c3, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Private_PrivateInterface2 c4 = new Private_PrivateInterface2(); - test("" + holder.c4, idx++, "(Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.c4, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PublicClass c5 = new Public_PublicClass(); - test("" + holder.c5, idx++, "(Lp1/Public_PublicClass;)Ljava/lang/String;"); + test("" + holder.c5, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PublicInterface c6 = new Public_PublicInterface(); - test("" + holder.c6, idx++, "(Lp1/Public_PublicInterface;)Ljava/lang/String;"); + test("" + holder.c6, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PrivateInterface1 c7 = new Public_PrivateInterface1(); - test("" + holder.c7, idx++, "(Lp1/Public_PrivateInterface1;)Ljava/lang/String;"); + test("" + holder.c7, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PrivateInterface2 c8 = new Public_PrivateInterface2(); - test("" + holder.c8, idx++, "(Lp1/Public_PrivateInterface2;)Ljava/lang/String;"); + test("" + holder.c8, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // ---------------------------------------------------------------------------- // public Private_PublicClass[] ac1 = new Private_PublicClass[0]; - test("" + holder.ac1, idx++, "([Lp1/PublicClass;)Ljava/lang/String;"); + test("" + holder.ac1, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Private_PublicInterface[] ac2 = new Private_PublicInterface[0]; - test("" + holder.ac2, idx++, "([Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.ac2, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Private_PrivateInterface1[] ac3 = new Private_PrivateInterface1[0]; - test("" + holder.ac3, idx++, "([Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.ac3, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Private_PrivateInterface2[] ac4 = new Private_PrivateInterface2[0]; - test("" + holder.ac4, idx++, "([Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.ac4, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PublicClass[] ac5 = new Public_PublicClass[0]; - test("" + holder.ac5, idx++, "([Lp1/Public_PublicClass;)Ljava/lang/String;"); + test("" + holder.ac5, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PublicInterface[] ac6 = new Public_PublicInterface[0]; - test("" + holder.ac6, idx++, "([Lp1/Public_PublicInterface;)Ljava/lang/String;"); + test("" + holder.ac6, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PrivateInterface1[] ac7 = new Public_PrivateInterface1[0]; - test("" + holder.ac7, idx++, "([Lp1/Public_PrivateInterface1;)Ljava/lang/String;"); + test("" + holder.ac7, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PrivateInterface2[] ac8 = new Public_PrivateInterface2[0]; - test("" + holder.ac8, idx++, "([Lp1/Public_PrivateInterface2;)Ljava/lang/String;"); + test("" + holder.ac8, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // ---------------------------------------------------------------------------- // public Private_PublicClass[][] aac1 = new Private_PublicClass[0][]; - test("" + holder.aac1, idx++, "([[Lp1/PublicClass;)Ljava/lang/String;"); + test("" + holder.aac1, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Private_PublicInterface[][] aac2 = new Private_PublicInterface[0][]; - test("" + holder.aac2, idx++, "([[Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.aac2, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Private_PrivateInterface1[][] aac3 = new Private_PrivateInterface1[0][]; - test("" + holder.aac3, idx++, "([[Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.aac3, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Private_PrivateInterface2[][] aac4 = new Private_PrivateInterface2[0][]; - test("" + holder.aac4, idx++, "([[Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.aac4, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PublicClass[][] aac5 = new Public_PublicClass[0][]; - test("" + holder.aac5, idx++, "([[Lp1/Public_PublicClass;)Ljava/lang/String;"); + test("" + holder.aac5, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PublicInterface[][] aac6 = new Public_PublicInterface[0][]; - test("" + holder.aac6, idx++, "([[Lp1/Public_PublicInterface;)Ljava/lang/String;"); + test("" + holder.aac6, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PrivateInterface1[][] aac7 = new Public_PrivateInterface1[0][]; - test("" + holder.aac7, idx++, "([[Lp1/Public_PrivateInterface1;)Ljava/lang/String;"); + test("" + holder.aac7, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public Public_PrivateInterface2[][] aac8 = new Public_PrivateInterface2[0][]; - test("" + holder.aac8, idx++, "([[Lp1/Public_PrivateInterface2;)Ljava/lang/String;"); + test("" + holder.aac8, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // ---------------------------------------------------------------------------- // public PublicInterface i1 = new Private_PublicInterface(); - test("" + holder.i1, idx++, "(Lp1/PublicInterface;)Ljava/lang/String;"); + test("" + holder.i1, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public PrivateInterface1 i2 = new Private_PrivateInterface1(); - test("" + holder.i2, idx++, "(Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.i2, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public PrivateInterface2 i3 = new Private_PrivateInterface2(); - test("" + holder.i3, idx++, "(Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.i3, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public PublicInterface[] ai1 = new Private_PublicInterface[0]; - test("" + holder.ai1, idx++, "([Lp1/PublicInterface;)Ljava/lang/String;"); + test("" + holder.ai1, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public PrivateInterface1[] ai2 = new Private_PrivateInterface1[0]; - test("" + holder.ai2, idx++, "([Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.ai2, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public PrivateInterface2[] ai3 = new Private_PrivateInterface2[0]; - test("" + holder.ai3, idx++, "([Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.ai3, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public PublicInterface[][] aai1 = new Private_PublicInterface[0][]; - test("" + holder.aai1, idx++, "([[Lp1/PublicInterface;)Ljava/lang/String;"); + test("" + holder.aai1, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public PrivateInterface1[][] aai2 = new Private_PrivateInterface1[0][]; - test("" + holder.aai2, idx++, "([[Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.aai2, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); // public PrivateInterface2[][] aai3 = new Private_PrivateInterface2[0][]; - test("" + holder.aai3, idx++, "([[Ljava/lang/Object;)Ljava/lang/String;"); + test("" + holder.aai3, idx++, "(Ljava/lang/String;)Ljava/lang/String;"); } diff --git a/test/langtools/tools/javac/generics/ParametricException.java b/test/langtools/tools/javac/generics/parametricException/ParametricException.java similarity index 100% rename from test/langtools/tools/javac/generics/ParametricException.java rename to test/langtools/tools/javac/generics/parametricException/ParametricException.java diff --git a/test/langtools/tools/javac/newlines/NewLineTest.java b/test/langtools/tools/javac/newlines/NewLineTest.java index b1567d363b0c1..35c60e5965360 100644 --- a/test/langtools/tools/javac/newlines/NewLineTest.java +++ b/test/langtools/tools/javac/newlines/NewLineTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,8 +58,9 @@ public static void main(String args[]) throws Exception { .options("-J-Dline.separator='@'") .run(Task.Expect.FAIL); - List lines = Files.readAllLines(javacOutput.toPath(), - Charset.defaultCharset()); + String encoding = System.getProperty("native.encoding"); + Charset cs = (encoding != null) ? Charset.forName(encoding) : Charset.defaultCharset(); + List lines = Files.readAllLines(javacOutput.toPath(), cs); if (lines.size() != 1) { throw new AssertionError("The compiler output should have one line only"); } diff --git a/test/langtools/tools/javac/options/release/ReleaseOption.java b/test/langtools/tools/javac/options/release/ReleaseOption.java index 301482fb4c424..369d5cd413f10 100644 --- a/test/langtools/tools/javac/options/release/ReleaseOption.java +++ b/test/langtools/tools/javac/options/release/ReleaseOption.java @@ -1,10 +1,10 @@ /** * @test /nodynamiccopyright/ * @bug 8072480 - * @summary Verify that javac rejects Java 8 program with --release 7 + * @summary Verify that javac rejects Java 17 program with --release 11 * @compile ReleaseOption.java - * @compile/fail/ref=ReleaseOption-release7.out -XDrawDiagnostics --release 7 -Xlint:-options ReleaseOption.java + * @compile/fail/ref=ReleaseOption.out -XDrawDiagnostics --release 11 ReleaseOption.java */ -interface ReleaseOption extends java.util.stream.Stream { +interface ReleaseOption extends java.util.random.RandomGenerator { } diff --git a/test/langtools/tools/javac/options/release/ReleaseOption.out b/test/langtools/tools/javac/options/release/ReleaseOption.out new file mode 100644 index 0000000000000..be884bf1ed1f2 --- /dev/null +++ b/test/langtools/tools/javac/options/release/ReleaseOption.out @@ -0,0 +1,2 @@ +ReleaseOption.java:9:49: compiler.err.doesnt.exist: java.util.random +1 error diff --git a/test/langtools/tools/javac/options/release/ReleaseOptionThroughAPI.java b/test/langtools/tools/javac/options/release/ReleaseOptionThroughAPI.java index 4198d9cece9f7..981e92838694a 100644 --- a/test/langtools/tools/javac/options/release/ReleaseOptionThroughAPI.java +++ b/test/langtools/tools/javac/options/release/ReleaseOptionThroughAPI.java @@ -50,11 +50,11 @@ void run() throws IOException { PrintWriter outWriter = new PrintWriter(out)) { Iterable input = fm.getJavaFileObjects(System.getProperty("test.src") + "/ReleaseOption.java"); - List options = Arrays.asList("--release", "7", "-XDrawDiagnostics", "-Xlint:-options"); + List options = Arrays.asList("--release", "11", "-XDrawDiagnostics", "-d", "."); compiler.getTask(outWriter, fm, null, options, null, input).call(); String expected = - "ReleaseOption.java:9:49: compiler.err.doesnt.exist: java.util.stream" + lineSep + + "ReleaseOption.java:9:49: compiler.err.doesnt.exist: java.util.random" + lineSep + "1 error" + lineSep; if (!expected.equals(out.toString())) { throw new AssertionError("Unexpected output: " + out.toString()); diff --git a/test/langtools/tools/javac/patterns/T8312229.java b/test/langtools/tools/javac/patterns/T8312229.java new file mode 100644 index 0000000000000..20c3eb3e02326 --- /dev/null +++ b/test/langtools/tools/javac/patterns/T8312229.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + /* + * @test + * @bug 8312229 + * @summary Ensure javac does not crash when a variable is used from an anonymous class + * @compile --enable-preview -source ${jdk.version} T8312229.java + */ +public class T8312229 { + void test(Object o) { + Runnable r = () -> { + var l = switch (o) { + default -> { + Integer i = 42; + yield new Runnable() { + public void run() { + i.toString(); // should not crash here + } + }; + } + }; + }; + } +} diff --git a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java index 85a6f6af9d896..8c8c864837d28 100644 --- a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java +++ b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java @@ -40,6 +40,8 @@ import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; @@ -137,7 +139,7 @@ public Void scan(Element elem, Void ignore) { */ class TestTypeScanner extends TypeScanner { Element elem; - NavigableMap toBeFound; + NavigableMap> toBeFound; int count = 0; Set seen = new HashSet<>(); @@ -145,10 +147,10 @@ class TestTypeScanner extends TypeScanner { super(types); this.elem = elem; - NavigableMap testByPos = new TreeMap<>(); + NavigableMap> testByPos = new TreeMap<>(); for (AnnotationMirror test : tests) { for (int pos : getPosn(test)) { - testByPos.put(pos, test); + testByPos.computeIfAbsent(pos, ArrayList::new).add(test); } } this.toBeFound = testByPos; @@ -170,17 +172,18 @@ Void scan(TypeMirror t, Void ignore) { out.println("scan " + count + ": " + t); if (toBeFound.size() > 0) { if (toBeFound.firstKey().equals(count)) { - AnnotationMirror test = toBeFound.pollFirstEntry().getValue(); - String annoType = getAnnoType(test); - AnnotationMirror anno = getAnnotation(t, annoType); - if (anno == null) { - error(elem, "annotation not found on " + count + ": " + t); - } else { - String v = getValue(anno, "value").toString(); - if (v.equals(getExpect(test))) { - out.println("found " + anno + " as expected"); + for (AnnotationMirror test : toBeFound.pollFirstEntry().getValue()) { + String annoType = getAnnoType(test); + AnnotationMirror anno = getAnnotation(t, annoType); + if (anno == null) { + error(elem, "annotation not found on " + count + ": " + t); } else { - error(elem, "Unexpected value: " + v + ", expected: " + getExpect(test)); + String v = getValue(anno, "value").toString(); + if (v.equals(getExpect(test))) { + out.println("found " + anno + " as expected"); + } else { + error(elem, "Unexpected value: " + v + ", expected: " + getExpect(test)); + } } } } else if (count > toBeFound.firstKey()) { @@ -405,6 +408,12 @@ R scan(Iterable iter, P p) { int value(); } + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + public @interface TD { + int value(); + } + // Test cases // TODO: add more cases for arrays @@ -527,6 +536,10 @@ public class Inner6 {} @Test(posn=1, annoType=TA.class, expect="23") public Set<@TA(23) ? super Object> f9; + @Test(posn=0, annoType=TA.class, expect="1") + @Test(posn=0, annoType=TD.class, expect="2") + public @TA(1) @TD(2) int f10; + // Test type use annotations on uses of type variables @Test(posn=5, annoType = TA.class, expect = "25") @Test(posn=5, annoType = TB.class, expect = "26") diff --git a/test/langtools/tools/javac/warnings/Serial.java b/test/langtools/tools/javac/warnings/Serial/Serial.java similarity index 100% rename from test/langtools/tools/javac/warnings/Serial.java rename to test/langtools/tools/javac/warnings/Serial/Serial.java diff --git a/test/langtools/tools/javac/warnings/Serial.out b/test/langtools/tools/javac/warnings/Serial/Serial.out similarity index 100% rename from test/langtools/tools/javac/warnings/Serial.out rename to test/langtools/tools/javac/warnings/Serial/Serial.out diff --git a/test/langtools/tools/lib/toolbox/ToolBox.java b/test/langtools/tools/lib/toolbox/ToolBox.java index 9dbb26e144814..bc76a12cad076 100644 --- a/test/langtools/tools/lib/toolbox/ToolBox.java +++ b/test/langtools/tools/lib/toolbox/ToolBox.java @@ -245,7 +245,7 @@ public void copyFile(String from, String to) throws IOException { public void copyFile(Path from, Path to) throws IOException { if (Files.isDirectory(to)) { to = to.resolve(from.getFileName()); - } else { + } else if (to.getParent() != null) { Files.createDirectories(to.getParent()); } Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); diff --git a/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java b/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java index f8ce856bddd18..e78e200ac248c 100644 --- a/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java +++ b/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,7 +53,7 @@ private static enum MethodGroup { IGNORED("isEmulatedClient", "isDebugBuild", "isFastDebugBuild", "isMusl", "isSlowDebugBuild", "hasSA", "isRoot", "isTieredSupported", "areCustomLoadersSupportedForCDS", "isDefaultCDSArchiveSupported", - "isHardenedOSX", "hasOSXPlistEntries", "isOracleLinux7"); + "isHardenedOSX", "hasOSXPlistEntries", "isOracleLinux7", "isOnWayland"); public final List methodNames; diff --git a/test/lib-test/jdk/test/lib/process/ProcessToolsLastLineTest.java b/test/lib-test/jdk/test/lib/process/ProcessToolsLastLineTest.java new file mode 100644 index 0000000000000..54b5a3bc2205b --- /dev/null +++ b/test/lib-test/jdk/test/lib/process/ProcessToolsLastLineTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8303697 + * @summary Test verifies that ProcessTools.startProcess() print all lines even the last line doesn't end with '\n' + * @library /test/lib + * @run main ProcessToolsLastLineTest + */ + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Asserts; + +public class ProcessToolsLastLineTest { + + static void test(String output) throws Exception { + final StringBuffer sb = new StringBuffer(); + Process p = ProcessTools.startProcess("process", + ProcessTools.createLimitedTestJavaProcessBuilder(ProcessToolsLastLineTest.class.getName(), output), + line -> { sb.append(line);}); + p.waitFor(); + String expectedOutput = output.replace("\n", ""); + Asserts.assertEQ(sb.toString(), expectedOutput); + } + + public static void main(String[] args) throws Exception { + + // The line which exceeds internal StreamPumper buffer (256 bytes) + String VERY_LONG_LINE = "X".repeat(257); + if (args.length > 0) { + System.out.print(args[0]); + } else { + test("\n"); + test("\nARG1"); + test("\nARG1\n"); + test("ARG1\n"); + test("ARG1"); + test("ARG1\nARG2"); + test("ARG1\nARG2\n"); + test("\nARG1\nARG2\n"); + test("\nARG1\n" + VERY_LONG_LINE + "\nARG2\n"); + test("\nARG1\n" + VERY_LONG_LINE); + test("\nARG1\n" + VERY_LONG_LINE + VERY_LONG_LINE + VERY_LONG_LINE + "\nARG2\n"); + test("\nARG1\n" + VERY_LONG_LINE + VERY_LONG_LINE + VERY_LONG_LINE); + + } + + } +} diff --git a/test/lib-test/jdk/test/lib/process/ProcessToolsStartProcessTest.java b/test/lib-test/jdk/test/lib/process/ProcessToolsStartProcessTest.java new file mode 100644 index 0000000000000..02c10c6106661 --- /dev/null +++ b/test/lib-test/jdk/test/lib/process/ProcessToolsStartProcessTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Unit test for ProcessTools.startProcess() + * @library /test/lib + * @compile ProcessToolsStartProcessTest.java + * @run main ProcessToolsStartProcessTest + */ + +import java.util.function.Consumer; +import java.io.File; + +import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.Utils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class ProcessToolsStartProcessTest { + static String output = ""; + + private static Consumer outputConsumer = s -> { + output += s + "\n"; + }; + + static boolean testStartProcess(int numOfLines, boolean withConsumer) throws Exception { + boolean success = true; + output = ""; + Process p; + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); + launcher.addToolArg("-cp"); + launcher.addToolArg(Utils.TEST_CLASSES); + launcher.addToolArg("ProcessToolsStartProcessTest"); + launcher.addToolArg(Integer.toString(numOfLines)); + ProcessBuilder pb = new ProcessBuilder(); + pb.command(launcher.getCommand()); + + System.out.println("DEBUG: Test with withConsumer=" + withConsumer); + System.out.println("DEBUG: about to start process."); + if (withConsumer) { + p = ProcessTools.startProcess("java", pb, outputConsumer); + } else { + p = ProcessTools.startProcess("java", pb); + } + OutputAnalyzer out = new OutputAnalyzer(p); + + System.out.println("DEBUG: process started."); + p.waitFor(); + if (p.exitValue() != 0) { + throw new RuntimeException("Bad exit value: " + p.exitValue()); + } + + if (withConsumer) { + int numLines = output.split("\n").length; + if (numLines != numOfLines) { + System.out.print("FAILED: wrong number of lines in Consumer output\n"); + success = false; + System.out.print(output); + } + } + + int numLinesOut = out.getStdout().split("\n").length; + if (numLinesOut != numOfLines) { + System.out.print("FAILED: wrong number of lines in OutputAnalyzer output\n"); + System.out.print(out.getStdout()); + success = false; + } + + return success; + } + + public static void main(String[] args) throws Exception { + if (args.length > 0) { + int iter = Integer.parseInt(args[0]); + for (int i = 0; i < iter; i++) { + System.out.println("A line on stdout: " + i + " " + ".".repeat(i)); + } + } else { + for (int i = 1; i < 5; i++) { + System.out.println("ITERATION " + i); + boolean test1Result = testStartProcess(i * 10 + i, false); + if (!test1Result) { + throw new RuntimeException("First test (no consumer) failed. See output for details."); + } + boolean test2Result = testStartProcess(i * 10 + i, true); + if (!test2Result) { + throw new RuntimeException("Second test (with consumer) failed. See output for details."); + } + } + } + } +} diff --git a/test/lib-test/jdk/test/lib/process/proc/A.java b/test/lib-test/jdk/test/lib/process/proc/A.java new file mode 100644 index 0000000000000..8f80202b69755 --- /dev/null +++ b/test/lib-test/jdk/test/lib/process/proc/A.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +public class A { + public static void main(String[] args) throws Exception { + B.go(); + } +} diff --git a/test/lib-test/jdk/test/lib/process/proc/B.java b/test/lib-test/jdk/test/lib/process/proc/B.java new file mode 100644 index 0000000000000..ef92b3f77a270 --- /dev/null +++ b/test/lib-test/jdk/test/lib/process/proc/B.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +public class B { + public static void go() { + System.out.println("Hello"); + System.err.println("World"); + } +} diff --git a/test/lib-test/jdk/test/lib/process/proc/Launcher.java b/test/lib-test/jdk/test/lib/process/proc/Launcher.java new file mode 100644 index 0000000000000..857586586c615 --- /dev/null +++ b/test/lib-test/jdk/test/lib/process/proc/Launcher.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import jdk.test.lib.process.Proc; + +/* + * @test + * @bug 8305846 + * @library /test/lib + */ +public class Launcher { + public static void main(String[] args) throws Exception { + Proc.create("A") + .compile() + .start() + .output() + .stdoutShouldContain("Hello") + .stderrShouldContain("World"); + } +} diff --git a/test/lib-test/jdk/test/whitebox/vm_flags/BooleanTest.java b/test/lib-test/jdk/test/whitebox/vm_flags/BooleanTest.java index 59d699138816b..8843e34ce45b9 100644 --- a/test/lib-test/jdk/test/whitebox/vm_flags/BooleanTest.java +++ b/test/lib-test/jdk/test/whitebox/vm_flags/BooleanTest.java @@ -69,7 +69,7 @@ public static void main(String[] args) throws Exception { } private static void testFunctional(boolean value) throws Exception { - ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-Xbootclasspath/a:.", "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", diff --git a/test/lib/jdk/test/lib/NetworkConfiguration.java b/test/lib/jdk/test/lib/NetworkConfiguration.java index 386a2bf0a6f08..8ea10ede6a4a2 100644 --- a/test/lib/jdk/test/lib/NetworkConfiguration.java +++ b/test/lib/jdk/test/lib/NetworkConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -176,17 +176,6 @@ private boolean supportsIp4Multicast(NetworkInterface nif) { return false; } - // On AIX there is a bug: - // When IPv6 is enabled on the system, the JDK opens sockets as AF_INET6. - // If there's an interface configured with IPv4 addresses only, it should - // be able to become the network interface for a multicast socket (that - // could be in both, IPv4 or IPv6 space). But both possible setsockopt - // calls for either IPV6_MULTICAST_IF or IP_MULTICAST_IF return - // EADDRNOTAVAIL. So we must skip such interfaces here. - if (Platform.isAix() && isIPv6Available() && !hasIp6Addresses(nif)) { - return false; - } - if (Platform.isOSX()) { // multicasting may not work on interfaces that only // have link local addresses diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java index 00e922bb15a8d..4f5b61a1f43c1 100644 --- a/test/lib/jdk/test/lib/Platform.java +++ b/test/lib/jdk/test/lib/Platform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static java.util.Locale.ROOT; public class Platform { public static final String vmName = privilegedGetProperty("java.vm.name"); @@ -134,7 +135,7 @@ public static boolean isWindows() { } private static boolean isOs(String osname) { - return osName.toLowerCase().startsWith(osname.toLowerCase()); + return osName.toLowerCase(ROOT).startsWith(osname.toLowerCase(ROOT)); } public static String getOsName() { @@ -175,15 +176,15 @@ public static int getOsVersionMinor() { } public static boolean isDebugBuild() { - return (jdkDebug.toLowerCase().contains("debug")); + return (jdkDebug.toLowerCase(ROOT).contains("debug")); } public static boolean isSlowDebugBuild() { - return (jdkDebug.toLowerCase().equals("slowdebug")); + return (jdkDebug.toLowerCase(ROOT).equals("slowdebug")); } public static boolean isFastDebugBuild() { - return (jdkDebug.toLowerCase().equals("fastdebug")); + return (jdkDebug.toLowerCase(ROOT).equals("fastdebug")); } public static String getVMVersion() { @@ -346,8 +347,8 @@ private static boolean isArch(String archnameRE) { } public static boolean isOracleLinux7() { - if (System.getProperty("os.name").toLowerCase().contains("linux") && - System.getProperty("os.version").toLowerCase().contains("el")) { + if (System.getProperty("os.name").toLowerCase(ROOT).contains("linux") && + System.getProperty("os.version").toLowerCase(ROOT).contains("el")) { Pattern p = Pattern.compile("el(\\d+)"); Matcher m = p.matcher(System.getProperty("os.version")); if (m.find()) { @@ -451,4 +452,13 @@ public static boolean isDefaultCDSArchiveSupported() { public static boolean areCustomLoadersSupportedForCDS() { return (is64bit() && (isLinux() || isOSX())); } + + /** + * Checks if the current system is running on Wayland display server on Linux. + * + * @return {@code true} if the system is running on Wayland display server + */ + public static boolean isOnWayland() { + return System.getenv("WAYLAND_DISPLAY") != null; + } } diff --git a/test/lib/jdk/test/lib/SA/SATestUtils.java b/test/lib/jdk/test/lib/SA/SATestUtils.java index d4daf5ef145dd..2f98cf99357f4 100644 --- a/test/lib/jdk/test/lib/SA/SATestUtils.java +++ b/test/lib/jdk/test/lib/SA/SATestUtils.java @@ -159,6 +159,7 @@ public static void addPrivilegesIfNeeded(ProcessBuilder pb) { * if we are root, so return true. Then return false for an expected denial * if "ptrace_scope" is 1, and true otherwise. */ + @SuppressWarnings("removal") private static boolean canPtraceAttachLinux() throws IOException { // SELinux deny_ptrace: var deny_ptrace = Paths.get("/sys/fs/selinux/booleans/deny_ptrace"); diff --git a/test/lib/jdk/test/lib/SecurityTools.java b/test/lib/jdk/test/lib/SecurityTools.java index ee786eb365c50..bc198a712b843 100644 --- a/test/lib/jdk/test/lib/SecurityTools.java +++ b/test/lib/jdk/test/lib/SecurityTools.java @@ -40,9 +40,8 @@ /** * Run security tools (including jarsigner and keytool) in a new process. * The en_US locale is always used so a test can always match output to - * English text. {@code /dev/urandom} is used as entropy source so tool will - * not block because of entropy scarcity. {@code -Jvm-options} is supported - * as an argument. + * English text. An argument can be a normal string, + * {@code -Jvm-options}, {@code $sysProp} or {@code -J$sysProp}. */ public class SecurityTools { @@ -58,15 +57,19 @@ public static ProcessBuilder getProcessBuilder(String tool, List args) { JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK(tool) .addVMArg("-Duser.language=en") .addVMArg("-Duser.country=US"); - if (!Platform.isWindows()) { - launcher.addVMArg("-Djava.security.egd=file:/dev/./urandom"); - } for (String arg : args) { if (arg.startsWith("-J")) { - launcher.addVMArg(arg.substring(2)); + String jarg = arg.substring(2); + if (jarg.length() > 1 && jarg.charAt(0) == '$') { + launcher.addVMArg(System.getProperty(jarg.substring(1))); + } else { + launcher.addVMArg(jarg); + } } else if (Platform.isWindows() && arg.isEmpty()) { // JDK-6518827: special handling for empty argument on Windows launcher.addToolArg("\"\""); + } else if (arg.length() > 1 && arg.charAt(0) == '$') { + launcher.addToolArg(System.getProperty(arg.substring(1))); } else { launcher.addToolArg(arg); } diff --git a/test/lib/jdk/test/lib/StringArrayUtils.java b/test/lib/jdk/test/lib/StringArrayUtils.java new file mode 100644 index 0000000000000..11124701dceba --- /dev/null +++ b/test/lib/jdk/test/lib/StringArrayUtils.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib; + +import java.util.ArrayList; + +public class StringArrayUtils { + /** + * The various concat() functions in this class can be used for building + * a command-line argument array for ProcessTools.createTestJavaProcessBuilder(), + * etc. When some of the arguments are conditional, this is more convenient + * than alternatives like ArrayList. + * + * Example: + * + *
      +     *     String args[] = StringArrayUtils.concat("-Xint", "-Xmx32m");
      +     *     if (verbose) {
      +     *         args = StringArrayUtils.concat(args, "-verbose");
      +     *     }
      +     *     args = StringArrayUtils.concat(args, "HelloWorld");
      +     *     ProcessTools.createTestJavaProcessBuilder(args);
      +     * 
      + */ + public static String[] concat(String... args) { + return args; + } + + public static String[] concat(String[] prefix, String... extra) { + String[] ret = new String[prefix.length + extra.length]; + System.arraycopy(prefix, 0, ret, 0, prefix.length); + System.arraycopy(extra, 0, ret, prefix.length, extra.length); + return ret; + } + + public static String[] concat(String prefix, String[] extra) { + String[] ret = new String[1 + extra.length]; + ret[0] = prefix; + System.arraycopy(extra, 0, ret, 1, extra.length); + return ret; + } +} diff --git a/test/lib/jdk/test/lib/UIBuilder.java b/test/lib/jdk/test/lib/UIBuilder.java new file mode 100644 index 0000000000000..c58f563ff5285 --- /dev/null +++ b/test/lib/jdk/test/lib/UIBuilder.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.test.lib; + +import javax.swing.*; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/** + * This is a common library for building UI for testing purposes. + */ +public class UIBuilder { + + /** + * Creates a {@link javax.swing.JDialog} object with a fixed layout that contains + * an instructions {@link javax.swing.JTextArea} and a message + * {@link javax.swing.JTextArea} for displaying text contents. Text contents can + * be set by using {@code setInstruction} method and {@code setMessage} method. + * + * The {@link javax.swing.JDialog} object also a pass {@link javax.swing.JButton} + * and a fail {@link javax.swing.JButton} to indicate test result. Buttons' action + * can be bound by using {@code setPassAction} and {@code setFailAction}. + */ + public static class DialogBuilder { + private JDialog dialog; + private JTextArea instructionsText; + private JTextArea messageText; + private JButton pass; + private JButton fail; + + /** + * Construct a new DialogBuilder object. + */ + public DialogBuilder() { + dialog = new JDialog(new JFrame()); + dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + instructionsText = new JTextArea("", 5, 100); + + dialog.add("North", new JScrollPane(instructionsText, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS)); + + messageText = new JTextArea("", 20, 100); + dialog.add("Center", new JScrollPane(messageText, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS)); + + JPanel buttons = new JPanel(); + pass = new JButton("pass"); + pass.setActionCommand("pass"); + buttons.add("East", pass); + + fail = new JButton("fail"); + fail.setActionCommand("fail"); + buttons.add("West", fail); + + dialog.add("South", buttons); + } + + /** + * Returns this {@code DialogBuilder} setting the dialog's title to a new value. + * @param title a string value + * @returns this DialogBuilder + */ + public DialogBuilder setTitle(String title) { + dialog.setTitle(title); + return this; + } + + /** + * Returns this {@code DialogBuilder} setting the instruction text to a new + * value. + * @param instruction a string value + * @returns this DialogBuilder + */ + public DialogBuilder setInstruction(String instruction) { + instructionsText.setText("Test instructions:\n" + instruction); + return this; + } + + /** + * Returns this {@code DialogBuilder} setting the message text to a new value. + * @param message a string value + * @returns this DialogBuilder + */ + public DialogBuilder setMessage(String message) { + messageText.setText(message); + return this; + } + + /** + * Returns this {@code DialogBuilder} setting pass button action to + * {@link java.awt.event.ActionListener}. + * @param action an action to perform on button click + * @returns this DialogBuilder + */ + public DialogBuilder setPassAction(ActionListener action) { + pass.addActionListener(action); + return this; + } + + /** + * Returns this {@code DialogBuilder} setting fail button action to + * {@link java.awt.event.ActionListener}. + * @param action an action to perform on button click + * @returns this DialogBuilder + */ + public DialogBuilder setFailAction(ActionListener action) { + fail.addActionListener(action); + return this; + } + + /** + * Returns this {@code DialogBuilder} setting window-closing action to + * {@link java.lang.Runnable}. + * @param action a runnerable action to perform on window close + * @returns this DialogBuilder + */ + public DialogBuilder setCloseAction(Runnable action) { + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + super.windowClosing(e); + action.run(); + } + }); + return this; + } + + /** + * Returns a {@link javax.swing.JDialog} window. + * @returns a JDialog + */ + public JDialog build() { + dialog.pack(); + return dialog; + } + } +} diff --git a/test/lib/jdk/test/lib/Utils.java b/test/lib/jdk/test/lib/Utils.java index f84ddab6d555e..50146100677ba 100644 --- a/test/lib/jdk/test/lib/Utils.java +++ b/test/lib/jdk/test/lib/Utils.java @@ -23,7 +23,11 @@ package jdk.test.lib; +import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.FilenameFilter; import java.io.IOException; import java.net.Inet6Address; import java.net.InetAddress; @@ -35,11 +39,16 @@ import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.nio.file.CopyOption; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclEntryType; +import java.nio.file.attribute.AclFileAttributeView; import java.nio.file.attribute.FileAttribute; import java.nio.channels.SocketChannel; +import java.nio.file.attribute.PosixFilePermissions; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -53,12 +62,14 @@ import java.util.LinkedList; import java.util.List; import java.util.Random; +import java.util.Set; import java.util.function.BooleanSupplier; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import static jdk.test.lib.Asserts.assertTrue; import jdk.test.lib.process.ProcessTools; @@ -821,4 +832,220 @@ public static Path createTempDirectory(String prefix, FileAttribute... attrs) Path dir = Paths.get(System.getProperty("user.dir", ".")); return Files.createTempDirectory(dir, prefix, attrs); } + + /** + * Converts slashes in a pathname to backslashes + * if slashes is not the file separator. + */ + public static String convertPath(String path) { + // No need to do the conversion if file separator is '/' + if (FILE_SEPARATOR.length() == 1 && FILE_SEPARATOR.charAt(0) == '/') { + return path; + } + + char[] cs = path.toCharArray(); + for (int i = 0; i < cs.length; i++) { + if (cs[i] == '/') { + cs[i] = '\\'; + } + } + String newPath = new String(cs); + return newPath; + } + + /** + * Return file directories that satisfy the specified filter. + * + * @param searchDirectory the base directory to search + * @param filter a filename filter + * @return file directories + */ + public static List findFiles(Path searchDirectory, + FilenameFilter filter) { + return Arrays.stream(searchDirectory.toFile().listFiles(filter)) + .map(f -> f.toPath()) + .collect(Collectors.toList()); + } + + /** + * Copy files to the target path. + * + * @param source the paths to the files to copy + * @param target the path to the target files + * @param filenameMapper mapper function applied to filenames + * @param options options specifying how the copy should be done + * @return the paths to the target files + * @throws IOException if error occurs + */ + public static List copyFiles(List source, Path target, + Function filenameMapper, + CopyOption... options) throws IOException { + List result = new ArrayList<>(); + + if (!target.toFile().exists()) { + Files.createDirectory(target); + } + + for (Path file : source) { + if (!file.toFile().exists()) { + continue; + } + + String baseName = file.getFileName().toString(); + + Path targetFile = target.resolve(filenameMapper.apply(baseName)); + Files.copy(file, targetFile, options); + result.add(targetFile); + } + return result; + } + + /** + * Copy files to the target path. + * + * @param source the paths to the files to copy + * @param target the path to the target files + * @param options options specifying how the copy should be done + * @return the paths to the target files + * @throws IOException if error occurs + */ + public static List copyFiles(List source, Path target, + CopyOption... options) throws IOException { + return copyFiles(source, target, (s) -> s, options); + } + + /** + * Replace file string by applying the given mapper function. + * + * @param source the file to read + * @param contentMapper the mapper function applied to file's content + * @throws IOException if an I/O error occurs + */ + public static void replaceFileString(Path source, + Function contentMapper) throws IOException { + StringBuilder sb = new StringBuilder(); + String lineSep = System.getProperty("line.separator"); + + try (BufferedReader reader = + new BufferedReader(new FileReader(source.toFile()))) { + + String line; + + // read all and replace all at once?? + while ((line = reader.readLine()) != null) { + sb.append(contentMapper.apply(line)) + .append(lineSep); + } + } + + try (FileWriter writer = new FileWriter(source.toFile())) { + writer.write(sb.toString()); + } + } + + /** + * Replace files' string by applying the given mapper function. + * + * @param source the file to read + * @param contentMapper the mapper function applied to files' content + * @throws IOException if an I/O error occurs + */ + public static void replaceFilesString(List source, + Function contentMapper) throws IOException { + for (Path file : source) { + replaceFileString(file, contentMapper); + } + } + + /** + * Grant file user access or full access to everyone. + * + * @param file file to grant access + * @param userOnly true for user access, otherwise full access to everyone + * @throws IOException if error occurs + */ + public static void grantFileAccess(Path file, boolean userOnly) + throws IOException { + Set attr = file.getFileSystem().supportedFileAttributeViews(); + if (attr.contains("posix")) { + String perms = userOnly ? "rwx------" : "rwxrwxrwx"; + Files.setPosixFilePermissions(file, PosixFilePermissions.fromString(perms)); + } else if (attr.contains("acl")) { + AclFileAttributeView view = + Files.getFileAttributeView(file, AclFileAttributeView.class); + List acl = new ArrayList<>(); + for (AclEntry thisEntry : view.getAcl()) { + if (userOnly) { + if (thisEntry.principal().getName() + .equals(view.getOwner().getName())) { + acl.add(allowAccess(thisEntry)); + } else if (thisEntry.type() == AclEntryType.ALLOW) { + acl.add(revokeAccess(thisEntry)); + } else { + acl.add(thisEntry); + } + } else { + if (thisEntry.type() != AclEntryType.ALLOW) { + acl.add(allowAccess(thisEntry)); + } else { + acl.add(thisEntry); + } + } + } + view.setAcl(acl); + } else { + throw new RuntimeException("Unsupported file attributes: " + attr); + } + } + + /** + * Return an ACL entry that revokes owner access. + * + * @param acl original ACL entry to build from + * @return an ACL entry that revokes all access + */ + public static AclEntry revokeAccess(AclEntry acl) { + return buildAclEntry(acl, AclEntryType.DENY); + } + + /** + * Return an ACL entry that allow owner access. + * @param acl original ACL entry to build from + * @return an ACL entry that allows all access + */ + public static AclEntry allowAccess(AclEntry acl) { + return buildAclEntry(acl, AclEntryType.ALLOW); + } + + /** + * Build an ACL entry with a given ACL entry type. + * + * @param acl original ACL entry to build from + * @return an ACL entry with a given ACL entry type + */ + public static AclEntry buildAclEntry(AclEntry acl, AclEntryType type) { + return AclEntry.newBuilder(acl) + .setType(type) + .build(); + } + + /** + * Grant file user access. + * + * @param file file to grant access + * @throws IOException if error occurs + */ + public static void userAccess(Path file) throws IOException { + grantFileAccess(file, true); + } + + /** + * Grant file full access to everyone. + * + * @param file file to grant access + * @throws IOException if error occurs + */ + public static void fullAccess(Path file) throws IOException { + grantFileAccess(file, false); + } } diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java new file mode 100644 index 0000000000000..c39e6bb8e9489 --- /dev/null +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.cds; + +import java.io.File; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.StringArrayUtils; + +/* + * This is a base class used for testing CDS functionalities with complex applications. + * You can define the application by overridding the vmArgs(), classpath() and appCommandLine() + * methods. Application-specific validation checks can be implemented with checkExecution(). +*/ +abstract public class CDSAppTester { + private final String name; + private final String classListFile; + private final String classListFileLog; + private final String staticArchiveFile; + private final String staticArchiveFileLog; + private final String dynamicArchiveFile; + private final String dynamicArchiveFileLog; + private final String productionRunLog; + + public CDSAppTester(String name) { + // Old workflow + this.name = name; + classListFile = name() + ".classlist"; + classListFileLog = classListFile + ".log"; + staticArchiveFile = name() + ".static.jsa"; + staticArchiveFileLog = staticArchiveFile + ".log"; + dynamicArchiveFile = name() + ".dynamic.jsa"; + dynamicArchiveFileLog = dynamicArchiveFile + ".log"; + productionRunLog = name() + ".production.log"; + } + + private enum Workflow { + STATIC, // classic -Xshare:dump workflow + DYNAMIC, // classic -XX:ArchiveClassesAtExit + } + + public enum RunMode { + CLASSLIST, + DUMP_STATIC, + DUMP_DYNAMIC, + PRODUCTION; + + public boolean isStaticDump() { + return this == DUMP_STATIC; + } + public boolean isProductionRun() { + return this == PRODUCTION; + } + } + + public final String name() { + return this.name; + } + + // optional + public String[] vmArgs(RunMode runMode) { + return new String[0]; + } + + // optional + public String classpath(RunMode runMode) { + return null; + } + + // must override + // main class, followed by arguments to the main class + abstract public String[] appCommandLine(RunMode runMode); + + // optional + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {} + + private Workflow workflow; + + public final boolean isStaticWorkflow() { + return workflow == Workflow.STATIC; + } + + public final boolean isDynamicWorkflow() { + return workflow == Workflow.DYNAMIC; + } + + private String logToFile(String logFile, String... logTags) { + StringBuilder sb = new StringBuilder("-Xlog:"); + String prefix = ""; + for (String tag : logTags) { + sb.append(prefix); + sb.append(tag); + prefix = ","; + } + sb.append(":file=" + logFile + "::filesize=0"); + return sb.toString(); + } + + private void listOutputFile(String file) { + File f = new File(file); + if (f.exists()) { + System.out.println("[output file: " + file + " " + f.length() + " bytes]"); + } else { + System.out.println("[output file: " + file + " does not exist]"); + } + } + + private OutputAnalyzer executeAndCheck(String[] cmdLine, RunMode runMode, String... logFiles) throws Exception { + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(cmdLine); + Process process = pb.start(); + OutputAnalyzer output = CDSTestUtils.executeAndLog(process, runMode.toString()); + for (String logFile : logFiles) { + listOutputFile(logFile); + } + output.shouldHaveExitValue(0); + CDSTestUtils.checkCommonExecExceptions(output); + checkExecution(output, runMode); + return output; + } + + private OutputAnalyzer createClassList() throws Exception { + RunMode runMode = RunMode.CLASSLIST; + String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), + "-Xshare:off", + "-XX:DumpLoadedClassList=" + classListFile, + "-cp", classpath(runMode), + logToFile(classListFileLog, + "class+load=debug")); + cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); + return executeAndCheck(cmdLine, runMode, classListFile, classListFileLog); + } + + private OutputAnalyzer dumpStaticArchive() throws Exception { + RunMode runMode = RunMode.DUMP_STATIC; + String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), + "-Xlog:cds", + "-Xlog:cds+heap=error", + "-Xshare:dump", + "-XX:SharedArchiveFile=" + staticArchiveFile, + "-XX:SharedClassListFile=" + classListFile, + "-cp", classpath(runMode), + logToFile(staticArchiveFileLog, + "cds=debug", + "cds+class=debug", + "cds+heap=warning", + "cds+resolve=debug")); + return executeAndCheck(cmdLine, runMode, staticArchiveFile, staticArchiveFileLog); + } + + private OutputAnalyzer dumpDynamicArchive() throws Exception { + RunMode runMode = RunMode.DUMP_DYNAMIC; + String[] cmdLine = new String[0]; + if (isDynamicWorkflow()) { + // "classic" dynamic archive + cmdLine = StringArrayUtils.concat(vmArgs(runMode), + "-Xlog:cds", + "-XX:ArchiveClassesAtExit=" + dynamicArchiveFile, + "-cp", classpath(runMode), + logToFile(dynamicArchiveFileLog, + "cds=debug", + "cds+class=debug", + "cds+resolve=debug", + "class+load=debug")); + } + cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); + return executeAndCheck(cmdLine, runMode, dynamicArchiveFile, dynamicArchiveFileLog); + } + + private OutputAnalyzer productionRun() throws Exception { + RunMode runMode = RunMode.PRODUCTION; + String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), + "-cp", classpath(runMode), + logToFile(productionRunLog, "cds")); + + if (isStaticWorkflow()) { + cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + staticArchiveFile); + } else if (isDynamicWorkflow()) { + cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + dynamicArchiveFile); + } + + cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); + return executeAndCheck(cmdLine, runMode, productionRunLog); + } + + public void run(String args[]) throws Exception { + String err = "Must have exactly one command line argument of the following: "; + String prefix = ""; + for (Workflow wf : Workflow.values()) { + err += prefix; + err += wf; + prefix = ", "; + } + if (args.length != 1) { + throw new RuntimeException(err); + } else { + if (args[0].equals("STATIC")) { + runStaticWorkflow(); + } else if (args[0].equals("DYNAMIC")) { + runDynamicWorkflow(); + } else { + throw new RuntimeException(err); + } + } + } + + private void runStaticWorkflow() throws Exception { + this.workflow = Workflow.STATIC; + createClassList(); + dumpStaticArchive(); + productionRun(); + } + + private void runDynamicWorkflow() throws Exception { + this.workflow = Workflow.DYNAMIC; + dumpDynamicArchive(); + productionRun(); + } +} diff --git a/test/lib/jdk/test/lib/cds/CDSOptions.java b/test/lib/jdk/test/lib/cds/CDSOptions.java index 2fa949dc4ccc7..130b7b117dc9b 100644 --- a/test/lib/jdk/test/lib/cds/CDSOptions.java +++ b/test/lib/jdk/test/lib/cds/CDSOptions.java @@ -32,6 +32,8 @@ public class CDSOptions { public ArrayList prefix = new ArrayList(); public ArrayList suffix = new ArrayList(); public boolean useSystemArchive = false; + public String appJar; + public String appJarDir; // classes to be archived public String[] classList; @@ -100,4 +102,15 @@ public CDSOptions setClassList(ArrayList list) { return this; } + // AppCDS methods + public CDSOptions setAppJar(String appJar) { + this.appJar = appJar; + return this; + } + + public CDSOptions setAppJarDir(String appJarDir) { + this.appJarDir = appJarDir; + return this; + } + } diff --git a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java index 4ebe24915849a..d4ddfb90b9b83 100644 --- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -257,7 +257,7 @@ public static OutputAnalyzer createArchive(CDSOptions opts) for (String s : opts.suffix) cmd.add(s); String[] cmdLine = cmd.toArray(new String[cmd.size()]); - ProcessBuilder pb = ProcessTools.createTestJvm(cmdLine); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(cmdLine); return executeAndLog(pb, "dump"); } @@ -430,7 +430,7 @@ public static OutputAnalyzer runWithArchive(CDSOptions opts) for (String s : opts.suffix) cmd.add(s); String[] cmdLine = cmd.toArray(new String[cmd.size()]); - ProcessBuilder pb = ProcessTools.createTestJvm(cmdLine); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(cmdLine); return executeAndLog(pb, "exec"); } @@ -616,6 +616,9 @@ public static OutputAnalyzer executeAndLog(Process process, String logName) thro if (copyChildStdoutToMainStdout) System.out.println("[STDOUT]\n" + output.getStdout()); + if (output.getExitValue() != 0 && output.getStdout().contains("A fatal error has been detected")) { + throw new RuntimeException("Hotspot crashed"); + } return output; } diff --git a/test/lib/jdk/test/lib/classloader/ClassUnloadCommon.java b/test/lib/jdk/test/lib/classloader/ClassUnloadCommon.java index 23e8fa62b532e..ae8ae12cf977b 100644 --- a/test/lib/jdk/test/lib/classloader/ClassUnloadCommon.java +++ b/test/lib/jdk/test/lib/classloader/ClassUnloadCommon.java @@ -23,12 +23,13 @@ /* - * To use ClassUnloadCommon from a sub-process, see hotspot/test/runtime/logging/ClassLoadUnloadTest.java + * To use ClassUnloadCommon from a sub-process, see test/hotspot/jtreg/runtime/logging/ClassLoadUnloadTest.java * for an example. */ package jdk.test.lib.classloader; +import jdk.test.whitebox.WhiteBox; import java.io.File; import java.net.MalformedURLException; @@ -62,8 +63,8 @@ private static void allocateMemory(int kilobytes) { } public static void triggerUnloading() { - allocateMemory(16 * 1024); // force young collection - System.gc(); + WhiteBox wb = WhiteBox.getWhiteBox(); + wb.fullGC(); // will do class unloading } /** diff --git a/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java b/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java index b3b1b8a7e2caf..31cff4b8c25f2 100644 --- a/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java +++ b/test/lib/jdk/test/lib/cli/CommandLineOptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -113,7 +113,7 @@ public static void verifyJVMStartup(String expectedMessages[], finalOptions.add("-version"); ProcessBuilder processBuilder - = ProcessTools.createJavaProcessBuilder(finalOptions.toArray( + = ProcessTools.createLimitedTestJavaProcessBuilder(finalOptions.toArray( new String[finalOptions.size()])); OutputAnalyzer outputAnalyzer = new OutputAnalyzer(processBuilder.start()); @@ -263,7 +263,7 @@ public static void verifyOptionValue(String optionName, Collections.addAll(vmOpts, additionalVMOpts); Collections.addAll(vmOpts, "-XX:+PrintFlagsFinal", "-version"); - ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( + ProcessBuilder processBuilder = ProcessTools.createLimitedTestJavaProcessBuilder( vmOpts.toArray(new String[vmOpts.size()])); OutputAnalyzer outputAnalyzer @@ -333,7 +333,7 @@ public static OutputAnalyzer startVMWithOptions(String[] optionNames, Collections.addAll(vmOpts, additionalVMOpts); Collections.addAll(vmOpts, "-version"); - ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( + ProcessBuilder processBuilder = ProcessTools.createLimitedTestJavaProcessBuilder( vmOpts.toArray(new String[vmOpts.size()])); return new OutputAnalyzer(processBuilder.start()); diff --git a/test/lib/jdk/test/lib/jfr/AppExecutorHelper.java b/test/lib/jdk/test/lib/jfr/AppExecutorHelper.java index 6768f4ed8f947..5268e05b68885 100644 --- a/test/lib/jdk/test/lib/jfr/AppExecutorHelper.java +++ b/test/lib/jdk/test/lib/jfr/AppExecutorHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,7 +74,7 @@ public static OutputAnalyzer executeAndRecord(String settings, String jfrFilenam Collections.addAll(arguments, classArguments); } - ProcessBuilder pb = ProcessTools.createTestJvm(arguments); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(arguments); return ProcessTools.executeProcess(pb); } } diff --git a/test/lib/jdk/test/lib/net/IPSupport.java b/test/lib/jdk/test/lib/net/IPSupport.java index 05f3966dd5e49..d2e0becc0dd30 100644 --- a/test/lib/jdk/test/lib/net/IPSupport.java +++ b/test/lib/jdk/test/lib/net/IPSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,8 @@ package jdk.test.lib.net; +import jdk.test.lib.Platform; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; @@ -47,6 +49,9 @@ public class IPSupport { private static final boolean hasIPv6; private static final boolean preferIPv4Stack; private static final boolean preferIPv6Addresses; + private static final int IPV4_SNDBUF = 65507; + private static final int IPV6_SNDBUF = 65527; + private static final int IPV6_SNDBUF_AIX = 65487; static { try { @@ -82,6 +87,7 @@ private static boolean hasAddress(InetAddress address) { } } + @SuppressWarnings("removal") private static T runPrivilegedAction(Callable callable) { try { PrivilegedExceptionAction pa = () -> callable.call(); @@ -121,7 +127,6 @@ public static final boolean preferIPv6Addresses() { return preferIPv6Addresses; } - /** * Whether or not the current networking configuration is valid or not. * @@ -164,4 +169,21 @@ public static void printPlatformSupport(PrintStream out) { out.println("preferIPv6Addresses: " + preferIPv6Addresses()); } + /** + * Return current platform's maximum size for IPv4 UDP send buffer + */ + public static final int getMaxUDPSendBufSizeIPv4() { + return IPV4_SNDBUF; + } + + /** + * Return current platform's maximum size for IPv6 UDP send buffer + */ + public static final int getMaxUDPSendBufSizeIPv6() { + if (Platform.isAix()) { + return IPV6_SNDBUF_AIX; + } else { + return IPV6_SNDBUF; + } + } } diff --git a/test/lib/jdk/test/lib/net/SimpleSSLContext.java b/test/lib/jdk/test/lib/net/SimpleSSLContext.java index 5660ed048aa64..e8611fb007f32 100644 --- a/test/lib/jdk/test/lib/net/SimpleSSLContext.java +++ b/test/lib/jdk/test/lib/net/SimpleSSLContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,6 +54,7 @@ public SimpleSSLContext() throws IOException { this(() -> "TLS"); } + @SuppressWarnings("removal") private SimpleSSLContext(Supplier protocols) throws IOException { try { final String proto = protocols.get(); diff --git a/test/lib/jdk/test/lib/process/OutputBuffer.java b/test/lib/jdk/test/lib/process/OutputBuffer.java index 3741ccbe2ff62..fba2a5f6dcf3f 100644 --- a/test/lib/jdk/test/lib/process/OutputBuffer.java +++ b/test/lib/jdk/test/lib/process/OutputBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -120,6 +120,7 @@ public String get() { private final StreamTask outTask; private final StreamTask errTask; private final Process p; + private volatile Integer exitValue; // null implies we don't yet know private final void logProgress(String state) { System.out.println("[" + Instant.now().toString() + "] " + state @@ -146,14 +147,17 @@ public String getStderr() { @Override public int getExitValue() { + if (exitValue != null) { + return exitValue; + } try { logProgress("Waiting for completion"); boolean aborted = true; try { - int result = p.waitFor(); + exitValue = p.waitFor(); logProgress("Waiting for completion finished"); aborted = false; - return result; + return exitValue; } finally { if (aborted) { logProgress("Waiting for completion FAILED"); diff --git a/test/lib/jdk/test/lib/process/Proc.java b/test/lib/jdk/test/lib/process/Proc.java index 3541f34144fe0..802ca11da7b23 100644 --- a/test/lib/jdk/test/lib/process/Proc.java +++ b/test/lib/jdk/test/lib/process/Proc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,8 @@ package jdk.test.lib.process; +import jdk.test.lib.compiler.CompilerUtils; + import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -118,12 +120,18 @@ public class Proc { private boolean inheritIO = false; private boolean noDump = false; - private List cp; // user-provided classpath + private boolean addcp; // user-provided classpath is appended + private List cp; // user-provided classpaths + + private boolean compile; // compile the program as well + private String clazz; // Class to launch private String debug; // debug flag, controller will show data // transfer between procs. If debug is set, // it MUST be different between Procs. + private final StringBuilder stdout = new StringBuilder(); + final private static String PREFIX = "PROCISFUN:"; // policy file @@ -194,8 +202,7 @@ public Proc inheritProp(String k) { } return this; } - // Sets classpath. If not called, Proc will choose a classpath. If called - // with no arg, no classpath will be used. Can be called multiple times. + // Sets classpath. Can be called multiple times. public Proc cp(String... s) { if (cp == null) { cp = new ArrayList<>(); @@ -203,6 +210,12 @@ public Proc cp(String... s) { cp.addAll(Arrays.asList(s)); return this; } + // Adds classpath to defaults. Can be called multiple times. + // Once called, addcp is always true. + public Proc addcp(String... s) { + addcp = true; + return cp(s); + } // Adds a permission to policy. Can be called multiple times. // All perm() calls after a series of grant() calls are grouped into // a single grant block. perm() calls before any grant() call are grouped @@ -258,6 +271,34 @@ public Proc grant(String v) { grant.append(v).append(", "); return this; } + // Compile as well + public Proc compile() { + compile = true; + return this; + } + + // get full classpath. + // 1. Default classpath used if neither cp() or addcp() is called + // 2. User provided classpath (can be empty) used if only cp() is called + // 3. User provided classpath + default classpath used, otherwise + String fullcp() { + if (cp == null) { + return System.getProperty("test.class.path") + File.pathSeparator + + System.getProperty("test.src.path"); + } else { + var newcp = new ArrayList<>(cp); + if (addcp) { + newcp.add(System.getProperty("test.class.path")); + newcp.add(System.getProperty("test.src.path")); + } + if (!newcp.isEmpty()) { + return newcp.stream().collect(Collectors.joining(File.pathSeparator)); + } else { + return null; + } + } + } + // Starts the proc public Proc start() throws IOException { List cmd = new ArrayList<>(); @@ -281,18 +322,26 @@ public Proc start() throws IOException { } } - Collections.addAll(cmd, splitProperty("test.vm.opts")); - Collections.addAll(cmd, splitProperty("test.java.opts")); - - if (cp == null) { + var lcp = fullcp(); + if (lcp != null) { cmd.add("-cp"); - cmd.add(System.getProperty("test.class.path") + File.pathSeparator + - System.getProperty("test.src.path")); - } else if (!cp.isEmpty()) { - cmd.add("-cp"); - cmd.add(cp.stream().collect(Collectors.joining(File.pathSeparator))); + cmd.add(lcp); + } + + if (compile) { + boolean comp = CompilerUtils.compile( + Path.of(System.getProperty("test.src"), clazz + ".java"), + Path.of(System.getProperty("test.classes")), + cmd.subList(1, cmd.size()).toArray(new String[0])); + // subList(1): all options added without launcher name + if (!comp) { + throw new RuntimeException("Compilation error"); + } } + Collections.addAll(cmd, splitProperty("test.vm.opts")); + Collections.addAll(cmd, splitProperty("test.java.opts")); + if (!secprop.isEmpty()) { Path p = Path.of(getId("security")); try (OutputStream fos = Files.newOutputStream(p); @@ -358,6 +407,9 @@ String getId(String suffix) { // Reads a line from stdout of proc public String readLine() throws IOException { String s = br.readLine(); + if (s != null) { + stdout.append(s).append('\n'); + } if (debug != null) { System.out.println("PROC: " + debug + " readline: " + (s == null ? "" : s)); @@ -409,6 +461,15 @@ public void waitFor(int expected) throws Exception { } } + // Returns an OutputAnalyzer + public OutputAnalyzer output() throws Exception { + int exitCode = waitFor(); + Path stderr = Path.of(getId("stderr")); + return new OutputAnalyzer(stdout.toString(), + Files.exists(stderr) ? Files.readString(stderr) : "", + exitCode); + } + // The following methods are used inside a proc // Writes out a BASE64 binary with a prefix diff --git a/test/lib/jdk/test/lib/process/ProcessTools.java b/test/lib/jdk/test/lib/process/ProcessTools.java index 673d24066cd79..439539e7009af 100644 --- a/test/lib/jdk/test/lib/process/ProcessTools.java +++ b/test/lib/jdk/test/lib/process/ProcessTools.java @@ -27,6 +27,7 @@ import jdk.test.lib.Platform; import jdk.test.lib.Utils; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -44,6 +45,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -68,14 +70,17 @@ protected void processLine(String line) { ps.println("[" + prefix + "] " + line); } } - private ProcessTools() { } /** *

      Starts a process from its builder.

      * The default redirects of STDOUT and STDERR are started - * + *

      + * Same as + * {@linkplain #startProcess(String, ProcessBuilder, Consumer, Predicate, long, TimeUnit) startProcess} + * {@code (name, processBuilder, null, null, -1, TimeUnit.NANOSECONDS)} + *

      * @param name The process name * @param processBuilder The process builder * @return Returns the initialized process @@ -90,11 +95,15 @@ public static Process startProcess(String name, /** *

      Starts a process from its builder.

      * The default redirects of STDOUT and STDERR are started - *

      It is possible to monitor the in-streams via the provided {@code consumer} + *

      + * Same as + * {@linkplain #startProcess(String, ProcessBuilder, Consumer, Predicate, long, TimeUnit) startProcess} + * {@code (name, processBuilder, consumer, null, -1, TimeUnit.NANOSECONDS)} + *

      * * @param name The process name - * @param consumer {@linkplain Consumer} instance to process the in-streams * @param processBuilder The process builder + * @param consumer {@linkplain Consumer} instance to process the in-streams * @return Returns the initialized process * @throws IOException */ @@ -105,7 +114,7 @@ public static Process startProcess(String name, throws IOException { try { return startProcess(name, processBuilder, consumer, null, -1, TimeUnit.NANOSECONDS); - } catch (InterruptedException | TimeoutException e) { + } catch (InterruptedException | TimeoutException | CancellationException e) { // will never happen throw new RuntimeException(e); } @@ -115,8 +124,9 @@ public static Process startProcess(String name, *

      Starts a process from its builder.

      * The default redirects of STDOUT and STDERR are started *

      - * It is possible to wait for the process to get to a warmed-up state - * via {@linkplain Predicate} condition on the STDOUT/STDERR + * Same as + * {@linkplain #startProcess(String, ProcessBuilder, Consumer, Predicate, long, TimeUnit) startProcess} + * {@code (name, processBuilder, null, linePredicate, timeout, unit)} *

      * * @param name The process name @@ -141,6 +151,73 @@ public static Process startProcess(String name, return startProcess(name, processBuilder, null, linePredicate, timeout, unit); } + + /* + BufferOutputStream and BufferInputStream allow to re-use p.getInputStream() amd p.getOutputStream() of + processes started with ProcessTools.startProcess(...). + Implementation cashes ALL process output and allow to read it through InputStream. + The stream uses Future task from StreamPumper.process() to check if output is complete. + */ + private static class BufferOutputStream extends ByteArrayOutputStream { + private int current = 0; + final private Process p; + + private Future task; + + public BufferOutputStream(Process p) { + this.p = p; + } + + synchronized void setTask(Future task) { + this.task = task; + } + synchronized int readNext() { + if (current > count) { + throw new RuntimeException("Shouldn't ever happen. start: " + + current + " count: " + count + " buffer: " + this); + } + while (current == count) { + if (!p.isAlive() && (task != null)) { + try { + task.get(10, TimeUnit.MILLISECONDS); + if (current == count) { + return -1; + } + } catch (TimeoutException e) { + // continue execution, so wait() give a chance to write + } catch (InterruptedException | ExecutionException e) { + return -1; + } + } + try { + wait(1); + } catch (InterruptedException ie) { + return -1; + } + } + return this.buf[current++]; + } + } + + private static class BufferInputStream extends InputStream { + + private final BufferOutputStream buffer; + + public BufferInputStream(Process p) { + buffer = new BufferOutputStream(p); + } + + BufferOutputStream getOutputStream() { + return buffer; + } + + @Override + public int read() throws IOException { + return buffer.readNext(); + } + } + + /** *

      Starts a process from its builder.

      * The default redirects of STDOUT and STDERR are started @@ -178,6 +255,14 @@ public static Process startProcess(String name, stdout.addPump(new LineForwarder(name, System.out)); stderr.addPump(new LineForwarder(name, System.err)); + + + BufferInputStream stdOut = new BufferInputStream(p); + BufferInputStream stdErr = new BufferInputStream(p); + + stdout.addOutputStream(stdOut.getOutputStream()); + stderr.addOutputStream(stdErr.getOutputStream()); + if (lineConsumer != null) { StreamPumper.LinePump pump = new StreamPumper.LinePump() { @Override @@ -189,7 +274,6 @@ protected void processLine(String line) { stderr.addPump(pump); } - CountDownLatch latch = new CountDownLatch(1); if (linePredicate != null) { StreamPumper.LinePump pump = new StreamPumper.LinePump() { @@ -212,6 +296,9 @@ protected void processLine(String line) { final Future stdoutTask = stdout.process(); final Future stderrTask = stderr.process(); + stdOut.getOutputStream().setTask(stdoutTask); + stdErr.getOutputStream().setTask(stderrTask); + try { if (timeout > -1) { if (timeout == 0) { @@ -237,7 +324,7 @@ protected void processLine(String line) { throw e; } - return new ProcessImpl(p, stdoutTask, stderrTask); + return new ProcessImpl(p, stdoutTask, stderrTask, stdOut, stdErr); } /** @@ -283,17 +370,7 @@ public static long getProcessId() throws Exception { * @param command Arguments to pass to the java command. * @return The ProcessBuilder instance representing the java command. */ - public static ProcessBuilder createJavaProcessBuilder(List command) { - return createJavaProcessBuilder(command.toArray(String[]::new)); - } - - /** - * Create ProcessBuilder using the java launcher from the jdk to be tested. - * - * @param command Arguments to pass to the java command. - * @return The ProcessBuilder instance representing the java command. - */ - public static ProcessBuilder createJavaProcessBuilder(String... command) { + private static ProcessBuilder createJavaProcessBuilder(String... command) { String javapath = JDKToolFinder.getJDKTool("java"); ArrayList args = new ArrayList<>(); @@ -334,8 +411,8 @@ private static void printStack(Thread t, StackTraceElement[] stack) { * @param command Arguments to pass to the java command. * @return The ProcessBuilder instance representing the java command. */ - public static ProcessBuilder createTestJvm(List command) { - return createTestJvm(command.toArray(String[]::new)); + public static ProcessBuilder createTestJavaProcessBuilder(List command) { + return createTestJavaProcessBuilder(command.toArray(String[]::new)); } /** @@ -349,10 +426,54 @@ public static ProcessBuilder createTestJvm(List command) { * @param command Arguments to pass to the java command. * @return The ProcessBuilder instance representing the java command. */ - public static ProcessBuilder createTestJvm(String... command) { + public static ProcessBuilder createTestJavaProcessBuilder(String... command) { return createJavaProcessBuilder(Utils.prependTestJavaOpts(command)); } + /** + * Create ProcessBuilder using the java launcher from the jdk to + * be tested. + * + *

      Please observe that you likely should use + * createTestJavaProcessBuilder() instead of this method because + * createTestJavaProcessBuilder() will add JVM options from + * "test.vm.opts" and "test.java.opts" and this method will + * not do that. + * + *

      If you still chose to use + * createLimitedTestJavaProcessBuilder() you should probably use + * it in combination with @requires vm.flagless JTREG + * anotation as to not waste energy and test resources. + * + * @param command Arguments to pass to the java command. + * @return The ProcessBuilder instance representing the java command. + */ + public static ProcessBuilder createLimitedTestJavaProcessBuilder(List command) { + return createLimitedTestJavaProcessBuilder(command.toArray(String[]::new)); + } + + /** + * Create ProcessBuilder using the java launcher from the jdk to + * be tested. + * + *

      Please observe that you likely should use + * createTestJavaProcessBuilder() instead of this method because + * createTestJavaProcessBuilder() will add JVM options from + * "test.vm.opts" and "test.java.opts" and this method will + * not do that. + * + *

      If you still chose to use + * createLimitedTestJavaProcessBuilder() you should probably use + * it in combination with @requires vm.flagless JTREG + * anotation as to not waste energy and test resources. + * + * @param command Arguments to pass to the java command. + * @return The ProcessBuilder instance representing the java command. + */ + public static ProcessBuilder createLimitedTestJavaProcessBuilder(String... command) { + return createJavaProcessBuilder(command); + } + /** * Executes a test jvm process, waits for it to finish and returns the process output. * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added. @@ -384,7 +505,7 @@ public static OutputAnalyzer executeTestJvm(List cmds) throws Exception * @return The output from the process. */ public static OutputAnalyzer executeTestJvm(String... cmds) throws Exception { - ProcessBuilder pb = createTestJvm(cmds); + ProcessBuilder pb = createTestJavaProcessBuilder(cmds); return executeProcess(pb); } @@ -615,14 +736,19 @@ private static Process privilegedStart(ProcessBuilder pb) throws IOException { private static class ProcessImpl extends Process { + private final InputStream stdOut; + private final InputStream stdErr; private final Process p; private final Future stdoutTask; private final Future stderrTask; - public ProcessImpl(Process p, Future stdoutTask, Future stderrTask) { + public ProcessImpl(Process p, Future stdoutTask, Future stderrTask, + InputStream stdOut, InputStream etdErr) { this.p = p; this.stdoutTask = stdoutTask; this.stderrTask = stderrTask; + this.stdOut = stdOut; + this.stdErr = etdErr; } @Override @@ -632,12 +758,12 @@ public OutputStream getOutputStream() { @Override public InputStream getInputStream() { - return p.getInputStream(); + return stdOut; } @Override public InputStream getErrorStream() { - return p.getErrorStream(); + return stdErr; } @Override diff --git a/test/lib/jdk/test/lib/process/StreamPumper.java b/test/lib/jdk/test/lib/process/StreamPumper.java index ad62f6c300a90..63775f7d62c56 100644 --- a/test/lib/jdk/test/lib/process/StreamPumper.java +++ b/test/lib/jdk/test/lib/process/StreamPumper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,13 +95,15 @@ public StreamPumper(InputStream in, OutputStream out) { /** * Implements Thread.run(). Continuously read from {@code in} and write to - * {@code out} until {@code in} has reached end of stream. Abort on - * interruption. Abort on IOExceptions. + * {@code out} until {@code in} has reached end of stream. + * Additionally this method also splits the data read from the buffer into lines, + * and processes each line using linePumps. + * Abort on interruption. Abort on IOExceptions. */ @Override public void run() { - try (BufferedInputStream is = new BufferedInputStream(in)) { - ByteArrayOutputStream lineBos = new ByteArrayOutputStream(); + try (BufferedInputStream is = new BufferedInputStream(in); + ByteArrayOutputStream lineBos = new ByteArrayOutputStream()) { byte[] buf = new byte[BUF_SIZE]; int len = 0; int linelen = 0; @@ -133,6 +135,10 @@ public void run() { i++; } + // If no crlf was found, or there was additional data after the last crlf was found, then write the leftover data + // in lineBos. If there is more data to read it will be concatenated with the current data on the next iteration. + // If there is no more data, or no more crlf found, all the remaining data will be processed after the loop, as the + // final line. if (lastcrlf == -1) { lineBos.write(buf, 0, len); linelen += len; @@ -143,6 +149,12 @@ public void run() { } } + // If there was no terminating crlf the remaining data has been written to lineBos, + // but this final line of data now needs to be processed by the linePumper. + final String line = lineBos.toString(); + if (!line.isEmpty()) { + linePumps.forEach((lp) -> lp.processLine(line)); + } } catch (IOException e) { if (!e.getMessage().equalsIgnoreCase("stream closed")) { e.printStackTrace(); diff --git a/test/lib/jdk/test/lib/security/DiffieHellmanGroup.java b/test/lib/jdk/test/lib/security/DiffieHellmanGroup.java new file mode 100644 index 0000000000000..b8f7c0c34beec --- /dev/null +++ b/test/lib/jdk/test/lib/security/DiffieHellmanGroup.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.security; + +import java.math.BigInteger; + +/** + * An enumeration of DH groups for tests. + */ +public enum DiffieHellmanGroup { + + /** + * RFC 7919 - ffdhe2048. + */ + ffdhe2048(new BigInteger("FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" + + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" + + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" + + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" + + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" + + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + + "886B423861285C97FFFFFFFFFFFFFFFF", 16), 2), + /** + * RFC 7919 - ffdhe3072. + */ + ffdhe3072(new BigInteger("FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" + + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" + + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" + + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" + + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" + + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" + + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" + + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" + + "3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF", 16), 2), + /** + * RFC 7919 - ffdhe4096. + */ + ffdhe4096(new BigInteger("FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" + + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" + + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" + + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" + + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" + + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" + + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" + + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" + + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" + + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" + + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" + + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" + + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" + + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" + + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" + + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" + + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" + + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" + + "A907600A918130C46DC778F971AD0038092999A333CB8B7A" + + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" + + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6A" + + "FFFFFFFFFFFFFFFF", 16), 2); + + + public BigInteger getPrime() { + return prime; + } + + private final BigInteger prime; + + public BigInteger getBase() { + return base; + } + + private final BigInteger base; + + DiffieHellmanGroup(BigInteger prime, int base) { + this.prime = prime; + this.base = BigInteger.valueOf(base); + } +} diff --git a/test/lib/jdk/test/lib/security/SecurityUtils.java b/test/lib/jdk/test/lib/security/SecurityUtils.java index 319416a466c22..2885440e2a29a 100644 --- a/test/lib/jdk/test/lib/security/SecurityUtils.java +++ b/test/lib/jdk/test/lib/security/SecurityUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,12 +30,34 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.test.lib.security.DiffieHellmanGroup; /** * Common library for various security test helper functions. */ public final class SecurityUtils { + /* + * Key Sizes for various algorithms. + */ + private enum KeySize{ + RSA(2048), + DSA(2048), + DH(2048); + + private final int keySize; + KeySize(int keySize) { + this.keySize = keySize; + } + + @Override + public String toString() { + return String.valueOf(keySize); + } + } + + private final static int DEFAULT_SALTSIZE = 16; + private static String getCacerts() { String sep = File.separator; return System.getProperty("java.home") + sep @@ -107,6 +129,44 @@ public static void removeAlgsFromDSigPolicy(String... algs) { removeFromDSigPolicy("disallowAlg", List.of(algs)); } + /** + * Returns a salt size for tests + */ + public static int getTestSaltSize() { + return DEFAULT_SALTSIZE; + } + + /** + * Returns a key size in bits for tests, depending on the specified algorithm + */ + public static int getTestKeySize(String algo) { + return switch (algo) { + case "RSA" -> KeySize.RSA.keySize; + case "DSA" -> KeySize.DSA.keySize; + case "DH", "DiffieHellman" -> KeySize.DH.keySize; + default -> throw new RuntimeException("Test key size not defined for " + algo); + }; + } + + /** + * Returns a DH predefined group for tests + */ + public static DiffieHellmanGroup getTestDHGroup() { + return getTestDHGroup(2048); + } + + /** + * Returns a DH predefined group for tests, depending on the specified prime size + */ + public static DiffieHellmanGroup getTestDHGroup(int primeSize) { + return switch(primeSize) { + case 2048 -> DiffieHellmanGroup.ffdhe2048; + case 3072 -> DiffieHellmanGroup.ffdhe3072; + case 4096 -> DiffieHellmanGroup.ffdhe4096; + default -> throw new RuntimeException("Test DH group not defined for " + primeSize); + }; + } + private static void removeFromDSigPolicy(String rule, List algs) { String value = Security.getProperty("jdk.xml.dsig.secureValidationPolicy"); value = Arrays.stream(value.split(",")) diff --git a/test/lib/jdk/test/lib/security/TestCertificate.java b/test/lib/jdk/test/lib/security/TestCertificate.java index 914247e23f885..83de3b160122f 100644 --- a/test/lib/jdk/test/lib/security/TestCertificate.java +++ b/test/lib/jdk/test/lib/security/TestCertificate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,7 @@ public enum TestCertificate { // Subject: CN=SSLCertificate, O=SomeCompany // Issuer: CN=Intermediate CA Cert, O=SomeCompany // Validity: Tue Aug 30 14:37:19 PDT 2016 to Wed Aug 30 14:37:19 PDT 2017 - ONE("1000", + ONE("10:00", "CN=SSLCertificate, O=SomeCompany", "CN=Intermediate CA Cert, O=SomeCompany", -1063259762, diff --git a/test/lib/jdk/test/lib/security/TestTLSHandshake.java b/test/lib/jdk/test/lib/security/TestTLSHandshake.java index 309a3f8ef2cb8..4d53513276e55 100644 --- a/test/lib/jdk/test/lib/security/TestTLSHandshake.java +++ b/test/lib/jdk/test/lib/security/TestTLSHandshake.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,8 +35,8 @@ public final class TestTLSHandshake extends SSLSocketTest { "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"; public static final long HASHCODE = -1057291798L; public static final long ANCHOR_HASHCODE = 1688661792L; - public static final String CERT_SERIAL = "edbec8f705af2514"; - public static final String ANCHOR_CERT_SERIAL = "8e191778b2f331be"; + public static final String CERT_SERIAL = "00:ed:be:c8:f7:05:af:25:14"; + public static final String ANCHOR_CERT_SERIAL = "8e:19:17:78:b2:f3:31:be"; public String protocolVersion; public String peerHost; diff --git a/test/lib/jdk/test/lib/util/ForceGC.java b/test/lib/jdk/test/lib/util/ForceGC.java index 3c9ebc4d35e1e..e3587b9a2bebc 100644 --- a/test/lib/jdk/test/lib/util/ForceGC.java +++ b/test/lib/jdk/test/lib/util/ForceGC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,40 +38,70 @@ public class ForceGC { /** * Causes the current thread to wait until the {@code booleanSupplier} - * returns true, or a specific waiting time elapses. The waiting time - * is 1 second scaled with the jtreg testing timeout factor. + * returns true, or the waiting time elapses. The waiting time + * is 1 second scaled with the jtreg testing timeout factor. This method + * is equivalent to calling {@link #waitFor(BooleanSupplier, long) + * waitFor(booleanSupplier, Math.round(1000L * JTREG_TIMEOUT_FACTOR)} + * where {@code JTREG_TIMEOUT_FACTOR} is the value of + * "test.timeout.factor" system property. + * + * @apiNote If the given {@code booleanSupplier} is expected to never + * return true, for example to check if an object that is expected + * to be strongly reachable is still alive, + * {@link #waitFor(BooleanSupplier, long)} can be used to specify + * the timeout for the wait method to return. * * @param booleanSupplier boolean supplier * @return true if the {@code booleanSupplier} returns true, or false - * if did not complete after the specific waiting time. + * if did not complete after the waiting time. + */ public static boolean wait(BooleanSupplier booleanSupplier) { + return waitFor(booleanSupplier, Math.round(1000L * TIMEOUT_FACTOR)); + } + + /** + * Causes the current thread to wait until the {@code booleanSupplier} + * returns true, or the specified waiting time elapses. + * + * @apiNote If the given {@code booleanSupplier} is expected to never + * return true, for example to check if an object that is expected + * to be strongly reachable is still alive, this method can be used + * to specify the timeout independent of the jtreg timeout factor. + * + * @param booleanSupplier boolean supplier + * @param timeout the maximum time to wait, in milliseconds + * @return true if the {@code booleanSupplier} returns true, or false + * if did not complete after the specified waiting time. + */ + public static boolean waitFor(BooleanSupplier booleanSupplier, long timeout) { ReferenceQueue queue = new ReferenceQueue<>(); Object obj = new Object(); PhantomReference ref = new PhantomReference<>(obj, queue); - obj = null; - Reference.reachabilityFence(obj); - Reference.reachabilityFence(ref); + try { + obj = null; - int retries = (int)(Math.round(1000L * TIMEOUT_FACTOR) / 200); - for (; retries >= 0; retries--) { - if (booleanSupplier.getAsBoolean()) { - return true; - } + int retries = (int) (timeout / 200); + for (; retries >= 0; retries--) { + if (booleanSupplier.getAsBoolean()) { + return true; + } - System.gc(); + System.gc(); - try { - // The remove() will always block for the specified milliseconds - // if the reference has already been removed from the queue. - // But it is fine. For most cases, the 1st GC is sufficient - // to trigger and complete the cleanup. - queue.remove(200L); - } catch (InterruptedException ie) { - // ignore, the loop will try again + try { + // The remove() will always block for the specified milliseconds + // if the reference has already been removed from the queue. + // But it is fine. For most cases, the 1st GC is sufficient + // to trigger and complete the cleanup. + queue.remove(200L); + } catch (InterruptedException ie) { + // ignore, the loop will try again + } } + } finally { + Reference.reachabilityFence(ref); } - return booleanSupplier.getAsBoolean(); } } diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index 5a0ab744adecd..03db68e91d18f 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -138,6 +138,12 @@ public int getConstantPoolCacheLength(Class aClass) { return getConstantPoolCacheLength0(aClass); } + private native Object[] getResolvedReferences0(Class aClass); + public Object[] getResolvedReferences(Class aClass) { + Objects.requireNonNull(aClass); + return getResolvedReferences0(aClass); + } + private native int remapInstructionOperandFromCPCache0(Class aClass, int index); public int remapInstructionOperandFromCPCache(Class aClass, int index) { Objects.requireNonNull(aClass); @@ -603,7 +609,6 @@ public Object getMethodOption(Executable method, String name) { public native boolean isJFRIncluded(); public native boolean isDTraceIncluded(); public native boolean canWriteJavaHeapArchive(); - public native Object getResolvedReferences(Class c); public native void linkClass(Class c); public native boolean areOpenArchiveHeapObjectsMapped(); diff --git a/test/micro/org/openjdk/bench/java/security/AlgorithmConstraintsPermits.java b/test/micro/org/openjdk/bench/java/security/AlgorithmConstraintsPermits.java index 3cb9567b92bff..46e68ea627fdd 100644 --- a/test/micro/org/openjdk/bench/java/security/AlgorithmConstraintsPermits.java +++ b/test/micro/org/openjdk/bench/java/security/AlgorithmConstraintsPermits.java @@ -25,12 +25,14 @@ import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; import sun.security.util.DisabledAlgorithmConstraints; import java.security.AlgorithmConstraints; @@ -43,8 +45,10 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) -@Fork(jvmArgsAppend = {"--add-exports", "java.base/sun.security.util=ALL-UNNAMED"}) +@Fork(value = 3, jvmArgsAppend = {"--add-exports", "java.base/sun.security.util=ALL-UNNAMED"}) @State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) public class AlgorithmConstraintsPermits { AlgorithmConstraints tlsDisabledAlgConstraints; diff --git a/test/micro/org/openjdk/bench/java/security/CacheBench.java b/test/micro/org/openjdk/bench/java/security/CacheBench.java index a7fe33432f712..9b7c39a3cf2de 100644 --- a/test/micro/org/openjdk/bench/java/security/CacheBench.java +++ b/test/micro/org/openjdk/bench/java/security/CacheBench.java @@ -27,6 +27,7 @@ import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Param; @@ -38,11 +39,14 @@ import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; +import org.openjdk.jmh.annotations.Warmup; import sun.security.util.Cache; @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) -@Fork(jvmArgsAppend = {"--add-exports", "java.base/sun.security.util=ALL-UNNAMED", "-Xmx1g"}) +@Fork(value = 3, jvmArgsAppend = {"--add-exports", "java.base/sun.security.util=ALL-UNNAMED"}) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) public class CacheBench { @State(Scope.Benchmark) diff --git a/test/micro/org/openjdk/bench/java/security/CipherSuiteBench.java b/test/micro/org/openjdk/bench/java/security/CipherSuiteBench.java index 8703e85fef045..e37cec895caa5 100644 --- a/test/micro/org/openjdk/bench/java/security/CipherSuiteBench.java +++ b/test/micro/org/openjdk/bench/java/security/CipherSuiteBench.java @@ -30,10 +30,12 @@ import java.util.concurrent.TimeUnit; -@Fork(jvmArgsAppend = {"--add-exports", "java.base/sun.security.ssl=ALL-UNNAMED", "--add-opens", "java.base/sun.security.ssl=ALL-UNNAMED"}) +@Fork(value = 3, jvmArgsAppend = {"--add-exports", "java.base/sun.security.ssl=ALL-UNNAMED", "--add-opens", "java.base/sun.security.ssl=ALL-UNNAMED"}) @State(Scope.Benchmark) @OutputTimeUnit(TimeUnit.MICROSECONDS) @BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) public class CipherSuiteBench { Method nameOf; @@ -53,6 +55,6 @@ public void initilizeClass() throws ClassNotFoundException, NoSuchMethodExceptio @Benchmark public Object benchmarkCipherSuite() throws InvocationTargetException, IllegalAccessException { - return nameOf.invoke(null,cipherSuite); + return nameOf.invoke(null, cipherSuite); } } diff --git a/test/micro/org/openjdk/bench/java/security/DoPrivileged.java b/test/micro/org/openjdk/bench/java/security/DoPrivileged.java index f8d16082f8bb6..583da306fa039 100644 --- a/test/micro/org/openjdk/bench/java/security/DoPrivileged.java +++ b/test/micro/org/openjdk/bench/java/security/DoPrivileged.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,11 +24,14 @@ import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; import java.security.AccessController; import java.security.PrivilegedAction; @@ -40,6 +43,9 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) public class DoPrivileged { private PrivilegedAction privilegedAction; diff --git a/test/micro/org/openjdk/bench/java/security/GetContext.java b/test/micro/org/openjdk/bench/java/security/GetContext.java index e72813d2811e5..1e36165d3f070 100644 --- a/test/micro/org/openjdk/bench/java/security/GetContext.java +++ b/test/micro/org/openjdk/bench/java/security/GetContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,11 +26,14 @@ import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; import java.security.AccessControlContext; import java.security.AccessController; @@ -43,6 +46,9 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) public abstract class GetContext { public static class Top extends GetContext { diff --git a/test/micro/org/openjdk/bench/java/security/GetMessageDigest.java b/test/micro/org/openjdk/bench/java/security/GetMessageDigest.java index 17434394ba1ee..5c7b31fa133d9 100644 --- a/test/micro/org/openjdk/bench/java/security/GetMessageDigest.java +++ b/test/micro/org/openjdk/bench/java/security/GetMessageDigest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +47,7 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 5, time = 1) -@Measurement(iterations = 10, time = 1) +@Measurement(iterations = 5, time = 1) @Fork(value = 3) public class GetMessageDigest { diff --git a/test/micro/org/openjdk/bench/java/security/MessageDigests.java b/test/micro/org/openjdk/bench/java/security/MessageDigests.java index 2969baaa22ec4..a3ab483c39c19 100644 --- a/test/micro/org/openjdk/bench/java/security/MessageDigests.java +++ b/test/micro/org/openjdk/bench/java/security/MessageDigests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,18 +45,18 @@ */ @State(Scope.Thread) @OutputTimeUnit(TimeUnit.MILLISECONDS) -@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) -@Fork(jvmArgsAppend = {"-Xms1024m", "-Xmx1024m", "-Xmn768m", "-XX:+UseParallelGC"}, value = 5) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(jvmArgsAppend = {"-Xms1024m", "-Xmx1024m", "-Xmn768m", "-XX:+UseParallelGC"}, value = 3) public class MessageDigests { - @Param({"64", "1024", "16384"}) + @Param({"64", "16384"}) private int length; - @Param({"md2", "md5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512"}) + @Param({"md5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", "SHA3-256", "SHA3-512"}) private String digesterName; - @Param({"DEFAULT", "SUN"}) + @Param({"DEFAULT"}) protected String provider; private byte[] inputBytes; diff --git a/test/micro/org/openjdk/bench/java/security/PKCS12KeyStores.java b/test/micro/org/openjdk/bench/java/security/PKCS12KeyStores.java index 9c8f378fb9b1b..f68b4503ef524 100644 --- a/test/micro/org/openjdk/bench/java/security/PKCS12KeyStores.java +++ b/test/micro/org/openjdk/bench/java/security/PKCS12KeyStores.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,10 +38,10 @@ */ @State(Scope.Benchmark) @OutputTimeUnit(TimeUnit.MILLISECONDS) -@Warmup(iterations = 2) -@Measurement(iterations = 10) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) @BenchmarkMode(Mode.AverageTime) -@Fork(jvmArgsAppend = {"-Xms1024m", "-Xmx1024m", "-Xmn768m", "-XX:+UseParallelGC"}, value = 5) +@Fork(jvmArgsAppend = {"-Xms1024m", "-Xmx1024m", "-Xmn768m", "-XX:+UseParallelGC"}, value = 3) public class PKCS12KeyStores { private static final char[] PASS = "changeit".toCharArray(); diff --git a/test/micro/org/openjdk/bench/java/security/PermissionsImplies.java b/test/micro/org/openjdk/bench/java/security/PermissionsImplies.java index 22bccdcb8599e..7018cc73c37da 100644 --- a/test/micro/org/openjdk/bench/java/security/PermissionsImplies.java +++ b/test/micro/org/openjdk/bench/java/security/PermissionsImplies.java @@ -42,8 +42,8 @@ */ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) -@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) -@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) @Fork(3) @State(Scope.Thread) public class PermissionsImplies { diff --git a/test/micro/org/openjdk/bench/java/security/SSLHandshake.java b/test/micro/org/openjdk/bench/java/security/SSLHandshake.java index 714fc9de31cea..62c4504558518 100644 --- a/test/micro/org/openjdk/bench/java/security/SSLHandshake.java +++ b/test/micro/org/openjdk/bench/java/security/SSLHandshake.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,6 +51,9 @@ @BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.SECONDS) @State(Scope.Benchmark) +@Warmup(iterations = 5, time = 5) +@Measurement(iterations = 5, time = 5) +@Fork(value = 3) public class SSLHandshake { private SSLContext sslc; @@ -77,11 +80,12 @@ public void init() throws Exception { KeyStore ks = TestCertificates.getKeyStore(); KeyStore ts = TestCertificates.getTrustStore(); - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + KeyManagerFactory kmf = KeyManagerFactory.getInstance( + KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, new char[0]); - TrustManagerFactory tmf = - TrustManagerFactory.getInstance("SunX509"); + TrustManagerFactory tmf = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ts); SSLContext sslCtx = SSLContext.getInstance(tlsVersion); @@ -109,9 +113,6 @@ private HandshakeStatus checkResult(SSLEngine engine, SSLEngineResult result) { * The client and the server both operate on the same thread. */ @Benchmark - @Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) - @Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) - @Fork(3) public SSLSession doHandshake() throws Exception { createSSLEngines(); diff --git a/test/micro/org/openjdk/bench/java/security/SecureRandomBench.java b/test/micro/org/openjdk/bench/java/security/SecureRandomBench.java new file mode 100644 index 0000000000000..b9196b25c6239 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/security/SecureRandomBench.java @@ -0,0 +1,43 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package org.openjdk.bench.java.security; + +import org.openjdk.jmh.annotations.*; + +import java.security.SecureRandom; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) +public class SecureRandomBench { + + @Benchmark + public SecureRandom create() throws Exception { + return new SecureRandom(); + } +} diff --git a/test/micro/org/openjdk/bench/java/security/Signatures.java b/test/micro/org/openjdk/bench/java/security/Signatures.java new file mode 100644 index 0000000000000..7a14cb24b88b2 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/security/Signatures.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.security; + +import org.openjdk.jmh.annotations.*; + +import java.security.*; +import java.security.spec.*; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(jvmArgsAppend = {"-Xms1024m", "-Xmx1024m", "-Xmn768m", "-XX:+UseParallelGC"}, value = 3) +public class Signatures { + private static Signature signer; + + @Param({"64", "512", "2048", "16384"}) + private static int messageLength; + + @Param({"secp256r1", "secp384r1", "secp521r1"}) + private String algorithm; + + private static byte[] message; + + @Setup + public void setup() throws Exception { + message = new byte[messageLength]; + (new Random(System.nanoTime())).nextBytes(message); + + String signName = switch (algorithm) { + case "secp256r1" -> "SHA256withECDSA"; + case "secp384r1" -> "SHA384withECDSA"; + case "secp521r1" -> "SHA512withECDSA"; + default -> throw new RuntimeException(); + }; + + AlgorithmParameters params = + AlgorithmParameters.getInstance("EC", "SunEC"); + params.init(new ECGenParameterSpec(algorithm)); + ECGenParameterSpec ecParams = + params.getParameterSpec(ECGenParameterSpec.class); + + KeyPairGenerator kpg = + KeyPairGenerator.getInstance("EC", "SunEC"); + kpg.initialize(ecParams); + KeyPair kp = kpg.generateKeyPair(); + + signer = Signature.getInstance(signName, "SunEC"); + signer.initSign(kp.getPrivate()); + } + + @Benchmark + public byte[] sign() throws SignatureException { + signer.update(message); + return signer.sign(); + } + + public static class EdDSA extends Signatures { + @Param({"Ed25519", "Ed448"}) + private String algorithm; + + @Setup + public void setup() throws Exception { + message = new byte[messageLength]; + (new Random(System.nanoTime())).nextBytes(message); + + KeyPairGenerator kpg = + KeyPairGenerator.getInstance(algorithm, "SunEC"); + NamedParameterSpec spec = new NamedParameterSpec(algorithm); + kpg.initialize(spec); + KeyPair kp = kpg.generateKeyPair(); + + signer = Signature.getInstance(algorithm, "SunEC"); + signer.initSign(kp.getPrivate()); + } + } + + public static class DSA extends Signatures { + @Param({"SHA256withDSA", "SHA384withDSA", "SHA512withDSA"}) + private String algorithm; + + @Setup + public void setup() throws Exception { + message = new byte[messageLength]; + (new Random(System.nanoTime())).nextBytes(message); + + int keyLength = switch (algorithm) { + case "SHA256withDSA" -> 2048; + case "SHA384withDSA" -> 3072; + case "SHA512withDSA" -> 3072; + default -> throw new RuntimeException(); + }; + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA"); + kpg.initialize(keyLength); + KeyPair kp = kpg.generateKeyPair(); + + signer = Signature.getInstance(algorithm); + signer.initSign(kp.getPrivate()); + } + } + + public static class RSA extends Signatures { + @Param({"SHA256withRSA", "SHA384withRSA", "SHA512withRSA"}) + private String algorithm; + + @Setup + public void setup() throws Exception { + message = new byte[messageLength]; + (new Random(System.nanoTime())).nextBytes(message); + + int keyLength = switch (algorithm) { + case "SHA256withRSA" -> 2048; + case "SHA384withRSA" -> 3072; + case "SHA512withRSA" -> 4096; + default -> throw new RuntimeException(); + }; + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(keyLength); + KeyPair kp = kpg.generateKeyPair(); + + signer = Signature.getInstance(algorithm); + signer.initSign(kp.getPrivate()); + } + } + + public static class RSASSAPSS extends Signatures { + @Param({"SHA256", "SHA384", "SHA512"}) + private String algorithm; + + @Setup + public void setup() throws Exception { + message = new byte[messageLength]; + (new Random(System.nanoTime())).nextBytes(message); + + int keyLength = switch (algorithm) { + case "SHA256" -> 2048; + case "SHA384" -> 3072; + case "SHA512" -> 4096; + default -> throw new RuntimeException(); + }; + + PSSParameterSpec spec = switch (algorithm) { + case "SHA256" -> + new PSSParameterSpec( + "SHA-256", "MGF1", + MGF1ParameterSpec.SHA256, + 32, PSSParameterSpec.TRAILER_FIELD_BC); + case "SHA384" -> + new PSSParameterSpec( + "SHA-384", "MGF1", + MGF1ParameterSpec.SHA384, + 48, PSSParameterSpec.TRAILER_FIELD_BC); + case "SHA512" -> + new PSSParameterSpec( + "SHA-512", "MGF1", + MGF1ParameterSpec.SHA512, + 64, PSSParameterSpec.TRAILER_FIELD_BC); + default -> throw new RuntimeException(); + }; + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSASSA-PSS"); + kpg.initialize(keyLength); + KeyPair kp = kpg.generateKeyPair(); + + signer = Signature.getInstance("RSASSA-PSS"); + signer.setParameter(spec); + signer.initSign(kp.getPrivate()); + } + } +} + diff --git a/test/micro/org/openjdk/bench/java/security/TestCertificates.java b/test/micro/org/openjdk/bench/java/security/TestCertificates.java index 50d8401c0708a..752f0442a9381 100644 --- a/test/micro/org/openjdk/bench/java/security/TestCertificates.java +++ b/test/micro/org/openjdk/bench/java/security/TestCertificates.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -115,12 +115,12 @@ class TestCertificates { private TestCertificates() {} public static KeyStore getKeyStore() throws GeneralSecurityException, IOException { - KeyStore result = KeyStore.getInstance("JKS"); + KeyStore result = KeyStore.getInstance(KeyStore.getDefaultType()); result.load(null, null); CertificateFactory cf = CertificateFactory.getInstance("X.509"); Certificate serverCert = cf.generateCertificate( new ByteArrayInputStream( - TestCertificates.SERVER_CERT.getBytes(StandardCharsets.ISO_8859_1))); + SERVER_CERT.getBytes(StandardCharsets.ISO_8859_1))); Certificate caCert = cf.generateCertificate( new ByteArrayInputStream( CA_CERT.getBytes(StandardCharsets.ISO_8859_1))); @@ -135,7 +135,7 @@ public static KeyStore getKeyStore() throws GeneralSecurityException, IOExceptio } public static KeyStore getTrustStore() throws GeneralSecurityException, IOException { - KeyStore result = KeyStore.getInstance("JKS"); + KeyStore result = KeyStore.getInstance(KeyStore.getDefaultType()); result.load(null, null); CertificateFactory cf = CertificateFactory.getInstance("X.509"); Certificate rootcaCert = cf.generateCertificate( diff --git a/test/micro/org/openjdk/bench/java/util/Base64Decode.java b/test/micro/org/openjdk/bench/java/util/Base64Decode.java index 6822f4fd64e96..6d1ea8e3a111d 100644 --- a/test/micro/org/openjdk/bench/java/util/Base64Decode.java +++ b/test/micro/org/openjdk/bench/java/util/Base64Decode.java @@ -47,7 +47,7 @@ public class Base64Decode { "112", "512", "1000", "20000", "50000"}) private int maxNumBytes; - @Param({"4"}) + @Param({"4", "32", "76", "128"}) private int lineSize; private byte[] lineSeparator = {'\r', '\n'}; diff --git a/test/micro/org/openjdk/bench/vm/compiler/InterfaceCalls.java b/test/micro/org/openjdk/bench/vm/compiler/InterfaceCalls.java index bbecd3d3b23aa..57badf8af93e7 100644 --- a/test/micro/org/openjdk/bench/vm/compiler/InterfaceCalls.java +++ b/test/micro/org/openjdk/bench/vm/compiler/InterfaceCalls.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Scope; @@ -38,250 +39,240 @@ @State(Scope.Thread) public class InterfaceCalls { - interface AnInterface { - public int getInt(); + interface FirstInterface { + public int getIntFirst(); } interface SecondInterface { - public int get1(); + public int getIntSecond(); } - interface OnlyHasOneImplInterface { - public int getLong(); + interface FirstInterfaceExt extends FirstInterface { + default int getIntFirst() { return 44; } } - interface AloneInterface { - public int getNumber(); + interface FirstInterfaceExtExt extends FirstInterfaceExt { + default int getIntFirst() { return 45; } } - class SingleImplementor implements OnlyHasOneImplInterface { - public int getLong() { + class FirstClass implements FirstInterface, SecondInterface { + public int getIntFirst() { return 1; } - } - - class Extender1 extends SingleImplementor { - } - class FirstClass implements AnInterface { - public int getInt() { + public int getIntSecond() { return 1; } } - class SecondClass implements AnInterface { - public int getInt() { + class SecondClass implements FirstInterface, SecondInterface { + public int getIntFirst() { return 2; } + + public int getIntSecond() { + return 1; + } } - class ThirdClass implements AnInterface { - public int getInt() { + class ThirdClass implements FirstInterface, SecondInterface { + public int getIntFirst() { return -3; } + + public int getIntSecond() { + return 1; + } } - class FourthClass implements AnInterface { - public int getInt() { + class FourthClass implements FirstInterface, SecondInterface { + public int getIntFirst() { return -4; } - } - class FifthClass implements AnInterface { - public int getInt() { - return -5; + public int getIntSecond() { + return 1; } } - class MultiClass1 implements AnInterface, SecondInterface { - public int get1() { - return 1; + class FifthClass implements FirstInterface, SecondInterface { + public int getIntFirst() { + return -5; } - public int getInt() { - return 2; + public int getIntSecond() { + return 1; } } - class MultiClass2 implements AnInterface, SecondInterface { - public int get1() { + class FirstClassDontInline implements FirstInterface { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int getIntFirst() { return -1; } + } - public int getInt() { + class SecondClassDontInline implements FirstInterface { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int getIntFirst() { return -2; } } - class Aloner implements AloneInterface { - public int getNumber() { - return 7; + class ThirdClassDontInline implements FirstInterface { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int getIntFirst() { + return -3; } } - public Object dummy1; - - public Object dummy2; - - public Object dummy3; - - public AnInterface multi1a, multi2a; + class FourthClassDontInline implements FirstInterface { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int getIntFirst() { + return -4; + } + } - public SecondInterface multi1b, multi2b; + class FifthClassDontInline implements FirstInterface { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int getIntFirst() { + return -5; + } + } - public Object multic, multic2; + class FirstClassDontInlineExtExt implements FirstInterfaceExtExt { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int getIntFirst() { + return -1; + } + } - public AnInterface[] as = new AnInterface[5]; + class SecondClassDontInlineExtExt implements FirstInterfaceExtExt { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int getIntFirst() { + return -2; + } + } - public AnInterface multi; + class ThirdClassDontInlineExtExt implements FirstInterfaceExtExt { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int getIntFirst() { + return -3; + } + } - public OnlyHasOneImplInterface single1; + class FourthClassDontInlineExtExt implements FirstInterfaceExtExt { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int getIntFirst() { + return -4; + } + } - public OnlyHasOneImplInterface single2; + class FifthClassDontInlineExtExt implements FirstInterfaceExtExt { + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int getIntFirst() { + return -5; + } + } - public AloneInterface alone; + final int asLength = 5; + public FirstInterface[] as = new FirstInterface[asLength]; + public FirstInterface[] noninlined = new FirstInterface[asLength]; + public FirstInterfaceExtExt[] noninlinedextext = new FirstInterfaceExtExt[asLength]; - int count; @Setup public void setupSubclass() { - dummy1 = new FirstClass(); - dummy2 = new SecondClass(); - dummy3 = new ThirdClass(); as[0] = new FirstClass(); as[1] = new SecondClass(); as[2] = new ThirdClass(); as[3] = new FourthClass(); as[4] = new FifthClass(); - MultiClass1 mc1 = new MultiClass1(); - multi1a = mc1; - multi1b = mc1; - multic = mc1; - MultiClass2 mc2 = new MultiClass2(); - multi2a = mc2; - multi2b = mc2; - multic2 = mc2; - single1 = new SingleImplementor(); - single2 = new Extender1(); - alone = new Aloner(); - } - - private void swapMultiParts() { - AnInterface tmpa = multi1a; - SecondInterface tmpb = multi1b; - multi1a = multi2a; - multi2a = tmpa; - multi1b = multi2b; - multi2b = tmpb; - } - @SuppressWarnings("unused") - private void swapMulti() { - Object tmp = multic; - multic = multic2; - multic2 = tmp; + noninlined[0] = new FirstClassDontInline(); + noninlined[1] = new SecondClassDontInline(); + noninlined[2] = new ThirdClassDontInline(); + noninlined[3] = new FourthClassDontInline(); + noninlined[4] = new FifthClassDontInline(); + + noninlinedextext[0] = new FirstClassDontInlineExtExt(); + noninlinedextext[1] = new SecondClassDontInlineExtExt(); + noninlinedextext[2] = new ThirdClassDontInlineExtExt(); + noninlinedextext[3] = new FourthClassDontInlineExtExt(); + noninlinedextext[4] = new FifthClassDontInlineExtExt(); } /** - * Tests a call where there are multiple implementors but only one of the implementors is every used here so the - * call-site is monomorphic + * Tests a call where there are multiple implementors but only one of the + * implementors is every used here so the call-site is monomorphic */ @Benchmark public int testMonomorphic() { - return as[0].getInt(); + return as[0].getIntFirst(); } - /** Tests a interface call that only has one single implementation */ - @Benchmark - public int testSingle() { - return alone.getNumber(); - } + int l = 0; - /** - * Tests a call where there is a single implementation but multiple classes that inherit that implementation and both - * these implementors are used. - */ + /** Tests single base interface method call */ @Benchmark - public int testSingle2() { - OnlyHasOneImplInterface oi; - if ((count & 1) == 0) { - oi = single1; - } else { - oi = single2; - } - count++; - return oi.getLong(); - } - - /** - * Tests calling two different interface methods in two different interfaces on the same receiver. Make sure to switch - * between two different types of receivers to achieve polymorhpism - */ - @Benchmark - public void testCall2Poly2(Blackhole bh) { - bh.consume(multi1a.getInt()); - bh.consume(multi1b.get1()); - swapMultiParts(); + public int testIfaceCall(Blackhole bh) { + FirstInterface ai = noninlined[l]; + l = ++ l % asLength; + return ai.getIntFirst(); } + /** Tests extended interface method call */ @Benchmark - public int testCallMulti1Poly2NoSwap() { - return multi1a.getInt(); + public int testIfaceExtCall(Blackhole bh) { + FirstInterfaceExtExt ai = noninlinedextext[l]; + l = ++ l % asLength; + return ai.getIntFirst(); } /** - * This test goes together with Multi2 below It tests if a class implements multiple interfaces if the different - * interfaces take different amounts of time (They do for hotspot) + * Interface call address computation within loop but the receiver preexists + * the loop and the ac can be moved outside of the loop */ @Benchmark - public int testCallMulti1Poly2() { - swapMultiParts(); - return multi1a.getInt(); + public int test1stInt2Types() { + FirstInterface ai = as[l]; + l = 1 - l; + return ai.getIntFirst(); } - /** - * This test goes together with Multi2 below It tests if a class implements multiple interfaces if the different - * interfaces take different amounts of time (They do for hotspot) - */ @Benchmark - public int testCallMulti2Poly2() { - swapMultiParts(); - return multi1b.get1(); + public int test1stInt3Types() { + FirstInterface ai = as[l]; + l = ++ l % 3; + return ai.getIntFirst(); } - /** Interface call with three different receivers */ @Benchmark - public void testCallPoly3(Blackhole bh) { - for (int kk = 0; kk < 3; kk++) { - bh.consume(as[kk].getInt()); - } + public int test1stInt5Types() { + FirstInterface ai = as[l]; + l = ++ l % asLength; + return ai.getIntFirst(); } - /** Interface call with five different receivers. */ @Benchmark - public void testCallPoly5(Blackhole bh) { - for (int kk = 0; kk < 5; kk++) { - bh.consume(as[kk].getInt()); - } + public int test2ndInt2Types() { + SecondInterface ai = (SecondInterface) as[l]; + l = 1 - l; + return ai.getIntSecond(); } - int l; - - /** - * Interface call address computation within loop but the receiver preexists the loop and the ac can be moved outside - * of the loop - */ @Benchmark - public int testAC1() { - AnInterface ai = as[l]; - l = 1 - l; - return ai.getInt(); + public int test2ndInt3Types() { + SecondInterface ai = (SecondInterface) as[l]; + l = ++ l % 3; + return ai.getIntSecond(); } - /** Tests an interface cast followed by an interface call. */ @Benchmark - public int testInterfaceCastAndCall() throws Exception { - return ((AnInterface) dummy1).getInt() + ((AnInterface) dummy2).getInt() - + ((AnInterface) dummy3).getInt(); + public int test2ndInt5Types() { + SecondInterface ai = (SecondInterface) as[l]; + l = ++ l % asLength; + return ai.getIntSecond(); } + }